TCP异常处理(accept返回前连接中止)与SO_LINGER选项

一、accept返回前终止分析

问题一:

因为accept是堵塞的, 并且等待来自客户端的连接, 但是, 如果在accept期间 , 如果因为系统调用中断了accept就会返回一个非致命的错误, 而此时有来自客户端进行TCP三路握手完成后, 而我们通过循环在此调用accept函数可以完成连接,。因为TCP的连接是在内核中完成的, 与accept函数的执行无关。

问题二:

如果我们在调用accept函数返回之前, 该客户端TCP发送了一个RST(复位)。在服务器中, 表现为该连接仍在TCP队列中, 等待服务器进程调用accept的时候RST到达。此时返回的套接字是一个已连接,但是却有接受了RST的套接字。

模型图如下:

                                                            TCP异常处理(accept返回前连接中止)与SO_LINGER选项_第1张图片

*基于不同系统的处理方式如下:

1、源自Berkeley是完全有内核中断连接, 不返回给我进程9

2、大多数的SVR4实现返回一个错误给服务器进程, 作为accept的返回结果, 错误取决去实现的本身。SVR4中errno返回EPROTO, POSIX指出返回ECONNABORTED(软件引起的连接中断),

3、而非致命错误(系统中断)的时候, 我们可以再次调用accept返回连接套接字。


实验如下:

客户端程序在建立调用connect函数后, 立刻调用设置SO_LINGER套接字选项产生一个RST发送给对端。

而服务器程序在socket bind listen后, 睡眠睡眠一段时间, 然后调用accept函数, 来模拟accept函数连接前中断。


运行方案:在当服务器在sleep,睡眠5s期间调用客户端程序, 然后5s后服务器处理完连接后进入一下循环, 继续睡眠5s, 此时调用ctri +C退出程序

服务器程序部分核心代码

for(;;){
    int confd;
    len = sizeof(cliaddr);
    sleep(5);
    fprintf(stdout, "accept...\n");
    confd = accept(sockfd, (struct sockaddr *) &cliaddr, &len);
    fprintf(stderr, "%s\n", strerror(errno));
    int n = read(confd, recvbuf, MAXLINE);
    fprintf(stderr, "%s\n", strerror(errno));
    close(confd);
}
客户端程序部分核心代码

Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    lger.l_onoff = 1;
    lger.l_linger = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lger, sizeof(lger));
    close(sockfd);

运行结果如下:

TCP异常处理(accept返回前连接中止)与SO_LINGER选项_第2张图片
通过打印错误可以:程序accept照样接收套接字, 没有遵循Berkeley的处理原则, 而是SVR4 处理(根据操作系统不同, 内核处理方式不同)。



抓包结果如下:

TCP异常处理(accept返回前连接中止)与SO_LINGER选项_第3张图片

二、SO_LINGER选项分析

SO_LINGER选项用以下数据结构来改变:

struct linger{
	int l_onoff;    // 0 = off,  nozero = on
	int l_linger;   // linger time
};

1、l_onoff = 0, 表示该选项关闭, l_linger值将会被忽略。close调用后, 将会立刻返回给调用则如下图1所示

2、设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是通常的四分组终止序列,这避免了TIME_WAIT状态

3、l_onoff  = nozero ,l_linger = nozero的时候, 该套接字关闭时内核将拖延一段时间(由l_linger觉得)。调用close函数,当套接字缓冲区还有数据的话, 进程将会进入睡眠状态, 内核将会继续发送数据直到所有的数据都被对方确认完全接受。但是如果, l_linger时间到达, 而数据还没有完全发送完毕, 那么剩下的缓冲区的数据将会被摒弃,close将会返回EWOULDBLOCK错误。若数据发送完毕, 那么将会告诉我们数据和FIN都已经被对方确认。如果套接字在非堵塞的情况以上的假设不存在

TCP异常处理(accept返回前连接中止)与SO_LINGER选项_第4张图片

三、总结

1、了解accept异常中断和rst

2、熟悉SO_LINGER在堵塞情况的应用和在非堵塞情况下的区别

3、SO_LINGER选项中中close的工作原理



你可能感兴趣的:(TCP异常处理(accept返回前连接中止)与SO_LINGER选项)