当client,调用read(socketfd,buffer,n)时,返回0的情况:
1、server端调用了close(soketfd)函数
2、server调用了close(fd,SHUT_WR),关闭server端的写连接,半关闭
关于close 和shutdown的过程
一.void close(fd):close发送的是FIN分节(不一定是发送FIN,也可能发送RST(如果local接受缓冲区的数据没有被读完);sever端close时候,如果发送了FIN,则这个sokcet在server端将不能够被read和write,会返回错误(如果有read或者write则返回errno9: Bad file descriptor错误)
默认状态,是把此套接字标记为关闭状态(真正只是把套接字的引用计数-1),close函数都立刻返回。如果引用计数为0,才开始发送四字节的FIN分节(连接终止序列),close在sever端调用之后,会立刻返回,同时尽力发送此时仍然在套接字发送缓冲区的发送数据,在此之后才会发出FIN四分节的连接终止序列。
扩展:在父子进程模型中,我们每次fork子进程之后,都需要在父进程中关闭连接套接字(connfd),而在子进程中都需要关闭监听套接字,此模型中accept在fork函数之前。
原因有两点:
1、如果不关闭,则父进程将消耗所以的可用描述符,因为它每次accept新连接,都得返回一个新的未用的fd
2.如果不关父进程中的connfd,则父子进程的引用计数都保持在2,即便子进程关闭,只是把引用计数-1,这样由于父进程正常情况永远不会关闭,所以这些连接其实永远都不会关闭,消耗资源,因为这阻挡了终止序列FIN的发出。
二:shutdown(int fd,int how)
close是关闭读写两个方向的数据传输,而shutdown指定关闭SHUT_WRS称为半关闭,只关闭一个方向,另一个方向依然可用。
两个参数的意思:
shutdown:SHUT_RD
server端关闭连接的读这一半,进程不能再对这样的套接字调用任何读操作(如果有read则返回errno9: Bad file descriptor错误)。这个SHUT_RD标志告诉内核,我绝食了,再有吃的来(数据),不要放我的餐桌上了(接收缓冲区)直接扔给狗吃把。(但是此时client还是可以发送给server套接字的接收缓冲区,只是被TCP抛弃而已)
套接字不再有数据可接受,server不能调用read,server进程仍可往套接字发送数据,server套接字接收缓冲区中所有数据被丢弃,再接收到的任何数据都被TCP丢弃,对套接字发送缓冲区没有任何影响;
shutdown:SHUT_WR(又叫做半关闭):与close不同的是,无论引用计数是否为0,这样的操作都进行,此后server进程不能对此套接字进行写操作)
当前server套接字发送缓冲区中的内容被发送到对端,后跟正常的TCP连接终止序列(即发送FIN),
关闭连接的写这一半,进程不能再对这样的套接字调用任何写操作;(如果server有write则返回errno9: Bad file descriptor错误)
在server的套接字上不能再发出发送请求,进程仍可从套接字接收数据,当前server套接字发送缓冲区中的内容被发送到对端,后跟正常的TCP连接终止序列(即发送FIN),对套接字接收缓冲区无任何影响;
注意:1. server端,close调用之后如果在local TCP buffer内如果还有数据没有读取会给对方,则主动直接发送RST给client, 否则发送FIN,这取决于调用close时刻local TCP buffer的状态, 跟对端是不是继续发送数据无关。
2. shutdown关闭读不会给对方发FIN, 只有关闭写才会发FIN, 而且跟local TCP buffer状态没关系,只发送FIN包,从不发送RST
总结:shutdown从不发送RST给对端(只发送FIN),只有close在接收缓存区没有被读完的条件下,才发送给对端RST。