close
是关闭套接字,调用之后无法再通过 write
与 read
读写数据。但是通过查看 TCP 的四次挥手我们知道

在 close
之后,对端可能还会发送一部分数据,这部分数据其实是丢失的。所以就引入了半关闭,只能发送数据但是不能接收 或者 只能接收数据但是不能发送。
这里提到了一个需求:客户端向服务器发起请求,然后服务端将文件传输给客户端之后,客户端接收完文件,然后告知服务端接收完毕了,客户端自动退出。
这里有一个疑问,客户端怎么知道接收完文件了?
- 从
read
函数返回-1表示接收结束,但是read
返回-1 表示对端关闭了连接,那么需求中接收完文件还要发送响应就不行了 - 写一个标志位,告知这个标志位就表示文件结束。但是这样的话就必须让文件中没有与标志符一样的内容
这个时候就用到了半关闭
1 | /** |
知道了 shutdown
函数之后,看看是如何实现的 示例地址:https://github.com/XBoom/network-ip.git 中的 apps/socket/09/
运行结果如下
1 | ./server 5555 |
另外通过抓包可以看出跟平时的四次挥手不一样的地方(5555 表示服务端)
- 首先通过三次握手(1~3)
- 服务端第一次发送数据给客户端,客户端确认(4~5)
- 服务端第二次发送数据给客户端,客户端确认(6、8)
- 在服务端第二次发送数据的时候,同时发送了
FIN
结束(7),也就是调用的shutdown(client_sock, SHUT_WR);
- 客户端接着向服务端发送数据,客户端发送响应(9-10),发现客户端调用的
shutdown(server_sock, SHUT_RD);
并没有作用,因为已经收到了服务端的FIN
报文 - 发送结束之后,客户端通过
close
发起FIN
对于两个 shutdown
可以看成下图这样
shotdown
分别对应的是两个流,当一端关闭之后就不需要再次关闭了。而一条流的关闭也不会影响另外一条流
那么如果改变一下服务端的关闭流程,先关闭读,再关闭写。改成如下
1 | //关闭读 |
程序运行结果如下,好像服务端没有什么
1 | [root]#./server 5555 |
看看交互报文
- 服务端发送完数据之后,关闭了读
shutdown(client_sock, SHUT_RD);
- 客户端接收完数据之后也关闭了读,导致客户端与服务端都阻塞等待
参考文档
- 《TCPIP 网络编程》