套接字选项SO_LINGER
来源:互联网 发布:侠客风云传优化差 编辑:程序博客网 时间:2024/05/17 09:25
在说明套接字选项SO_LINGER之前,我们来先看一个问题。如果发送缓冲区中还有数据没有发送到对方协议栈,此时close发送端的socket会发生什么,下面代码给出答案。
服务端:
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <stdarg.h>#include <fcntl.h>#include <signal.h>#include <sys/wait.h>int main(){ char recvbuf[100000]={0}; int sockSrv = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addrSrv; addrSrv.sin_family = AF_INET; addrSrv.sin_addr.s_addr =htonl(INADDR_ANY); addrSrv.sin_port = htons(8888); bind(sockSrv, (const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in)); listen(sockSrv, 5); sockaddr_in addrClient; socklen_t len=sizeof(addrClient); int sockConn = accept(sockSrv, (struct sockaddr *)&addrClient, &len); while(1) { getchar(); read(sockConn,recvbuf,sizeof(recvbuf)-1); } getchar(); close(sockSrv); return 0;}客户端:
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <stdarg.h>#include <fcntl.h>#include <error.h>int main(){ int ret=0; int sockClient = socket(AF_INET, SOCK_STREAM, 0); char readbuf[175000]={0}; struct sockaddr_in addrSrv; addrSrv.sin_addr.s_addr=inet_addr("127.0.0.1"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); ret=connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in)); write(sockClient,readbuf,sizeof(readbuf)+1); getchar(); close(sockClient); return 0;}
编译并运行,并用tcpdump进行抓包,在客户端多按几次回车键,用netstat命令查看情况。
[mapan@localhost ~]$ netstat -nao|grep 8888 tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN off (0.00/0/0)tcp 161018 0 127.0.0.1:8888 127.0.0.1:41282 ESTABLISHED off (0.00/0/0)tcp 0 124001 127.0.0.1:41282 127.0.0.1:8888 ESTABLISHED probe (0.19/0/1)
其中1611018为服务端接收缓冲区中的数据,124001为发送缓冲区中的数据。此时由于服务端没有调用read函数读取数据,服务端的接收缓冲区已满,所以客户端的发送缓冲区的数据不能由TCP到达服务端的接收缓冲区中。如果这时我们突然断开客户端,客户端调用了close函数,close函数立即返回,发送缓冲区剩余的数据由系统接管将数据发送至对端,但是我们并不知道对方的是否已接收到数据,这时SO_LINGER选项改登场了。
SO_LINGER结构如下:
struct linger { int l_onoff //0=off, nonzero=on(开关) int l_linger //linger time(延迟时间) }1)当l_onoff为0时,l_linger的值被忽略,这也是close的默认操作。
2)当l_onoff为0时,l_linger的值也为0。在这个情况下,close将会延迟l_linger秒,这里是0秒。此时当调用close的时候,TCP连接会立即断开。发送缓冲区里面剩余的数据将被丢弃,并向对方发送一个RST,这是非正常的断开连接。下面看代码,服务端不变,客户端稍微变一下。
客户端:
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <stdarg.h>#include <fcntl.h>#include <error.h>int main(){ int ret=0; int sockClient = socket(AF_INET, SOCK_STREAM, 0); char readbuf[175000]={0}; struct sockaddr_in addrSrv; addrSrv.sin_addr.s_addr=inet_addr("127.0.0.1"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); struct linger so_linger; so_linger.l_onoff=1; so_linger.l_linger=0; setsockopt(sockClient,SOL_SOCKET,SO_LINGER,&so_linger,sizeof(so_linger)); ret=connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in)); write(sockClient,readbuf,sizeof(readbuf)+1); getchar(); close(sockClient); return 0;}
编译并运行,用netstat查看。
[mapan@localhost ~]$ netstat -nao|grep 8888 tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN off (0.00/0/0)tcp 138900 0 127.0.0.1:8888 127.0.0.1:41332 ESTABLISHED off (0.00/0/0)tcp 0 36101 127.0.0.1:41332 127.0.0.1:8888 ESTABLISHED probe (1.23/0/0)此时在客户端还卡在getchar()出,在客户端按下回车键,查看tcpdump抓包的结果。抓包结构如下:
[root@localhost mapan]# tcpdump -iany port 8888 -nlps0tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes19:06:53.835303 IP 127.0.0.1.41336 > 127.0.0.1.ddi-tcp-1: Flags [S], seq 2551942925, win 65495, options [mss 65495,sackOK,TS val 3407479544 ecr 0,nop,wscale 7], length 019:06:53.835318 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41336: Flags [S.], seq 3358092135, ack 2551942926, win 65483, options [mss 65495,sackOK,TS val 3407479544 ecr 3407479544,nop,wscale 7], length 019:06:53.835333 IP 127.0.0.1.41336 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3407479544 ecr 3407479544], length 019:06:53.835421 IP 127.0.0.1.41336 > 127.0.0.1.ddi-tcp-1: Flags [.], seq 1:32742, ack 1, win 512, options [nop,nop,TS val 3407479544 ecr 3407479544], length 3274119:06:53.835431 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41336: Flags [.], ack 32742, win 829, options [nop,nop,TS val 3407479544 ecr 3407479544], length 019:06:53.835466 IP 127.0.0.1.41336 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 32742:65483, ack 1, win 512, options [nop,nop,TS val 3407479544 ecr 3407479544], length 3274119:06:53.835558 IP 127.0.0.1.41336 > 127.0.0.1.ddi-tcp-1: Flags [.], seq 65483:98224, ack 1, win 512, options [nop,nop,TS val 3407479544 ecr 3407479544], length 3274119:06:53.835571 IP 127.0.0.1.41336 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 98224:130965, ack 1, win 512, options [nop,nop,TS val 3407479544 ecr 3407479544], length 3274119:06:53.874708 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41336: Flags [.], ack 130965, win 62, options [nop,nop,TS val 3407479584 ecr 3407479544], length 019:06:54.079730 IP 127.0.0.1.41336 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 130965:138901, ack 1, win 512, options [nop,nop,TS val 3407479789 ecr 3407479584], length 793619:06:54.079741 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41336: Flags [.], ack 138901, win 0, options [nop,nop,TS val 3407479789 ecr 3407479789], length 019:06:54.241704 IP 127.0.0.1.41336 > 127.0.0.1.ddi-tcp-1: Flags [R.], seq 138901, ack 1, win 512, options [nop,nop,TS val 3407479951 ecr 3407479789], length 0
客户端发RST了。
3)当l_onoff为非零,l_linger也为非零。此时调用close函数时,内核将会延迟l_linger秒直到所有数据都发送对端且收到应答消息或者l_linger秒被消耗完。如果l_linger被消耗完,发送缓冲区中还有数据,则剩余的数据被丢弃,并向对端发送RST。如果套接字处于非阻塞状态,发送缓冲区的数据立即丢弃,close返回,所以要判断一下close函数的返回值。还是服务端代码不变,客户端代码稍作修改,客户端代码如下:#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <stdarg.h>#include <fcntl.h>#include <error.h>int main(){ int ret=0; int sockClient = socket(AF_INET, SOCK_STREAM, 0); char readbuf[175000]={0}; struct sockaddr_in addrSrv; addrSrv.sin_addr.s_addr=inet_addr("127.0.0.1"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); struct linger so_linger; so_linger.l_onoff=1; so_linger.l_linger=60; setsockopt(sockClient,SOL_SOCKET,SO_LINGER,&so_linger,sizeof(so_linger)); ret=connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in)); write(sockClient,readbuf,sizeof(readbuf)+1); getchar(); close(sockClient); return 0;}
编译并运行,先运行服务端后运行客户端。和上面一样,此时客户端发送缓冲区内有残余数据。这时在客户端按下回车键会发现进程并没有结束,而是休眠了。然后在服务端连续按回车键,随着客户端进程就结束了,标明数据已全部发送到服务端且受到答复。我们看下tcpdump抓包结果:
[root@localhost mapan]# tcpdump -iany port 8888 -nlps0tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes19:27:14.937303 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [S], seq 1492693418, win 65495, options [mss 65495,sackOK,TS val 3408700646 ecr 0,nop,wscale 7], length 019:27:14.937319 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [S.], seq 1875150207, ack 1492693419, win 65483, options [mss 65495,sackOK,TS val 3408700646 ecr 3408700646,nop,wscale 7], length 019:27:14.937334 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3408700646 ecr 3408700646], length 019:27:14.937425 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [.], seq 1:32742, ack 1, win 512, options [nop,nop,TS val 3408700646 ecr 3408700646], length 3274119:27:14.937434 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [.], ack 32742, win 829, options [nop,nop,TS val 3408700646 ecr 3408700646], length 019:27:14.937474 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 32742:65483, ack 1, win 512, options [nop,nop,TS val 3408700646 ecr 3408700646], length 3274119:27:14.937573 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [.], seq 65483:98224, ack 1, win 512, options [nop,nop,TS val 3408700646 ecr 3408700646], length 3274119:27:14.937586 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 98224:130965, ack 1, win 512, options [nop,nop,TS val 3408700646 ecr 3408700646], length 3274119:27:14.976713 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [.], ack 130965, win 62, options [nop,nop,TS val 3408700686 ecr 3408700646], length 019:27:15.181743 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 130965:138901, ack 1, win 512, options [nop,nop,TS val 3408700891 ecr 3408700686], length 793619:27:15.181754 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [.], ack 138901, win 0, options [nop,nop,TS val 3408700891 ecr 3408700891], length 019:27:15.386744 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3408701096 ecr 3408700891], length 019:27:15.386754 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [.], ack 138901, win 0, options [nop,nop,TS val 3408701096 ecr 3408700891], length 019:27:15.796774 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3408701506 ecr 3408701096], length 019:27:16.616810 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3408702326 ecr 3408701096], length 019:27:16.616820 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [.], ack 138901, win 0, options [nop,nop,TS val 3408702326 ecr 3408700891], length 019:27:16.807990 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [.], ack 138901, win 780, options [nop,nop,TS val 3408702517 ecr 3408700891], length 019:27:16.808017 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 138901:163706, ack 1, win 512, options [nop,nop,TS val 3408702517 ecr 3408702517], length 2480519:27:16.808021 IP 127.0.0.1.41348 > 127.0.0.1.ddi-tcp-1: Flags [FP.], seq 163706:175002, ack 1, win 512, options [nop,nop,TS val 3408702517 ecr 3408702517], length 1129619:27:16.808033 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [.], ack 163706, win 631, options [nop,nop,TS val 3408702517 ecr 3408702517], length 019:27:16.847713 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.41348: Flags [.], ack 175003, win 562, options [nop,nop,TS val 3408702557 ecr 3408702517], length 0最后4行是客户端发送数据到服务端,服务端给出ACK回应。
通过上面的学习,我们知道SO_LINGER能保证数据能发送到对端。
阅读全文
0 0
- 套接字选项SO_LINGER
- 套接字选项 SO_LINGER详解
- 套接字选项SO_LINGER, SO_KEEPALIVE等
- 54-套接字选项(SO_LINGER)
- SO_LINGER选项
- SO_LINGER选项
- setsockopt :SO_LINGER 选项设置
- setsockopt :SO_LINGER 选项设置
- 读书笔记: SO_LINGER 选项
- setsockopt 设置 SO_LINGER 选项
- setsockopt :SO_LINGER 选项设置
- setsockopt :SO_LINGER 选项设置
- setsockopt :SO_LINGER 选项设置
- TCP选项之SO_LINGER
- TCP选项之SO_LINGER
- setsockopt :SO_LINGER 选项设置
- setsockopt :SO_LINGER 选项设置
- tsockopt :SO_LINGER 选项设置 .
- RuntimeError: module compiled against API version 0xb but this version of numpy is 0xa
- Windows下搭建基于Anaconda的Tensorflow环境
- 懂行的进,电路原理图checklist
- apache kylin 2.1.0+CDH5.8 安装部署教程
- 基于ftplib的交互式ftp客户端实现
- 套接字选项SO_LINGER
- Rails—HTML样式
- System.currentTimeMillis() vs. new Date() vs. Calendar.getInstance().getTime()
- 小程序app.json 基础配置
- RESTful理解
- JQ标签页(jq-ui)
- Linux 调度器内幕
- java实现 斐波拉契数列
- iOS仿今日头条频道选择、仿微信项目、音频控件、多种动画效果等源码