tcp表示流的结束

时间:2015-03-22 00:57:01

标签: c tcp stream

我无法结束tcp流。我正在编写一个简单的服务器和客户端,客户端连接到服务器,服务器显示一条欢迎消息,询问客户端是否有用户名。

问题是,当服务器写入消息时,客户端的read()被阻止。它只在我调用shutdown()时被解锁。

服务器:

           if (FD_ISSET(tcp_listenfd, &rset)) {
                  len = sizeof(cliaddr);
                  if ((new_confd = accept(tcp_listenfd, (struct sockaddr *) &cliaddr, &len)) < 0) {
                            perror("accept");
                            exit(1);
                  }
                  /* Send connection message asking for handle */
                  writen(new_confd, handle_msg, strlen(handle_msg));
                  /* Fork here or shutdown fd is inherited */
                  shutdown(new_confd, SHUT_WR);

客户端:

    if ((connect(sock, (struct sockaddr *) server, sizeof(struct sockaddr_in))) < 0) {
            perror("inet_wstream:connect");
            exit(1);
    }

    s_welcome_msg[19] = '\0';
    readn(sock, s_welcome_msg, 20); //Blocks here if shutdown() is not called in server 

readn()writen()函数改编自Stevens的“The Socket Networking API”:http://www.informit.com/articles/article.aspx?p=169505&seqNum=9

如何在不调用shutdown()且没有客户端阻止的情况下从服务器编写欢迎消息?如果需要更多上下文,我将发布更多代码。

2 个答案:

答案 0 :(得分:1)

请注意,readn()在循环中设计为read(),直到读取20个字节或EOF或套接字上的错误为止。如果服务器发送的消息长度少于20个字节,则客户端将阻止等待更多数据。

为防止阻止,您可以在套接字上执行正常read()(或recv())。在这种情况下,这可能会做你想要的。

通常,您不能依赖于能够为write()read()配对TCP连接。单个write()字符串&#34; bar&#34;可以随意拆分数据。作为一个极端的例子,连续三个read()可以返回&#34; b&#34;,&#34; a&#34;和&#34; r&#34;。这个特定的例子不太可能,但是对于较大的write()read(),您必须考虑到这一点(对于较小的传输,如果您想要完全安全的话)。

要解决此问题,您必须在接收端进行自己的缓冲。在这种情况下,最简单的解决方案是read()一次一个字符(或者使用readn()与您期望的数据量完全相同(如果已知)。一个更通用的解决方案是read()尽可能多的数据(当前可用(确保检查read()的返回值以查看您获得的数据!)到缓冲区中并且仅作用于数据,只要你收集到足够的数据。只要有某些数据可供读取,普通read()就不会阻止,但您可能会收到的数据少于您请求的数据。

&#34;足够了&#34;通常是一个完整的消息&#34;在您的协议中。您需要一些方法来确定消息边界。两个备选方案是长度字段(通常是我的经验中的最佳解决方案)或消息终止符。两者都将与其他数据一起发送。

<强>更新

顺便提一下,你的空终止逻辑中有一个错误。将20个字节读入s_welcome_msg会将s_welcome_msg[19]设置为读取的最后一个字节,覆盖空终止符。如果要将一个20字节的非空终止字符串读入s_welcome_msg并将其终止,则s_welcome_msg需要长度为21个字节,您需要执行s_welcome_msg[20] = '\0' }。

答案 1 :(得分:-2)

read(n)将阻塞,直到收到请求的字节数

(接收字段只有19个字节,所以它确实读取了20个字节 这将是一个缓冲区溢出,它是未定义的行为,可能/将导致seg错误事件)

作为一种可能的解决方案,我建议使用带有超时的select()语句的循环     当select()表示某些数据可用时,        只读一个字节        将该字节附加到s_welcome_msg []缓冲区

(同时始终检查缓冲区是否溢出 通常,这意味着只读取最多18个字节 所以读取值将是一个有效的字符串)

您的代码应该使read()成为非阻塞 所以它不会挂起。

读完字节后,     如果输入缓冲区未满(读取18个字节)     然后循环回到select()语句

如果发生select()超时,     然后假设已经读取了所有数据     并在选择/读取循环

之后继续执行下一个代码语句

还记得永远刷新&#39;超时值     在select()语句参数之前     执行select()