首页 » PHP教程 » phpsocketshutdown技巧_linux Socket编程之TCP之closeshutdown函数实例详解

phpsocketshutdown技巧_linux Socket编程之TCP之closeshutdown函数实例详解

访客 2024-12-09 0

扫一扫用手机浏览

文章目录 [+]

当套接字的引用计数为0的时候才会引发TCP的四分组连接终止序列;

shutdown:

phpsocketshutdown技巧_linux Socket编程之TCP之closeshutdown函数实例详解

不用管套接字的引用计数就引发TCP的正常连接终止序列;

phpsocketshutdown技巧_linux Socket编程之TCP之closeshutdown函数实例详解
(图片来自网络侵删)

这里由一个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:

#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;}

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

#define EBADF 9 / Bad file descriptor /

解释:如果做事端调用shutdown关闭读之后,客户端再往已经收到RST分节的做事端进行write操作,会引发缺点导致程序终止

抓包信息:

须要C/C++ Linux做事器架构师学习资料私信“资料”(资料包括C/C++,Linux,golang技能,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

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 ECONNRESET 54 / 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 EBADF 9 / 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分节

相关文章

移动付出php技巧_PHP的支付宝APP支付

文|何掌柜第一步:创建运用并获取APPID要在您的运用中利用支付宝开放产品的接谈锋能,您须要先去蚂蚁金服开放平台(open.ali...

PHP教程 2024-12-11 阅读0 评论0