使用带有C的select()系统调用为UDP应用程序创建多个客户端和一个服务器

时间:2020-07-22 11:58:05

标签: c sockets udp

帮助我纠正此程序,我想创建一个可以无限期运行并可以服务许多客户端的服务器,为此,我正在使用UDP(用户数据报协议)应用程序的选择系统调用。
我的问题是,此代码正在不同的会话中为多个客户端运行,这意味着在为一个客户端运行后,它会停止,而当我再次启动服务器时,它又可以为另一个客户端提供服务。 我希望我的代码仅在一个会话中无限期工作,并根据需要服务尽可能多的客户端。

Server Code:-
'''

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/select.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #define port1 8080
    #define MAXN 1024
    #define TRUE 1
    
    int main(){
    int sockfd,sockfd1;
    struct sockaddr_in servaddr,cliaddr;
    char buffer[MAXN];
    char buff[MAXN];
    int max_clients=2,valread,new_socket;
    char *hello = "Hello Client";
    char *message = "hiiii Server";
    struct timeval timeout;
    timeout.tv_sec = 20;
    timeout.tv_usec = 0;
    
    
    
    //create socket 2
    sockfd = socket(AF_INET, SOCK_DGRAM,0);
    if(sockfd<0){
    perror("Error Creating Socket0");
    exit(1);
    }
    //memset(&servaddr, 0, sizeof(servaddr));
        //memset(&cliaddr, 0, sizeof(cliaddr));
         
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port1);
    servaddr.sin_addr.s_addr = INADDR_ANY;
    
    
    if(bind(sockfd, (const *)&servaddr, sizeof(servaddr))<0){
    perror("Error in binding0 ");
    exit(1);
    }
    
    
    
    
    //Use Select......
    //I have created 2 socket having file deescriptor sockfd and sockfd1
    int s;
    int client_socket[2]={0,0};
    fd_set readfds;
    
    while(){
    FD_ZERO(&readfds);
    FD_SET(sockfd,&readfds);
    //Let's say sockfd is max_fd
    int max_fd = sockfd,sd,activity;
    
    for(int i=0;i<2;i++){
    sd = client_socket[i];
    if(sd>0){
    FD_SET(sd,&readfds);
    }
    if(sd>max_fd)
    max_fd = sd;
    
    }
    
    
    
    activity = select( max_fd + 1 , &readfds , NULL , NULL , &timeout);  
           
            if ((activity < 0))  
            {  
                printf("select error");  
            }  
              int   addrlen = sizeof(servaddr);
            //If something happened on the master socket ,  
            //then its an incoming connection  
            if (FD_ISSET(sockfd, &readfds))  
            {  
                //inform user of socket number - used in send and receive commands  
                printf("New connection , socket fd is %d , ip is : %s , port : %d\n " , new_socket , inet_ntoa(servaddr.sin_addr) , ntohs
                      (servaddr.sin_port));  
               
                //send new connection greeting message  
                if( send(new_socket, message, strlen(message), 0) != strlen(message) )  
                {  
                    perror("send");  
                }  
                     
                puts("Welcome message sent successfully");  
                     
                //add new socket to array of sockets  
                for (int i = 0; i < max_clients; i++)  
                {  
                    //if position is empty  
                    if( client_socket[i] == 0 )  
                    {  
                        client_socket[i] = new_socket;  
                        printf("Adding to list of sockets as %d\n" , i);  
                             
                        break;  
                    }  
                }  
            }  
                 
            //else its some IO operation on some other socket
            for (int i = 0; i < max_clients; i++)  
            {  
                sd = client_socket[i];  
                     
                if (FD_ISSET( sd , &readfds))  
                {  
                    //Check if it was for closing , and also read the  
                    //incoming message  
                    if ((valread = read( sd , buffer, 1024)) == 0)  
                    {  
                        //Somebody disconnected , get his details and print  
                        getpeername(sd , (struct sockaddr*)&servaddr , \
                            (socklen_t*)&addrlen);  
                        printf("Host disconnected , ip %s , port %d \n" ,  
                              inet_ntoa(servaddr.sin_addr) , ntohs(servaddr.sin_port));  
                             
                        //Close the socket and mark as 0 in list for reuse  
                        //close( sd );  
                        client_socket[i] = 0;  
                    }  
                         
                    //Echo back the message that came in  
                    else
                    {  
                        //set the string terminating NULL byte on the end  
                        //of the data read  
                        buffer[valread] = '\0';  
                        send(sd , buffer , strlen(buffer) , 0 );  
                    }
    
                   
                }
    }  
    
    }
    
         return 0;
    
    }
    '''
    
    This is one of the Client Code and this code is running :-
    '''
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
     
    #define PORT     8080
    #define MAXN 1024
     
    // Driver code
    int main() {
        int sockfd;
        char buffer[MAXN];
        char *hello = "Hello from Multipleclient";
        struct sockaddr_in     servaddr;
     
        // Creating socket file descriptor
        if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
            perror("Error in socket creation");
            exit(1);
        }
     
        memset(&servaddr, 0, sizeof(servaddr));
         
        // Filling server information
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(PORT);
        servaddr.sin_addr.s_addr = INADDR_ANY;
         
        int n, len;
         
        sendto(sockfd, (const char *)hello, strlen(hello),
            MSG_CONFIRM, (const struct sockaddr *) &servaddr,  
                sizeof(servaddr));
        printf("Hello message sent.\n");
             
        n = recvfrom(sockfd, (char *)buffer, MAXN,  
                    MSG_WAITALL, (struct sockaddr *) &servaddr,
                    &len);
        buffer[n] = '\0';
        printf("Server : %s\n", buffer);
     
        close(sockfd);
        return 0;
    }

1 个答案:

答案 0 :(得分:0)

从一些小事情开始:

在客户端代码中,不要在将len传递给recvfrom之前对其进行初始化。另外,它的类型错误(int而不是socklen_t)。可以通过将len定义为socklen_t len = sizeof(servaddr);来纠正此问题。但是,由于您没有在任何地方使用长度或地址,因此您只需为两者传递NULL。另外,UDP不支持MSG_WAITALL,因此请传递0作为标志。 recvfrom(sockfd, (char *)buffer, MAXN, 0, NULL, NULL)

在客户端代码中,您将服务器IP地址填写为IPADDR_ANY,即0.0.0.0,仅在调用bind时有效。您正在呼叫sendto。指定目标地址时,您可能需要IPADDR_LOOPBACK作为本地计算机。

在服务器代码中,主while()循环缺少括号内的条件,因此该代码甚至无法编译。您可能希望while(1)进行无限循环。

在服务器代码中,对bind的调用将您转换为错误的类型。 (const *)的意思是(const int *),但您想要(const struct sockaddr *)

最大的问题:

基于服务器代码中的注释和代码,您似乎认为UDP通信具有持久性连接,并尝试为它们保存client_socket值。但是,UDP没有持久连接。在这种情况下,没有客户端套接字之类的东西。 select不会告诉您新的“连接”已准备就绪。相反,它告诉您已经在您唯一的UDP套接字(sockfd)上接收到数据。这与TCP完全不同,在TCP中,accept一直持续到一侧终止连接为止。

从服务器代码中的注释来看,您似乎打算让客户端在服务器中具有持久状态,至少要在它们发送更多数据报时被识别(否则它们总是会收到欢迎消息)。

实现此目的的一种可能方法如下:

// create and bind a UDP socket as you already do;
// initialize and empty list of clients;
while(1) {
    struct sockaddr_in cliaddr;
    socklen_t cliaddr_len = sizeof(cliaddr);
    recvfrom(sockfd, buffer, bufferlen, /*flags*/ 0, (struct sockaddr *)&cliaddr, &cliaddr_len);

    // check whether cliaddr is in your list;
    if (!is_in_list) {
        // this is a new client
        // add to list of clients;
        // send welcome message;
    }

    // do something else, maybe echo back the buffer contents;
}

您会发现该代码实际上不再使用select。如果您在等待时无事可做,则select是不必要的,并且您仅监听一个套接字。如果您正在做问题中未显示的其他操作,则可以将select添加回无限循环的顶部,并且仅在{{1}中设置了sockfd时处理UDP套接字。 },来自readfds

还请注意,由于没有持久连接,并且客户端未将任何套接字传递给select,因此客户端实际上无法从服务器接收答案。还要注意,UDP消息可能会在没有警告的情况下丢失,并且您应该确保您的代码可以处理该消息。看来使用TCP可以更轻松地实现这种客户端-服务器策略。

作为旁注,强烈建议在编译器中启用警告。这将为地址长度变量捕获错误的类型,并告诉您使用了bind但从未设置。礼貌地在发布问题之前修正所有警告,除非问题当然与警告有关。