1.close()函数:立即返回到进程
int close(int sockfd); //返回成功为0,出错为-1.
close 一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,该套接字描述符不能再由调用进程使用,也就是说它不能再作为read或write的第一个参数,然而TCP将尝试发送已排队等待发送到对端的任何数据,发送完毕后发生的是正常的TCP连接终止序列。
在多进程并发服务器中,父子进程共享着套接字,套接字描述符引用计数记录着共享着的进程个数,当父进程或某一子进程close掉套接字时,描述符引用计数会相应的减一,当引用计数仍大于零时,这个close调用就不会引发TCP的四路握手断连过程。
(假设服务器server端调用close)
如果server的socket套机字的接收缓冲区,还有数据未被应用进程读取,则server直接发送RST报文给client。
如果接收缓冲区没有数据了,但是发送缓冲区还有数据未被发送,则会尝试先发送此部分数据,然后才是发送FIN报文。
通过设置SO_LINNGER套接字口选项,可以改变缺省的功能。
struct linger {
int onoff; /* 0=off, nonzero=on */
int l_linger; /* linger time, POSIX specifies units as seconds */
};
1. close后,默认情况与使用l_onoff=0效果一致,本方继续接收数据并ACK,但会丢弃这些数据,直到对方FIN过来,完成连接终止过程;
2. close后,l_onoff非0且l_linger=0,立即丢弃发送缓存区数据并给对方发送一个RST,也就是不会有正常的四分组终止过程,也没有TIME_WAIT,对方收到RST后,肯定不能继续发数据了;
3. close后,l_onoff非0且l_linger>0,设置拖延时间,内核拖延,close会等待l_linger(阻塞情况下),两种可能,在延迟时间到达之前,数据发送完毕且被对方确认,或者延迟时间到了,通过进程检查close返回值判别,是那种情况,若是第二种,而返回ewouldbloack,且发送缓冲区的所有数据都丢失。
只要l_onoff为0,无论l_linger为什么值,都跟缺省情况完全一样
如果close造成的是发送FIN,在这之后,对方继续发送data 过来的话呢?
按照底层TCP协议,local侧是否继续发送ack of data给对方,直到local侧buffer满了
因为TCP两方的通信通道,local侧永远只能掌控local侧出去的方向,不能控制对方侧继续发送数据。即在FIN_WAIT2状态时,local侧依然可以接收对方的data
注意:缺省的情况下,server close(fd),且发送FIN给client之后,client端还是可以发送数据给server,只是server只是接收client发送到接收缓冲区的数据,然后全部丢失,也不应答。
2、shutdown()函数
int shutdown(int sockfd,int howto); //返回成功为0,出错为-1
shut_rd:关闭读这一半,在接收缓冲区的数据全部丢失,进程不能对此socket执行任何读操作,但是对方还是可以发送数据到此socket的接收缓冲区,且发送应答ack,只是接收缓冲区的数据全部丢失
shut_wr:关闭写一半,当前在发送缓冲区的数据首先被发送,然后才是FIN报文(这跟close一样),但是此函数不管套接字描述符的引用计数是否为0,都会执行操作,这根close不同。
两者之间比较:
close和shutdown的行为取决于内核的实现。
1. close调用之后如果在local TCP buffer内如果还有数据没有读取会给对方直接回RST, 否则发送FIN,这取决于调用close时刻local TCP buffer的状态, 跟对端是不是继续发送数据无关。
2. shutdown关闭读不会给对方发FIN, 只有关闭写才会发FIN, 而且跟local TCP buffer状态没关系,只发送FIN包,从不发送RST
设置l_onoff为非0,l_linger为非0,那么一种可能的情况是,在延迟时间到达之后,收到数据确认和FIN确认,此时close才返回,这告诉我们,发送的数据和FIN都被对方确认了。但是他不能告诉我们,数据是否被对方应用进程读取。但是不设置这个选项,连对方TCP是否确认了数据都不知道。
让client知道server读取了其数据的解决方法:
调用shutdown,之后read堵塞,直到对方的FIN发来(因为对方应用层读取数据和FIN之后,调用close),read才返回0.