一、close
close函数原型如下:
int close(int sockfd);
参数sockfd为套接字描述符,成功返回0,失败返回-1,错误码errno:EBADF表示一个非有效描述符;EINTER表示被信号中断;EIO表示一个IO错误。
该函数的功能是关闭套接字描述符引用计数,当计数大于0时什么都不干,当计数等于0时触发TCP/IP的四次挥手,即主动关闭方发送FIN。
比如:在多进程服务器中,父子进程共享套接字描述符,其引用计数为父进程数+子进程数的和,当父进程或其中某个子进程调用close函数时,描述符计数减一,当所有进程都调用close函数后,引用计数减到0,触发TCP/IP四次挥手过程。
如果在调用close以前,TCP协议栈的发送队列中有已排队等候发送的数据,则协议栈尝试将数据发送出去,发送完毕后根据套接字描述引用计数来决定是否关闭连接。
如果在调用close以后,且套接字描述符引用计数为0,则在其上调用write或者read函数则会产生错误码为9即EBADF的错误。
需要注意的是:调用close函数时,TCP协议栈对发送队列中已排队等候发送数据的处理流程受SO_LINGER选项的影响,该选项关联的数据结构如下:
struct linger{
int l_onoff; //0=off,nonzero=on
int l_linger; //linger time
};
l_onoff是选项开关,如果是0则关闭选项并且忽略参数l_linger,如果是非零则打开选项,默认是0;l_linger是延迟关闭连接时间,只有
l_onoff打开时即为非零时,该参数的值才有效。这几个参数与close时TCP协议栈对待发数据的处理流程如下表:
表1 待发数据处理流程
l_onoff
|
l_linger
|
close行为
|
发送队列
|
TCP协议栈
|
零
|
忽略
|
立即返回
|
保持直至发送完成
|
接管套接字并保证将数据发送至对端
|
非零
|
零
|
立即返回
|
立即放弃
|
直接发送RST,自身立即复位,不用经过2MSL状态,对端收到复位错误码
|
非零
|
非零
|
阻塞直到l_linger时间超时或数据发送完成(套接字必须设置为阻塞)
|
在超时时间段内保持尝试发送,若超时则立即放弃
|
超时则同第二种情况,若发送完成则皆大欢喜
|
二、shutdown
shutdown函数原型如下:
int shutdown(int sockfd, int howto);
参数sockfd为套接字描述符,howto取值如下:
(1)SHUT_RD:值为0,关闭连接的读功能。
(2)SHUT_WR:值为1,关闭连接的写功能。
(3)SHUT_RDWR:值为2,先关闭连接的读功能,再关闭连接的写功能。
成功返回0,错误返回-1,错误码errno:
EBADF表示一个非有效描述符;ENOTCONN表示在该描述符上未连接;ENOTSOCK表示是一个文件描述符而非套接字描述符。
shutdown提供了将四次挥手的过程拆分开的可能,需要注意的是,如果调用shutdown时填参数SHUT_WR或者SHUT_RDWR,然后调用write函数则会引发EPIPE/SIGPIPE,因为shutdown以这两个为参数时会发送FIN。
三、区别
1、是否触发四次挥手
close:只是减少套接字描述符的计数,如果计数为0,则触发四次挥手。
shutdown:拆分四次挥手过程,在设置howto参数为SHUT_WR或者SHUT_RDWR时,会立即发送FIN。
2、多进程共享描述符
close:只要描述符计数不为0,没有调用过该函数的进程仍然可以正常收发数据。
shutdown:无论描述符计数是多少,只要任一进程调用该函数都会破坏所有进程的连接,任一进程在该描述符上读取数据都会收到EOF结束符,写数据时会收到SIGPIPE信号。
阅读(6849) | 评论(0) | 转发(0) |