當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。