close:
当套接字的引用计数为0的时候才会引发TCP的四分组连接终止序列;
shutdown:
不用管套接字的引用计数就激发TCP的正常连接终止序列;
这里由一个SO_LINGER套接字选项
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time,POSIX specifies units as seconds */
};
shutdown:SHUT_RD
关闭连接的读这一半,进程不能再对这样的套接字调用任何读操作;
在套接字上不能再发出接收请求,进程仍可往套接字发送数据,套接字接收缓冲区中所有数据被丢弃,再接收到的任何数据由TCP丢弃,对套接字发送缓冲区没有任何影响;
程序模拟:
client:
- struct sockaddr_in serverAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
- serverAdd.sin_port = htons(SERV_PORT);
- int connfd = socket(AF_INET, SOCK_STREAM, 0);
- int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (connResult < 0) {
- printf("连接失败\n");
- close(connfd);
- return;
- }
- else
- {
- printf("连接成功\n");
- }
- ssize_t writeLen;
- char sendMsg[5000] = {0};
- unsigned long long totalSize = 0;
- if (1) {
- writeLen = write(connfd, sendMsg, sizeof(sendMsg));
- if (writeLen < 0) {
- printf("发送失败 errno = %d\n",errno);
- return;
- }
- else
- {
- totalSize += writeLen;
- printf("发送成功 totalSize = %zd\n",totalSize);
- }
- sleep(20);
- }
server:
- #define SERV_PORT 8000
- int main(int argc, const char * argv[])
- {
- struct sockaddr_in serverAdd;
- struct sockaddr_in clientAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
- serverAdd.sin_port = htons(SERV_PORT);
- socklen_t clientAddrLen;
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- int yes = 1;
- setsockopt(listenfd,
- SOL_SOCKET, SO_REUSEADDR,
- (void *)&yes, sizeof(yes));
- if (listenfd < 0) {
- printf("创建socket失败\n");
- return -1;
- }
- int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (bindResult < 0) {
- printf("绑定端口失败\n");
- close(listenfd);
- return -1;
- }
- listen(listenfd, 20);
- int connfd;
- unsigned char recvMsg[246988];
- unsigned long long totalSize = 0;
- clientAddrLen = sizeof(clientAdd);
- connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
- if (connfd < 0) {
- printf("连接失败\n");
- return -1;
- }
- else{
- // 这里我们用于测试,只接收一个连接
- close(listenfd);
- }
- shutdown(connfd,SHUT_RD);
- while (1) {
- ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
- if (readLen < 0) {
- printf("读取失败 errno = %d\n",errno);
- close(connfd);
- return -1;
- }
- else if (readLen == 0) {
- printf("读取完成 totalSize = %llu\n",totalSize);
- close(connfd);
- return -1;
- }
- else
- {
- totalSize += readLen;
- printf("readLen:%ld\n",readLen);
- }
- }
- return 0;
- }
client:
连接成功
发送成功 totalSize = 5000
server:
读取完成 totalSize = 0
是因为读关闭了,套接字接收缓冲区的数据直接被丢弃掉了,如果将服务端的shutdiwn替换成close会如下
读取失败 errno = 9
#defineEBADF 9/* Bad file descriptor */
说明:如果服务端调用shutdown关闭读之后,客户端再往已经收到RST分节的服务端进行write操作,会引发错误导致程序终止,RST详解里面有讲到
shutdown:SHUT_WR
关闭连接的写这一半,进程不能再对这样的套接字调用任何写操作;
在套接字上不能再发出发送请求,进程仍可从套接字接收数据,套接字发送缓冲区中的内容被发送到对端,后跟正常的TCP连接终止序列(即发送FIN),对套接字接收缓冲区无任何影响;
程序模拟:
client:
- struct sockaddr_in serverAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
- serverAdd.sin_port = htons(SERV_PORT);
- int connfd = socket(AF_INET, SOCK_STREAM, 0);
- int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (connResult < 0) {
- printf("连接失败\n");
- close(connfd);
- return;
- }
- else
- {
- printf("连接成功\n");
- }
- ssize_t writeLen;
- char sendMsg[200000] = {0};
- unsigned long long totalSize = 0;
- if (1) {
- writeLen = write(connfd, sendMsg, sizeof(sendMsg));
- if (writeLen < 0) {
- printf("发送失败 errno = %d\n",errno);
- return;
- }
- else
- {
- totalSize += writeLen;
- printf("发送成功 totalSize = %zd\n",totalSize);
- }
- shutdown(connfd,SHUT_WR);
- sleep(20);
- }
server:
- int main(int argc, const char * argv[])
- {
- struct sockaddr_in serverAdd;
- struct sockaddr_in clientAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
- serverAdd.sin_port = htons(SERV_PORT);
- socklen_t clientAddrLen;
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- int yes = 1;
- setsockopt(listenfd,
- SOL_SOCKET, SO_REUSEADDR,
- (void *)&yes, sizeof(yes));
- if (listenfd < 0) {
- printf("创建socket失败\n");
- return -1;
- }
- int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (bindResult < 0) {
- printf("绑定端口失败\n");
- close(listenfd);
- return -1;
- }
- listen(listenfd, 20);
- int connfd;
- unsigned char recvMsg[246988];
- unsigned long long totalSize = 0;
- clientAddrLen = sizeof(clientAdd);
- connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
- if (connfd < 0) {
- printf("连接失败\n");
- return -1;
- }
- else{
- // 这里我们用于测试,只接收一个连接
- close(listenfd);
- }
- while (1) {
- ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
- if (readLen < 0) {
- printf("读取失败 errno = %d\n",errno);
- close(connfd);
- return -1;
- }
- else if (readLen == 0) {
- printf("读取完成 totalSize = %llu\n",totalSize);
- close(connfd);
- return -1;
- }
- else
- {
- totalSize += readLen;
- printf("readLen:%ld\n",readLen);
- }
- }
- return 0;
- }
打印信息:
client:
连接成功
发送成功 totalSize = 200000
server:
readLen:1448
readLen:1448
readLen:1448
readLen:1448
... ...
readLen:1448
readLen:1448
readLen:3072
读取完成 totalSize = 200000
说明:客户端发送字节200000的大小大于TCP套接字发送缓冲区的大小,数据全部成功送达服务端,但是在调用shutdown关闭写之后不能再调用write方法,会引起错误终止程序,这里和上面的关闭读有差别;
shutdown:SHUT_RDWR
关闭连接的读和写;
close:l_onoff = 0(默认情况):
在套接字上不能再发出发送或接收请求,套接字发送缓冲区中的内容被发送到对端,如果描述符引用计数变为0,在发送完发送缓冲区的数据后,跟以正常的TCP连接终止序列(即发送FIN),套接字接收缓冲区中的内容被丢弃;
程序模拟:
client:
- struct sockaddr_in serverAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
- serverAdd.sin_port = htons(SERV_PORT);
- int connfd = socket(AF_INET, SOCK_STREAM, 0);
- int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (connResult < 0) {
- printf("连接失败\n");
- close(connfd);
- return;
- }
- ssize_t writeLen;
- char sendMsg[5000] = {0};
- for (int i = 0 ; i<sizeof(sendMsg); i++) {
- sendMsg[i] = 'a';
- }
- while (1) {
- writeLen = write(connfd, sendMsg, sizeof(sendMsg));
- if (writeLen < 0) {
- printf("发送失败\n");
- return;
- }
- else
- {
- printf("发送成功 writeLen = %zd\n",writeLen);
- }
- close(connfd);
- break;
- }
server:
- #define SERV_PORT 8000
- int main(int argc, const char * argv[])
- {
- struct sockaddr_in serverAdd;
- struct sockaddr_in clientAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
- serverAdd.sin_port = htons(SERV_PORT);
- socklen_t clientAddrLen;
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- int yes = 1;
- setsockopt(listenfd,
- SOL_SOCKET, SO_REUSEADDR,
- (void *)&yes, sizeof(yes));
- if (listenfd < 0) {
- printf("创建socket失败\n");
- return -1;
- }
- int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (bindResult < 0) {
- printf("绑定端口失败\n");
- close(listenfd);
- return -1;
- }
- listen(listenfd, 20);
- int connfd;
- unsigned char recvMsg[246988];
- unsigned long long totalSize = 0;
- clientAddrLen = sizeof(clientAdd);
- connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
- if (connfd < 0) {
- printf("连接失败\n");
- return -1;
- }
- else{
- // 这里我们用于测试,只接收一个连接
- close(listenfd);
- }
- sleep(1);
- close(connfd);
- while (1)
- {
- ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
- if (readLen < 0) {
- printf("读取失败\n");
- break;
- }
- else if (readLen == 0) {
- printf("读取完成 totalSize = %llu\n",totalSize);
- break;
- }
- else
- {
- totalSize += readLen;
- printf("readLen:%ld\n",readLen);
- }
- }
- return 0;
- }
先运行服务端,再运行客户端,打印信息如下:
client:
发送成功 writeLen = 5000
server:
读取失败
抓包信息:
说明:通过抓包可以看出客户端的5000字节的数据都成功发送到服务端,服务器里面有一秒的休眠才关闭连接就是为了接收完这些数据,关闭连接以后再来通过read读取接收缓冲区的数据却读取失败,说明已经被内核丢弃了,而客户端的数据是已经成功写到发送缓冲区,即使关闭连接,发送缓冲区的数据不会被丢弃,会正常发送到服务器,发送完成以后,接着发送终止连接的第一个FIN分节;
close:l_onoff = 1,l_linger = 0:
在套接字上不能再发出发送或接收请求,如果描述符引用计数变为0,RST被发送到对端,连接的状态被置为CLOSED(没有TIME_WAIT状态),套接字发送缓冲区和接收缓冲区中的内容被丢弃;
模拟程序(套接字发送缓冲区数据被丢弃):
client:
- struct sockaddr_in serverAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
- serverAdd.sin_port = htons(SERV_PORT);
- int connfd = socket(AF_INET, SOCK_STREAM, 0);
- int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (connResult < 0) {
- printf("连接失败\n");
- close(connfd);
- return;
- }
- else
- {
- printf("连接成功\n");
- }
- ssize_t writeLen;
- char sendMsg[200000] = {0};
- unsigned long long totalSize = 0;
- struct linger so_linger;
- so_linger.l_onoff = 1;
- so_linger.l_linger = 0;
- setsockopt(connfd,
- SOL_SOCKET,
- SO_LINGER,
- &so_linger,
- sizeof so_linger);
- if (1) {
- writeLen = write(connfd, sendMsg, sizeof(sendMsg));
- if (writeLen < 0) {
- printf("发送失败 errno = %d\n",errno);
- return;
- }
- else
- {
- totalSize += writeLen;
- printf("发送成功 totalSize = %zd\n",totalSize);
- }
- close(connfd);
- }
server:
- int main(int argc, const char * argv[])
- {
- struct sockaddr_in serverAdd;
- struct sockaddr_in clientAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
- serverAdd.sin_port = htons(SERV_PORT);
- socklen_t clientAddrLen;
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- int yes = 1;
- setsockopt(listenfd,
- SOL_SOCKET, SO_REUSEADDR,
- (void *)&yes, sizeof(yes));
- if (listenfd < 0) {
- printf("创建socket失败\n");
- return -1;
- }
- int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (bindResult < 0) {
- printf("绑定端口失败\n");
- close(listenfd);
- return -1;
- }
- listen(listenfd, 20);
- int connfd;
- unsigned char recvMsg[246988];
- unsigned long long totalSize = 0;
- clientAddrLen = sizeof(clientAdd);
- connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
- if (connfd < 0) {
- printf("连接失败\n");
- return -1;
- }
- else{
- // 这里我们用于测试,只接收一个连接
- close(listenfd);
- }
- while (1) {
- sleep(1);
- ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
- if (readLen < 0) {
- printf("读取失败 errno = %d\n",errno);
- close(connfd);
- return -1;
- }
- else if (readLen == 0) {
- printf("读取完成 totalSize = %llu\n",totalSize);
- close(connfd);
- return -1;
- }
- else
- {
- totalSize += readLen;
- printf("readLen:%ld\n",readLen);
- }
- }
- return 0;
- }
打印信息:
client:
连接成功
发送成功 totalSize = 200000
server:
readLen:91224
读取失败 errno = 54
抓包信息:
说明:调用close的时候RST分节发送出去,携带89777到91224字节的数据,然后丢弃发送缓冲区中的数据,因此服务器再向已经收到RST分节的套接字read失败,返回错误码:
#define ECONNRESET54/* Connection reset by peer */
再看下面的模拟程序(套接字接收缓冲区数据被丢弃):
client:
- struct sockaddr_in serverAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
- serverAdd.sin_port = htons(SERV_PORT);
- int connfd = socket(AF_INET, SOCK_STREAM, 0);
- int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (connResult < 0) {
- printf("连接失败\n");
- close(connfd);
- return;
- }
- else
- {
- printf("连接成功\n");
- }
- ssize_t writeLen;
- char sendMsg[200000] = {0};
- unsigned long long totalSize = 0;
- while (1) {
- writeLen = write(connfd, sendMsg, sizeof(sendMsg));
- if (writeLen < 0) {
- printf("发送失败 errno = %d\n",errno);
- return;
- }
- else
- {
- totalSize += writeLen;
- printf("发送成功 totalSize = %zd\n",totalSize);
- }
- }
server:
- int main(int argc, const char * argv[])
- {
- struct sockaddr_in serverAdd;
- struct sockaddr_in clientAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
- serverAdd.sin_port = htons(SERV_PORT);
- socklen_t clientAddrLen;
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- int yes = 1;
- setsockopt(listenfd,
- SOL_SOCKET, SO_REUSEADDR,
- (void *)&yes, sizeof(yes));
- if (listenfd < 0) {
- printf("创建socket失败\n");
- return -1;
- }
- int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (bindResult < 0) {
- printf("绑定端口失败\n");
- close(listenfd);
- return -1;
- }
- listen(listenfd, 20);
- int connfd;
- unsigned char recvMsg[246988];
- unsigned long long totalSize = 0;
- clientAddrLen = sizeof(clientAdd);
- connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
- if (connfd < 0) {
- printf("连接失败\n");
- return -1;
- }
- else{
- // 这里我们用于测试,只接收一个连接
- close(listenfd);
- }
- struct linger so_linger;
- so_linger.l_onoff = 1;
- so_linger.l_linger = 0;
- setsockopt(connfd,
- SOL_SOCKET,
- SO_LINGER,
- &so_linger,
- sizeof so_linger);
- while (1) {
- sleep(1);
- ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
- if (readLen < 0) {
- printf("读取失败 errno = %d\n",errno);
- close(connfd);
- return -1;
- }
- else if (readLen == 0) {
- printf("读取完成 totalSize = %llu\n",totalSize);
- close(connfd);
- return -1;
- }
- else
- {
- totalSize += readLen;
- printf("readLen:%ld\n",readLen);
- }
- close(connfd);
- }
- return 0;
- }
打印信息:
client:
连接成功
发送成功 totalSize = 200000
发送成功 totalSize = 400000
发送成功 totalSize = 600000
(lldb)
然后程序返回错误被终止server:
readLen:246988
读取失败 errno = 9
#define EBADF9/* Bad file descriptor */
抓包信息:
说明:服务端调用close以后直接发送RST分节,接收缓冲区数据被丢弃,客户端向已经收到RST分节的套接字write会返回错误终止程序
close:l_onoff = 1,l_linger != 0:
在套接字上不能再发出发送或接收请求,套接字发送缓冲区中的内容被发送到对端,如果描述符引用计数变为0,在发送完发送缓冲区的数据后,跟以正常的TCP连接终止序列(即发送FIN),套接字接收缓冲区中的内容被丢弃,如果在连接变为CLOSED状态前延滞时间到,那么close返回EWOULDBLOCK错误;
模拟程序:
client:
- struct sockaddr_in serverAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
- serverAdd.sin_port = htons(SERV_PORT);
- int connfd = socket(AF_INET, SOCK_STREAM, 0);
- int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (connResult < 0) {
- printf("连接失败\n");
- close(connfd);
- return;
- }
- else
- {
- printf("连接成功\n");
- }
- ssize_t writeLen;
- char sendMsg[200000] = {0};
- unsigned long long totalSize = 0;
- struct linger so_linger;
- so_linger.l_onoff = 1;
- so_linger.l_linger = 3;
- setsockopt(connfd,
- SOL_SOCKET,
- SO_LINGER,
- &so_linger,
- sizeof so_linger);
- if (1) {
- writeLen = write(connfd, sendMsg, sizeof(sendMsg));
- if (writeLen < 0) {
- printf("发送失败 errno = %d\n",errno);
- return;
- }
- else
- {
- totalSize += writeLen;
- printf("发送成功 totalSize = %zd\n",totalSize);
- }
- int cr = close(connfd);
- if (cr < 0) {
- printf("失败 errno = %d\n",errno);
- }
- }
server:
- int main(int argc, const char * argv[])
- {
- struct sockaddr_in serverAdd;
- struct sockaddr_in clientAdd;
- bzero(&serverAdd, sizeof(serverAdd));
- serverAdd.sin_family = AF_INET;
- serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
- serverAdd.sin_port = htons(SERV_PORT);
- socklen_t clientAddrLen;
- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
- int yes = 1;
- setsockopt(listenfd,
- SOL_SOCKET, SO_REUSEADDR,
- (void *)&yes, sizeof(yes));
- if (listenfd < 0) {
- printf("创建socket失败\n");
- return -1;
- }
- int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
- if (bindResult < 0) {
- printf("绑定端口失败\n");
- close(listenfd);
- return -1;
- }
- listen(listenfd, 20);
- int connfd;
- unsigned char recvMsg[20000];
- unsigned long long totalSize = 0;
- clientAddrLen = sizeof(clientAdd);
- connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
- if (connfd < 0) {
- printf("连接失败\n");
- return -1;
- }
- else{
- // 这里我们用于测试,只接收一个连接
- close(listenfd);
- }
- while (1) {
- sleep(1);
- ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
- if (readLen < 0) {
- printf("读取失败 errno = %d\n",errno);
- close(connfd);
- return -1;
- }
- else if (readLen == 0) {
- printf("读取完成 totalSize = %llu\n",totalSize);
- close(connfd);
- return -1;
- }
- else
- {
- totalSize += readLen;
- printf("readLen:%ld\n",readLen);
- }
- }
- return 0;
- }
打印信息:
client:
连接成功
发送成功 totalSize = 200000
server:
readLen:20000
... ...
readLen:20000
读取完成 totalSize = 200000
抓包信息:
说明:数据成功发送,且发送正常的FIN分节
现在我们看看对于不同情况的close的返回情况和可能遇到的一些问题:
1.默认操作的close
说明: 我们已经知道write操作返回成功只能说明数据已经发送到套接字的发送缓冲区,不能代表对端已经成功收到数据,close的默认返回成功也只是成功发出了一个FIN分节,也不代表对端已经确认
问题1: 如果中途网络发生故障,很有可能服务端接收不到这个来自客户端的FIN分节;
问题2: 假设服务器忙,那么来自客户端的数据由TCP加入到套接字接收缓冲区,下一个FIN分节也被加入到套接字接收缓冲区,然后等待处理,如果正好此时服务器应用进程崩溃掉,那么这些数据就丢失掉了,服务器并没有真正收到,而客户端也永远不会知道;
2.设置SO_LINGER套接字选项且l_linger为正值时的close
说明: 这种情况下客户的close要到它的数据和FIN已经被服务器的TCP确认以后才会返回;
问题: 同上问题2
3.设置SO_LINGER套接字选项且l_linger为偏小正值时的close
说明: 在服务端的确认到达之前,SO_LINGER套接字选项设置的延滞时间到,close将会返回EWOULDBLOCK错误,且套接字发送缓冲区中的任何残留数据被丢弃。
问题: 同问题2
总结: 设置SO_LINGER套接字选项以后,close的成功返回只是告诉我们先前发送的数据的FIN已经由对端TCP确认,而不能告诉我们对端应用进程是否已经读取数据,如果不设置该套接字选项,那么我们连对端TCP是否确认了数据都不知道。
解决方法:
1.使用shutdown(设置SHUT_WR)+read
说明:调用写关闭的shutdown以后发送FIN分节,然后执行read,返回0则说明服务端已经读取数据然后发送终止连接的第三个分节(FIN分节)。
2.应用级确认,可由客户端和服务端指定一个终止协议
tcp本身不提供记录结束标志,tcp是一个字节流协议,没有任何记录边界:如果应用程序需要确定记录的边界,它就要自己去实现;
参考:
《 UNIX Network ProgrammingVolume 1, Third Edition: TheSockets Networking API 》