使用select()的TCP服务器

时间:2015-03-21 18:49:26

标签: c sockets networking tcp

我需要编写一个可以处理多个连接的TCP服务器;我跟着this guide编写了以下程序:

static void _handle_requests(char* cmd,int sessionfd){
    //TODO: extend
    printf("RECEIVED: %s\n",cmd);
    if (!strcmp(cmd,BAR)){
        barrier_hit(&nodebar,sessionfd);

    }else if (!strcmp(cmd, BYE)){


    }else if (!strcmp(cmd, HI)){

    }
}

void handle_requests(void){
    listen(in_sock_fd,QUEUELEN);
    fd_set read_set, active_set;
    FD_ZERO(&active_set);
    FD_SET(in_sock_fd, &active_set);
    int numfd = 0;
    char cmd[INBUFLEN];
    for (;;){
        read_set = active_set;

        numfd = select(FD_SETSIZE,&read_set,NULL,NULL,NULL);
        for (int i = 0;i < FD_SETSIZE; ++i){
            if (FD_ISSET(i,&read_set)){ 
                if (i == in_sock_fd){
                    //new connection
                    struct sockaddr_in cliaddr;
                    socklen_t socklen = sizeof cliaddr;
                    int newfd = accept(in_sock_fd,(struct sockaddr*)&cliaddr, &socklen);
                    FD_SET(newfd,&active_set);  
                }else{
                    //already active connection
                    read(i,cmd,INBUFLEN);
                    _handle_requests(cmd,i);
                }
            }


        }
    }
}

..和一个连接()到服务器的客户端,并对套接字文件描述符进行两次连续的write()调用。

n = write(sm_sockfd, "hi", 3);

    if (n < 0) {
        perror("SM: ERROR writing to socket");
        return 1;
    }

//...later

 n = write(sm_sockfd, "barrier", 8);


    if (n < 0) {
        perror("SM: 'barrier msg' failed");
        exit(1);
    }

问题是,服务器只接收第一条消息(&#34; hi&#34;);之后,选择呼叫挂起。由于客户端上的写(&#34;屏障&#34;)成功了,该会话文件描述符是否已准备好进行读取?我犯过任何明显的错误吗?

感谢;对不起,如果这是显而易见的事情,我对C&#39的网络库完全不熟悉,该项目很快就会到期!

2 个答案:

答案 0 :(得分:2)

您对TCP套接字的工作方式存在误解。 TCP中没有消息边界,即如果您先发送&#34; hi&#34;然后&#34;屏障&#34;,你不能指望相应的接收返回&#34; hi&#34;和&#34;屏障&#34;。他们有可能返回&#34; hibarrier&#34;。它在理论上也可能(尽管非常罕见)他们会回归&#34; h&#34;,&#34;我&#34;,&#34; b&#34;,&#34; a& #34;,&#34; r&#34;,&#34; r&#34;,&#34;我&#34;,&#34; e&#34;,&#34; r&#34;。< / p>

您真的需要考虑如何划分邮件。一种可能性是在消息之前以网络字节顺序(4字节)发送消息的长度为32位整数。然后,当您收到消息时,首先读取4个字节,然后读取消息长度指示的字节数。

请注意,TCP可能会返回部分读取,因此您需要以某种方式处理这些内容。一种可能性是有一个缓冲区,用于保存读取的字节,然后在读取更多字节时附加到此缓冲区,并在缓冲区的前四个字节(即消息长度)指示您具有该缓冲区时处理缓冲区的内容完整的信息。

如果您想要一个保留数据包边界的顺序数据包协议,您可能需要考虑SCTP。但是,目前操作系统内核并没有广泛支持它,所以我要做的就是在TCP之上设置一个面向数据包层的32位长度技巧。

答案 1 :(得分:0)

这样做:

int nbrRead = read(i,cmd,INBUFLEN);

并打印出nbrRead的值。你会看到你一次性收到了所有东西。 TCP是一种流媒体协议,如果您进行3次或更多次连续发送,那么您将立即收到它们的机会非常高。

同时确保INBUFLEN足够大,2048对你的例子来说已经足够了。

相关问题