从shutdown()的POSIX.1-2008 / 2013文档 :
int shutdown(int socket, int how);
…
shutdown()
函数将导致与文件描述符套接字关联的套接字上全部或部分全双工连接closures。
- Socket.io来自Socket.IO-Client-Swift的POST请求
- 如何为UDP数据包发送者启用自定义IP地址?
- recv()失败:错误的文件描述符c ++ Linux
- C ++中的Linux守护进程来处理PHP请求
- 如何使用BPF过滤来监视原始套接字的CPU时间
shutdown()
函数采用以下参数:
socket
指定socket
的文件描述符。
how
指定关机的types。 数值如下:
SHUT_RD
禁用进一步的接收操作。SHUT_WR
禁用进一步的发送操作。SHUT_RDWR
禁用进一步的发送和接收操作。…
shutdown(2)
手册页说几乎相同的事情。
shutdown()
调用会导致与sockfd关联的套接字上全部或部分全双工连接closures。 如果SHUT_RD
how
,将不允许进一步的接待。 如果SHUT_WR
how
,进一步传输将被禁止。 如果SHUT_RDWR
how
,进一步的接收和传输将被禁止。
但是我想即使在shutdown(sockfd, SHUT_RD)
调用之后我也能够接收数据。 这是我精心策划的testing和我观察到的结果。
------------------------------------------------------ Time netcat (nc) C (a.out) Result Observed ------------------------------------------------------ 0 s listen - - 2 s connect() - 4 s send "aa" - - 6 s - recv() #1 recv() #1 receives "aa" 8 s - shutdown() - 10 s send "bb" - - 12 s - recv() #2 recv() #2 receives "bb" 14 s - recv() #3 recv() #3 returns 0 16 s - recv() #4 recv() #4 returns 0 18 s send "cc" - - 20 s - recv() #5 recv() #5 receives "cc" 22 s - recv() #6 recv() #6 returns 0 ------------------------------------------------------
以上是对上表的简要说明。
shutdown()
成功完成后发送的消息“bb”。 查看“12s”和“20s”的行。 这是C程序(客户端程序)。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netdb.h> int main() { struct addrinfo hints, *ai; int sockfd; int ret; ssize_t bytes; char buffer[1024]; /* Select TCP/IPv4 address only. */ memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; if ((ret = getaddrinfo("localhost", "8888", &hints, &ai)) == -1) { printf("getaddrinfo() error: %s\n", gai_strerror(ret)); return EXIT_FAILURE; } if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { printf("socket() error: %s\n", strerror(errno)); return EXIT_FAILURE; } /* Connect to localhost:8888. */ sleep(2); if ((connect(sockfd, ai->ai_addr, ai->ai_addrlen)) == -1) { printf("connect() error: %s\n", strerror(errno)); return EXIT_FAILURE; } freeaddrinfo(ai); /* Test 1: Receive before shutdown. */ sleep(4); bytes = recv(sockfd, buffer, 1024, 0); printf("recv() #1 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer); sleep(2); if (shutdown(sockfd, SHUT_RD) == -1) { printf("shutdown() error: %s\n", strerror(errno)); return EXIT_FAILURE; } printf("shutdown() complete\n"); /* Test 2: Receive after shutdown. */ sleep (4); bytes = recv(sockfd, buffer, 1024, 0); printf("recv() #2 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer); /* Test 3. */ sleep (2); bytes = recv(sockfd, buffer, 1024, 0); printf("recv() #3 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer); /* Test 4. */ sleep (2); bytes = recv(sockfd, buffer, 1024, 0); printf("recv() #4 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer); /* Test 5. */ sleep (4); bytes = recv(sockfd, buffer, 1024, 0); printf("recv() #5 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer); /* Test 6. */ sleep (2); bytes = recv(sockfd, buffer, 1024, 0); printf("recv() #6 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer); }
上面的代码保存在一个名为foo.c
的文件中。
这是一个很小的shell脚本,编译和运行上面的程序,并调用netcat( nc
)来监听端口8888,并按照上面的表格以特定的时间间隔对消息aa
, bb
和cc
作出响应。 以下shell脚本保存在一个名为run.sh
。
set -ex gcc -std=c99 -pedantic -Wall -Wextra -D_POSIX_C_SOURCE=200112L foo.c ./a.out & (sleep 4; printf aa; sleep 6; printf bb; sleep 8; printf cc) | nc -vvlp 8888
当上面的shell脚本运行时,观察到以下输出。
$ sh run.sh + gcc -std=c99 -pedantic -Wall -Wextra -D_POSIX_C_SOURCE=200112L foo.c + nc -vvlp 8888 + sleep 4 listening on [any] 8888 ... + ./a.out connect to [127.0.0.1] from localhost [127.0.0.1] 54208 + printf aa + sleep 6 recv() #1 returned 2 bytes: aa shutdown() complete + printf bb + sleep 8 recv() #2 returned 2 bytes: bb recv() #3 returned 0 bytes: recv() #4 returned 0 bytes: + printf cc recv() #5 returned 2 bytes: cc recv() #6 returned 0 bytes: sent 6, rcvd 0
输出显示C程序即使在调用shutdown()
之后也能够使用recv()
接收消息。 shutdown()
调用似乎已经受到影响的唯一行为是recv()
调用是立即返回还是被阻塞,等待下一条消息。 通常,在shutdown()
之前, recv()
调用将等待消息到达。 但是在shutdown()
调用之后,当没有新消息时, recv()
立即返回0
。
我期待所有recv()
调用后shutdown()
以某种方式失败(比如说,返回-1
)由于我已经在上面引用的文档。
两个问题:
recv()
调用能够接收shutdown()
调用正确的新消息,正如根据POSIX标准和上面引用的shutdown(2)
的手册页所述的那样? shutdown()
之后, recv()
立即返回0
,而不是等待新消息到达? 你问了两个问题:它是否符合posix标准,为什么recv
返回0而不是阻塞。
关机标准
shutdown
文档说:
shutdown()函数根据how参数的值来禁用套接字上的后续发送和/或接收操作。
这似乎意味着没有进一步的read
调用将返回任何数据。
但是recv
的文件说明:
如果没有消息可用于接收,并且对等体已经执行了有序关闭,那么recv()应该返回0。
一起读这可能意味着远程对等呼叫shutdown
recv
应该返回一个错误 recv
可以在shutdown
后继续返回数据。 虽然这有些模棱两可,但第一种解释是没有意义的,因为不清楚错误会起什么作用。 所以正确的解释是第二个。
(请注意,在堆栈中的任何一点进行缓冲的任何协议都可能有传输中的数据,这些数据仍然不能被读取。shutdown的语义使得您可以在调用shutdown
之后仍然接收这些数据。
但是这是指对等呼叫shutdown
,而不是呼叫过程。 如果调用进程称为shutdown
这是否也适用?
所以它是兼容或什么
标准是不明确的。
如果调用shutdown(fd, SHUT_RD)
的进程被认为等同于对等调用shutdown(fd, SHUT_WR)
则它是兼容的。
另一方面,严格阅读文本,似乎不符合规定。 但是,在进程在shutdown(SHUT_RD)
后调用recv
shutdown(SHUT_RD)
的情况下没有错误代码。 错误代码是详尽的,这意味着这种情况不是一个错误,所以应该返回0
,在对等的情况下,对方调用shutdown(SHUT_WR)
。
不过,这是你想要的行为 – 如果你想要的话,在途的信息可以被接收。 如果你不想他们不叫recv
。
如果这是不明确的,那么它应该被认为是标准中的一个错误。
为什么shutdown
后的recv
数据仅限于传输中的数据
在一般情况下,不可能知道哪些数据在传输中。
背景
posix提供了与不同类型的流(包括匿名管道,命名管道,IPv4和IPv6 TCP和UDP套接字以及原始以太网,令牌环和IPX / SPX以及X.25和ATM)统一交互的api。 ..
因此posix提供了一套广泛涵盖大多数流媒体和基于分组协议的主要功能的功能。
然而,不是每一种能力都被任何协议所支持
从设计的角度来看,如果一个调用者请求一个不被底层协议支持的操作,有许多选择:
输入一个错误状态,并禁止在文件描述符上进一步的操作。
从通话中返回一个错误,但是否则忽略它。
返回成功,做最有意义的事情。
实现某种包装或填充提供缺少的功能。
前两个选项被posix标准排除。 显然第三个选项已经被Linux开发者选中了。