使用 C 的 select() 系統調用為 UDP 應用程序創建多個客戶端和一個服務器 (Creating Multiple Client and One Server for UDP application using select() system call with C)


問題描述

使用 C 的 select() 系統調用為 UDP 應用程序創建多個客戶端和一個服務器 (Creating Multiple Client and One Server for UDP application using select() system call with C)

幫助我更正這個程序是這個代碼在不同的會話中為多個客戶端運行,這意味著在為一個客戶端運行後它會停止,當我再次啟動服務器時,它也可以為另一個客戶端提供服務。我希望我的代碼只在一個會話中無限期地工作,並為盡可能多的客戶提供服務。

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:

Some minor things to start with:

In the client code, you do not initialize len before passing it to recvfrom. Also, it is of the wrong type (int instead of socklen_t). This can be corrected by defining len as socklen_t len = sizeof(servaddr); However, since you do not use the length or address anywhere, you can just pass in NULL for both. Also, UDP does not support MSG_WAITALL, so pass 0 instead for the flags. recvfrom(sockfd, (char *)buffer, MAXN, 0, NULL, NULL).

In the client code, you fill in the server IP address as IPADDR_ANY, i.e. 0.0.0.0, which is only valid when calling bind. You are calling sendto. When specifying a destination address, you probably want IPADDR_LOOPBACK for the local computer.

In the server code, the main while() loop is missing a condition inside the parenthesis, so the code does not even compile. You probably want while(1) for an infinite loop.

In the server code, in the call to bind, you are casting to the wrong type. (const *) means (const int *), but you want (const struct sockaddr *).

The biggest problem:

Based on the comments and code in the server code, you seem to think that UDP communication has persistent connections, and try to save client_socket values for these. However, UDP does not have persistent connection. There is no such thing as a client socket in this context. select does not tell you that a new "connection" is ready. Instead, it tells you that data has been received on your one and only UDP socket (sockfd). This is entirely unlike TCP, where you accept a connection that lasts until one side terminates it.

From the comments in the server code, it seems that you intend for clients to have persistent state in the server, at least enough to be recognized when they send more datagrams (otherwise they would always get the welcome message).

A possible way to make this work is the following:

// 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;
}

You will notice that the code does not actually use select anymore. select is unnecessary if you have nothing to do while waiting, and you only listen to one socket. If you are doing other stuff not shown in the question, you can add the select back at the top of the infinite loop, and only handle the UDP socket if sockfd is set in the readfds from select.

Also note that the client cannot actually receive answers from the server, since there is no persistent connection, and the client has not passed any socket to bind. And also note that UDP messages may get lost without warning, and you should make sure your code can deal with that. It seems that this client‑server‑strategy would be easier to implement using TCP.

As a side note, it is strongly recommended to enable warnings in your compiler. That would have caught the wrong type for the address length variables, as well as told you that new_socket is used but never set. It is polite to fix all warnings before posting a question, unless of course the question is about the warning.

(by Rahul Kumar Dubeyrtoijala)

參考文件

  1. Creating Multiple Client and One Server for UDP application using select() system call with C (CC BY‑SA 2.5/3.0/4.0)

#sockets #UDP #C






相關問題

Ці павінен UDP-сокет праходзіць працэс прыняцця, як TCP-сокеты? (Does UDP socket need to go through the Accept Process like TCP sockets?)

як запусціць каманду openssl з прыкладання java (how to run openssl command from a java application)

TypeError: непадтрымоўваны сокет тыпу аперанда Python (TypeError: unsupported operand type-python socket)

套接字編程:某些 ISP 是否對 FTP 上傳施加了速率限制? (Socket programming: Do some ISP's impose rate-limiting on FTP uploads?)

兩個進程使用同一個端口? (Two processes using the same port?)

Akka TCP 客戶端:如何使用 akka actor 通過 TCP 發送消息 (Akka TCP client: How can I send a message over TCP using akka actor)

無法運行 python-bluez RFCOMM 服務器示例腳本 (Cannot run python-bluez RFCOMM server example script)

Java中的套接字和進程 (Sockets and Processes in Java)

在網絡環境中從 Brother TD-4100N 打印機檢索打印機狀態 (Retrieving the printer status from the Brother TD-4100N printer in a network environment)

通過套接字連接發送 GUI/TUI (Sending a GUI/TUI Over a Socket Connection)

如果服務器無法訪問,則在 android 上運行的客戶端將關閉並退出應用程序,無異常或函數“connect (socket)”中的任何消息 (the client running on android closes and exit application without exception or any message in function “connect (socket)” if the server is unreachable)

使用 C 的 select() 系統調用為 UDP 應用程序創建多個客戶端和一個服務器 (Creating Multiple Client and One Server for UDP application using select() system call with C)







留言討論