我的编程空间,编程开发者的网络收藏夹
学习永远不晚

socket连接关闭问题分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

socket连接关闭问题分析

socket编程过程中往往会遇到这样那样的问题,出现了这些问题,有的是由于并发访问量太大造成的,有些却是由于代码中编程不慎造成的。比如说,最常见的错误就是程序中报打开的文件数过多这个错误。socket建立连接的时候是三次握手,这个大家都很清楚,但是socket关闭连接的时候,需要进行四次挥手,但很多人对于这四次挥手的具体流程不清楚,吃了很多亏。

CLOSE_WAIT分析

socket是一种全双工的通信方式,建立完socket连接后,连接的任何一方都可以发起关闭操作。这里不妨假设连接的关闭是客户端发起。客户端的代码如下:

代码片段1.1

ret = CS_GetConnect(&client,ipAddr,9010);
if (ret == 0) {
    printf("connected success.");
}
CloseSocket(client);

基本逻辑就是,连接建立后立即关闭。其中CloseSocket函数是自定义函数,仅仅封装了在windows和linux下关闭socket的不同实现而已

代码片段1.2

#if defined(WIN32) || defined(WIN64)
#define CloseSocket(fd) do{ closesocket(fd); }while(0)
#else
#define CloseSocket(fd) do{ close(fd);  }while(0)
#endif

客户端调用了CloseSocket之后,发送FIN信号到服务器端,告诉socket程序,连接已经断开。服务器端接收到FIN信号后,会将自身的TCP状态置为`CLOSE_WAIT`,同时回复 一个ACK信号给客户端,客户端接收到这个ACK信号后,自身将处于`FIN_WAIT_2`状态。

但是tcp是全双工的通信协议,虽然客户端关闭了连接,但是服务器端对于这个关闭动作不予理睬怎么办。对于服务器端来说,这是个不幸的消息,因为它将一直处于`CLOSE_WAIT`状态,虽然客户端已经不需要和服务器间进行通信了,但是服务器端的socket连接句柄一直得不到释放;如果老是有这种情况出现,久而久之服务器端的连接句柄就会被耗尽。对于发起关闭的客户端来说,他处于`FIN_WAIT_2`状态,如果出现服务器端一直处于`CLOSE_WATI`状态的情况,客户端并不会一直处在`FIN_WAIT_2`状态,因为这个状态有一个超时时间,这个值可以在/etc/sysctl.conf中进行配置。在这个文件中配置`net.ipv4.tcp_fin_timeout=30`即可保证`FIN_WAIT_2`状态最多保持30秒,超过这个时间后就进入TIME_WAIT状态(下面要讲到这个状态)。

注意:这里socket的关闭从客户端发起,仅仅是为了举例说明,socket的关闭完全也可以从服务器端发起。比如说你写了一个爬虫程序去下载互联网上的某些web服务器上的资源的时候,某些要下载的web资源不存在,web服务器会立即关闭当前的socket连接,但是你的爬虫程序不够健壮,对于这种情况没有做处理,同样会使你的爬虫客户端处于CLOSE_WAIT状态。

那么怎样预防SOCKET处于CLOSE_WATI状态呢,答案在这里:

代码片段1.3

    while(true) {
        memset(getBuffer,0,MY_SOCKET_BUFFER_SIZE);
        Ret = recv(client, getBuffer, MY_SOCKET_BUFFER_SIZE, 0);
        if ( Ret == 0 || Ret == SOCKET_ERROR ) 
        {
            printf("对方socket已经退出,Ret【%d】!\n",Ret);
            Ret = SOCKET_READE_ERROR;//接收服务器端信息失败
            break;
        }
    }
clear:
    if (getBuffer != NULL) {
        free(getBuffer);
        getBuffer = NULL;
    }
    closesocket(client);

这里摘录了服务器端部分代码,注意这个recv函数,这个函数在连接建立时,会堵塞住当前代码,等有数据接收成功后才返回,返回值为接收到的字节数;但是对于连接对方socket关闭情况,它能立即感应到,并且返回0.所以对于返回0的时候,可以跳出循环,结束当前socket处理,进行一些垃圾回收工作,注意最后一句closesocket操作是很重要的,假设没有写这句话,服务器端会一直处于CLOSE_WAIT状态。如果写了这句话,那么socket的流程就会是这样的:

TIME_WAIT分析

服务器端调用了CloseSocket操作后,会发送一个FIN信号给客户端,客户端进入`TIME_WAIT`状态,而且将维持在这个状态一段时间,这个时间也被成为2MSL(MSL是maximum segment lifetime的缩写,意指最大分节生命周期,这是IP数据包能在互联网上生存的最长时间,超过这个时间将在互联网上消失),在这个时间段内如果客户端的发出的数据还没有被服务器端确认接收的话,可以趁这个时间等待服务端的确认消息。注意,客户端最后发出的ACK N+1消息,是一进入`TIME_WAIT`状态后就发出的,并不是在`TIME_WAIT`状态结束后发出的。如果在发送ACK N+1的时候,由于某种原因服务器端没有收到,那么服务器端会重新发送FIN N消息,这个时候如果客户端还处于`TIME_WAIT`状态的,会重新发送ACK N+1消息,否则客户端会直接发送一个RST消息,告诉服务器端socket连接已经不存在了。

有时,我们在使用netstat命令查看web服务器端的tcp状态的时候,会发现有成千上万的连接句柄处在`TIME_WAIT`状态。web服务器的socket连接一般都是服务器端主动关闭的,当web服务器的并发访问量过大的时候,由于web服务器大多情况下是短连接,socket句柄的生命周期比较短,于是乎就出现了大量的句柄堵在`TIME_WAIT`状态,等待系统回收的情况。如果这种情况太过频繁,又由于操作系统本身的连接数就有限,势必会影响正常的socket连接的建立。在linux下对于这种情况倒是有解救措施,方法就是修改/etc/sysctl.conf文件,保证里面含有以下三行配置:

配置型 2.1

    #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭  
    net.ipv4.tcp_tw_reuse = 1  
    #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭  
    net.ipv4.tcp_tw_recycle = 1  
    #表示系统同时保持TIME_WAIT的最大数量,如果超过这个数字,
    #TIME_WAIT将立刻被清除并打印警告信息。默认为180000,改为5000。
    net.ipv4.tcp_max_tw_buckets = 5000

关于重用`TIME_WAIT`状态的句柄的操作,也可以在代码中设置:

代码片段2.1

int on = 1;
if (setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on)))
{
    return ERROR_SET_REUSE_ADDR;
}

如果在代码中设置了关于重用的操作,程序中将使用代码中设置的选项决定重用或者不重用,/etc/sysctl.conf中`net.ipv4.tcp_tw_reuse`中的设置将不再其作用。

当然这样设置是有悖TCP的设计标准的,因为处于`TIME_WAIT`状态的TCP连接,是有其存在的积极作用的,前面已经介绍过。假设客户端的ACK N+1信号发送失败,服务器端在1MSL时间过后会重发FIN N信号,而此时客户端重用了之前关闭的连接句柄建立了新的连接,但是此时就会收到一个FIN信号,导致自己被莫名其妙关闭。

一般`TIME_WAIT`会维持在2MSL(linux下1MSL默认为30秒)时间,但是这个时间可以通过代码修改:

代码片段2.2

struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 10;
if (setsockopt(socketfd,SOL_SOCKET,SO_LINGER,(char *)&so_linger,sizeof(struct linger)))
{
    return ERROR_SET_LINGER;
}

这里代码将`TIME_WAIT`的时间设置为10秒(在BSD系统中,将会是0.01*10s)。TCP中的`TIME_WAIT`机制使得socket程序可以“优雅”的关闭,如果你想你的程序更优雅,最好不要设置`TIME_WAIT`的停留时间,让老的tcp数据包在合理的时间内自生自灭。当然对于`SO_LINGER`参数,它不仅仅能够自定义`TIME_WAIT`状态的时间,还能够将TCP的四次挥手直接禁用掉,假设对于so_linger结构体变量的设置是这个样子的:

    so_linger.l_onoff = 1;
    so_linger.l_linger = 0;

如果客户端的socket是这么设置的那么socket的关闭流程就直接是这个样子了:

这相当于客户端直接告诉服务器端,我这边异常终止了,对于我稍后给出的所有数据包你都可以丢弃掉。服务器端如果接受到这种RST消息,会直接把对应的socket句柄回收掉。有一些socket程序不想让TCP出现`TIME_WAIT`状态,会选择直接使用RST方式关闭socket,以保证socket句柄在最短的时间内得到回收,当然前提是接受有可能被丢弃老的数据包这种情况的出现。如果socket通信的前后数据包的关联性不是很强的话,换句话说每次通信都是一个单独的事务,那么可以考虑直接发送RST信号来快速关闭连接。

补充

1.文中提到的修改/etc/sysctl.conf文件的情况,修改完成之后需要运行`/sbin/sysctl -p`后才能生效。

2.图1中发送完FIN M信号后,被动关闭端的socket程序中输入流会接收到一个EOF标示,是在C代码中处理时recv函数返回0代表对方关闭,在java代码中会在InputStream的read函数中接收到-1:

代码片段3.1

Socket client = new Socket();//,9090
    try {
        client.connect(
            new InetSocketAddress("192.168.56.101",9090));
        while(true){                
            int c = client.getInputStream().read();
            if (c > 0) {
                System.out.print((char) c);
            } else {//如果对方socket关闭,read函数返回-1
                break;
            }
             try {
                Thread.currentThread().sleep(2000);                 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    } catch (IOException e2) {
        e2.printStackTrace();
    } finally {
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.如果主动关闭方已经发起了关闭的FIN信号,被动关闭方不予理睬,依然往主动关闭方发送数据,那么主动关闭方会直接返回RST新号,连接双方的句柄就被双方的操作系统回收,如果此时双方的路由节点之前还存在未到达的数据,将会被丢弃掉。

4.通信的过程中,socket双发中有一方的进程意外退出,则这一方将向其对应的另一方发送RST消息,所有双发建立的连接将会被回收,未接收完的消息就会被丢弃。

5.项目的配套代码可以从这里得到http://git.oschina.net/yunnysunny/socket_close

以上就是socket连接关闭问题分析的详细内容,更多关于socket连接关闭的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

socket连接关闭问题分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

socket连接关闭问题的示例分析

这篇文章主要为大家展示了“socket连接关闭问题的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“socket连接关闭问题的示例分析”这篇文章吧。socket编程过程中往往会遇到这样那样
2023-06-29

连接ADO.NET基础类有关问题分析

本篇内容主要讲解“连接ADO.NET基础类有关问题分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“连接ADO.NET基础类有关问题分析”吧!基于连接的对象(Connection-based o
2023-06-17

如何分析Socket TIME_WAIT 问题

本篇文章给大家分享的是有关如何分析Socket TIME_WAIT 问题,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Socket TIME_WAIT 问题tcp/ip详解的卷
2023-06-04

关于Java单个TCP(Socket)连接发送多个文件的问题

这篇文章主要介绍了关于Java单个TCP(Socket)连接发送多个文件的问题,每次我只能使用一个Socket发送一个文件,没有办法做到连续发送文件,本文来解决这个问题,需要的朋友可以参考下
2023-05-15

Redis拒绝连接问题分析与解决方案

目录前言1. 问题描述2. Redis拒绝连接的常见原因分析2.1 Redis服务未启动2.2 Redis配置中的绑定地址问题2.3 防火墙或安全组问题2.4 Redis连接池耗尽2.5 Redis服务器负载过高2.6 权限配置问题3. 深
Redis拒绝连接问题分析与解决方案
2024-10-16

Python相关问题代码分析

这篇文章主要介绍“Python相关问题代码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Python相关问题代码分析”文章能帮助大家解决问题。1、反射算术运算符你可能知道 Python 里面的魔
2023-07-06

ADO.NET构造相关问题分析

这篇文章主要讲解了“ADO.NET构造相关问题分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ADO.NET构造相关问题分析”吧!ADO.NET构造使用强类型DataSet的好处 ADO.
2023-06-17

Java Socket上Read操作阻塞问题的示例分析

这篇文章给大家分享的是有关Java Socket上Read操作阻塞问题的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Socket上的Read操作阻塞问题从Socket上读取对端发过来的数据一般有两种方法
2023-06-25

关于mysql数据库连接编码问题

前几天使用springboot做一个数据库查询功能,发现使用中文就无法查到数据,经过测试SQL语句是没有问题的,但是就是查询不到数据,一直显示为null。后来,我灵机一动尝试了一下查询参数改为英文,显示出查询结果是正常的。这就说明了是编码
2023-04-14

关于dns密码的问题分析

关于dns密码的问题分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。小弟一直以来都在寻找关于DNS配置,无奈很多资料都是过时的,特别关于KEY和RNDC的部分
2023-06-13

Eclipse Debug模式的开启与关闭问题简析

默认情况下,eclipse中右键debug,当运行到设置的断点时会自动跳到debug模式下。但由于我的eclipse环境,从开始一直用到现在,中间包括装、卸各种插件,更换版本,从英文界面导到中文界面又换回来,可以说现在的环境已经臃肿混乱到自
2023-05-31

解析ODBC Oracle连接中的编码问题

在ODBC Oracle连接中,编码问题通常指的是数据在传输过程中的字符集转换问题。Oracle支持多种字符集,包括UTF-8、UTF-16、ISO-8859-1等,而ODBC连接在传输数据时需要根据客户端和服务器端的字符集设置进行字符集的
解析ODBC Oracle连接中的编码问题
2024-07-15

分析Android多主题颜色的相关问题

如果您通过以下的代码来获取定义的颜色值context.getResources().getColor(R.color.some_color_resource_id); 在 Android Studio 中会有一个 lint 警告,提示您 R
2022-06-06

springboot2中HikariCP连接池的相关配置问题

这篇文章主要介绍了springboot2中HikariCP连接池的相关配置问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-12-22

关于python与opc ua Expert endpoint连接的问题

本文介绍了如何使用Python连接到OPCUAExpertEndpoint,包括:Python和OPCUA库安装创建客户端和连接到服务器获取根节点和浏览节点读取数据和订阅数据变更示例代码和注意事项通过Python连接OPCUAExpertEndpoint可实现与符合OPCUA标准设备的交互,适用于自动化、数据采集和工业物联网应用。
关于python与opc ua Expert endpoint连接的问题
2024-04-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录