我无法结束 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()
且不让客户端阻塞的情况下从服务器编写欢迎消息?如果需要更多上下文,我将发布更多代码。
最佳答案
请注意,readn()
设计为循环read()
,直到读取了 20 个字节或出现 EOF 或套接字错误。如果服务器发送的消息长度小于 20 个字节,客户端将阻塞等待更多数据。
为防止阻塞,您可以在套接字上执行普通的read()
(或recv()
)。在这种情况下,这很可能会满足您的要求。
不过,一般来说,您不能依赖于将 write()
和 read()
配对用于 TCP 连接。字符串“bar”的单个 write()
可以任意拆分数据。作为一个极端的例子,三个连续的 read()
可能会返回“b”、“a”和“r”。该特定示例不太可能,但对于较大的 write()
和 read()
,您必须考虑到这一点(对于较小的传输,如果您想绝对安全)。
要解决此问题,您必须在接收端进行自己的缓冲。在这种情况下,最简单的解决方案是一次 read()
一个字符(或者使用 readn()
与您期望的数据量,如果它是已知的).一个更通用的解决方案是 read()
尽可能多的数据(确保检查 read()
的返回值以查看您返回了多少数据!) 放入缓冲区,并且仅在收集到足够的数据时才对数据进行操作。只要有一些数据可供读取,普通的read()
就不会阻塞,但您取回的数据可能少于您请求的数据。
“够了”通常是您协议(protocol)中的完整“消息”。您将需要一些方法来确定消息边界。两种选择是长度字段(根据我的经验通常是最好的解决方案)或消息终止符。两者都将与其余数据一起发送。
更新:
顺便说一句,你的空终止逻辑有一个错误。将 20 个字节读入 s_welcome_msg
会将 s_welcome_msg[19]
设置为最后读取的字节,覆盖空终止符。如果要将 20 字节的非空终止字符串读入 s_welcome_msg
并以空终止它,s_welcome_msg
需要 21 字节长,并且您需要执行 s_welcome_msg[20] = '\0'
。
关于c - tcp 指示流结束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29189821/