使用c中的套接字处理多个客户端双向通信

时间:2014-11-11 20:03:17

标签: c sockets select

这是我第一次构建一个接受c中多个客户端的服务器,我需要使用select来实现这一点。

我目前能够向服务器发送信息,但是当服务器执行回送时,信息最终会触发服务器端的select语句,而不是实际进入客户端。请帮助:(

客户端:

sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd < 0) syserr("can't open socket");
printf("create socket...\n");

memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr = *((struct in_addr*)server->h_addr);
serv_addr.sin_port = htons(portno);

if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
    syserr("can't connect to server");
printf("connect...\n");

// send the server our username
n = (int) send(sockfd, argv[3], strlen(argv[3]), 0) ;
if(n < 0) syserr("Can't send username to server\n");
printf("Username sent to server\n");

// wait for response, n ALWAYS RETURNS 0
n = (int) recv(sockfd, buffer, strlen(buffer), 0);

printf("buffer:\n%s\n",buffer) ;
if(n < 0) syserr("Can't receive from server\n");
else if (n == 0)
{
    close(sockfd) ;
    syserr("Server disconnected you due to duplicate username.\n") ;
}
else buffer[n] = '\0';



while ( done == 0 )
{
// rest of program loop, not being reached yet
}

close(sockfd);
return 0;

服务器:

portno = atoi(argv[1]);

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) syserr("can't open socket");
printf("create socket...\n");

memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);

if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
    syserr("can't bind");
printf("bind socket to port %d...\n", portno);

listen(sockfd, 5);

FD_ZERO(&fds) ;
FD_SET(sockfd, &fds) ;

for(;;)
{
    printf("wait on port %d...\n", portno);
    addrlen = sizeof(clt_addr);
    //newsockfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
    //if(newsockfd < 0) syserr("can't accept");

    readfds = fds ;
    int result = select(FD_SETSIZE, &readfds, NULL, NULL, NULL) ;

    // no timeout val, so never 0 return
    if(result == -1)
        syserr("Error on select call.\n") ;

    int i ;
    for( i = 0; i < FD_SETSIZE; i++ ) // loop through set of fds
    {
        if (FD_ISSET(i, &readfds)) // if fd i has data available to be read
        {
            if (i == sockfd) // if we are reading from server fd
            {
                if (numClients < maxClients) // if we could fit more clients
                {
                    // place incoming client's fd into array of all clients
                    clients[numClients].clientfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
                    if(clients[numClients].clientfd < 0) syserr("Error on accept.\n");

                    FD_SET(clients[numClients].clientfd, &fds) ;

                    // receive the username sent by user
                    printf("new incoming connection, block on receive\n");
                    n = (int) recv(clients[numClients].clientfd, clients[numClients].username, 30, 0);
                    if(n < 0) syserr("can't receive from client");

                    clients[numClients].username[n] = '\0';

                    printf("New user accepted: %s\n", clients[numClients].username) ;
                    clients[numClients].online = 1 ;

                    // NOW I NEED TO SEND SOME INFO BACK                        
        /// THIS IS THE PART THAT IS NOT SENDING BACK PROPERLY
        /// IT IS ACTUALLY TRIGERRING THE SELECT ON THE SECOND ITERATION
                    n = (int) send(clients[numClients].clientfd, "Hey\nThis\nis\na\ntest", 18, 0) ;
                    if(n < 0) syserr("Can't send userlist to client.\n");
                    printf("n: %d\n", n) ; // My print returns the expected 18
                    printf("User List sent to client.\n") ; // This also prints

                    ++numClients ; // extend client counter
                }
                else
                {
                    printf("Sorry, we are full.\n") ;
                }
            }
            else // not server file descriptor
            {
        /// On my second iteration This is triggered through the ‘select’ because of my send back above

                // check if recv returns 0 for disconnect
                n = (int) recv(clients[i].clientfd, buffer, sizeof(buffer), 0);
                if(n < 0) syserr("can't receive from client"); // I actually get an error for bad fd

                if( n == 0 ) // disconnected
                {
        // This code not reached yet
                }
                else // A message is being sent
                {
                    // This code not reached yet
                }
            }
        }
    }
}
close(sockfd); 
return 0;

2 个答案:

答案 0 :(得分:0)

在服务器端,您的主要问题是,在您通过选择的第二次迭代中触发的else中,irecv()来自clients[i].clientfd,而不是maxfd + 1 {1}}。它们不太可能是相同的数字,但我不能确定,因为你没有发布所有代码。 (请始终 http://www.sscce.org/

还有其他问题。我会提到我立即注意到的那些

要选择的第一个参数应该是int maxfd = MYMAX(maxfd, new_fd_from_accept/listen),其中#define MYMAX(a,b) (((a)>(b))?(a):(b))maxfd。将sockfd初始化为man select即可。循环到FD_SETSIZE是一种浪费。

我无法看到你是否正确初始化变量,所以请确保你这样做。如果您使用的是Linux,请阅读man select_tutrecv()

在你刚刚接受的套接字上使用select(),而不是从select()指示这样做,似乎可以正常工作,因为你进行了阻塞读取,但我仍然建议将接受的fd放入fd_set并在{{1}}表示可以这样做时从中读取。

编辑:请参阅Serge Ballestas回答客户方问题。

答案 1 :(得分:0)

我无法看到你如何在客户端声明buffer,但我可以看到你这样使用它:

n = (int) recv(sockfd, buffer, strlen(buffer), 0);

如果buffer中的第一个字符为0,strlen(buffer)0n将始终为0.

如果您有char buffer[...];,请使用:

n = (int) recv(sockfd, buffer, sizeof(buffer), 0);

如果您在堆栈上使用动态分配:buffer = (char *) malloc(size);,请执行:

n = (int) recv(sockfd, buffer, size, 0);