1、close函数
关闭套接口,终止TCP连接。
#include<unistd.h>
int close(int sockfd);
默认情况下,close会将一个tcp连接终止,立即返回到调用进程。不能在这个套接口上面进行read和write。但是,若该套接口缓冲区已经有一些数据了,TCP将尝试将这些数据发送到对端,发送完毕就是正常的TCP连接终止序列。就是TCP终止的4次握手。
close有一个需要注意的是,close关闭,描述字的引用计数值-1。如这个秒速字的引用计数值不为0,那么就不会发生正常的TCP连接终止序列。这个运用在父进程与子进程共享一个套接口上有很好的运用。
2、shutdown
#include<sys/socket.h>
int shutdown(int sockfd,int howto);
close有两个限制:
2.1、close把描述字引用计数值-1,仅在描述字引用计数值为0的时候才关闭套接口。shutdown可以不管引用计数值就可以激发正常的TCP的连接终止序列。
2.2、close关闭套接口是数据发送接收两个方面。有时候我们需要只关闭某一方向,毕竟TCP是全双工的。
于是shutdown有用武之地了。
howto这个参数可以取下面的值:
SHUT_RD 关闭连接的读这一半。记住,接收缓冲区里面现有的数据都被丢弃。不能对该套接口进行读。对一个TCP套接口调用了这样的shutdown,该套接口会接收对端的所有数据,然后发送确认。然后丢弃这些数据。这里面我们可以看到,我们可以继续发送数据确认。说明发送还是可以的。
SHUT_WR 关闭连接的写这一半。这称为半关闭。当前发送缓冲区的数据将被发送,然后跟着发送正常的TCP终止序列。不管套接口是不是引用计数值是不是0。都这样执行。进程不能对该套接口进行任何的写。
SHUT_RDWR 这个与调用shutsown两次等效。第一次调用SHUT_RD,第二次调用SHUT_WR。
具体的对应的数值是 SHUT_RD:0,SHUT_WR:1,SHUT_RDWR:2.这个在函数调用的时候可以使用数字0、1、2.
3、colse的设置
关于我们1所讨论的都是close默认的情况,那么close可以设置吗?答案是肯定的,套接口选项中,有一个SO_LINGER,这个选项指定close如何对套接口如何操作。
struct linger
{
int l_onoff;
int l_linger;
}
l_onoff:为0表示SO_LINGER这个选项无效,为非0,分两种情况,一是l_linger为0,close关闭某一个套接口时候,TCp会丢弃发送缓冲区的数据,并发送一个RST给对端。不会有正常的四分组连接终止序列。第二种情况是l_linger不为0,那么套接口关闭时内核将拖延一段时间,那么这个时间可以用来对套接口缓冲区的残留数据进行处理。进程进入睡眠,知道要么所有数据已经发送完毕,或则延滞时间到了。如果数据发送完并被确认之前延滞时间到了,close会返回一个EWOULDBLOCK错误,并且套接口缓冲区的任何残留数据都被丢弃。
close缺省操作,注意close返回的位置
设置一个SO_LINGER,指定一个正的延滞时间。这样的情况客户的close会等到TCP确认之后才返回(当然延滞时间一定适当长,不然会返回EWOULDBLOCK错误)
上面有一个问题,就是我们知道数据已经发送给目的套接口,但是我们并不知道数据到底是否被应用进程接收了。数据发送过去还有一个拷贝到应用程序的过程,我们不知道这个步骤有没有做。
可以用shutdown来获知,后面接一个read,让read阻塞。就行。不能用close,因为close是关read和write,那么后面的read就无效。
另外我们获知应用程序已经读取我们的数据另外一个办法是使用应用级确认,简称ACK。就是数据接收完毕后,给发送套接口发送一个确认消息。这样就知道了。
close,shutdown记录。
《Unix网络编程》