服务器后台TCP连接存活问题

0. 背景

  公司的服务器后台部署在某一个地方,接入的是用户的APP,而该地方的网络信号较差,导致了服务器后台在运行一段时间后用户无法接入,那边的同事反馈使用netstat查看系统,存在较多的TCP连接。

1. 问题分析

  首先在公司内部测试服务器上部署,使用LoadRunner做压力测试,能正常运行,然后那边的同事反馈该地方信号较差。考虑到接入的问题,有可能接入进程的FD资源耗尽,导致accept失败。推论的依据是对于TCP连接来说,如果客户端那边由于一些异常情况导致断网而未能向服务器发起FIN关闭消息,服务端这边若没有设置存活检测的话,该连接会存在(存活时间暂未测)。

2. 实验测试

  这里简单地写了一个服务端的程序,主要功能是回应,即接受一个报文(格式:2Byte报文长度+报文内容),然后原封不动将报文内容发回客户端。

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <sys/epoll.h>
  5 #include <unistd.h>
  6 #include <pthread.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9 #include <arpa/inet.h>
 10
 11 int g_epfd;
 12
 13 int InitServer( unsigned short port )
 14 {
 15     int nServerFd = socket( AF_INET, SOCK_STREAM, 0 );
 16
 17     struct sockaddr_in addr;
 18     memset( &addr, 0, sizeof(addr) );
 19
 20     addr.sin_family = AF_INET;
 21     addr.sin_port = htons( port );
 22     addr.sin_addr.s_addr = 0;
 23
 24     if ( bind( nServerFd, (struct sockaddr *)&addr, sizeof(addr) ) <0 )
 25     {
 26         printf("bind error\n");
 27         exit(-1);
 28     }
 29
 30     if ( listen( nServerFd, 128 ) < 0 )
 31     {
 32         printf("listen error\n");
 33         exit(-1);
 34     }
 35
 36     return nServerFd;
 37 }
 38
 39 int AddFd( int epfd, int nFd , int nOneShot)
 40 {
 41     struct epoll_event event;
 42     memset( &event, 0, sizeof( event) );
 43
 44     event.data.fd = nFd;
 45     event.events |= EPOLLIN | EPOLLRDHUP | EPOLLET;
 46
 47     if ( nOneShot ) event.events |= EPOLLONESHOT;
 48
 49     return epoll_ctl( epfd, EPOLL_CTL_ADD, nFd, &event );
 50 }
 51
 52 int ResetOneShot( int epfd, int nFd )
 53 {
 54     struct epoll_event event;
 55     memset( &event, 0, sizeof(event) );
 56
 57     event.data.fd = nFd;
 58     event.events |= EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
 59
 60     return epoll_ctl( epfd, EPOLL_CTL_MOD, nFd, &event);
 61 }
 62
 63 void * ReadFromClient( void * arg )
 64 {
 65     int nClientFd = (int)arg;
 66     unsigned char buf[1024];
 67     const int nBufSize = sizeof( buf );
 68     int nRead;
 69     int nTotal;
 70     int nDataLen;
 71
 72     printf("ReadFromClient Enter\n");
 73
 74     if ( (nRead = read( nClientFd, buf, 2 )) != 2 )
 75     {
 76         printf("Read Data Len error\n");
 77         pthread_exit(NULL);
 78     }
 79
 80     nDataLen = *(unsigned short *)buf;
 81     printf("nDataLen [%d]\n", nDataLen);
 82     nDataLen = buf[0]*256 + buf[1];
 83     printf("nDataLen [%d]\n", nDataLen);
 84
 85     nRead = 0;
 86     nTotal = 0;
 87     while( 1 )
 88     {
 89         nRead = read( nClientFd, buf + nRead, nBufSize );
 90         if ( nRead < 0 )
 91         {
 92             printf("Read Data error\n");
 93             pthread_exit( NULL );
 94         }
 95         nTotal += nRead;
 96         if ( nTotal >= nDataLen )
 97         {
 98             break;
 99         }
100     }
101     printf("nTotal [%d]\n", nTotal);
102
103     sleep(5);
104
105     int nWrite = write( nClientFd, buf, nTotal );
106     printf("nWrite[%d]\n", nWrite);
107
108     printf("Not Write ResetOneShot [%d]\n", ResetOneShot(g_epfd, nClientFd));
109
110     return NULL;
111 }
112
113 int main(int argc, char const *argv[])
114 {
115     int i;
116     int nClientFd;
117     pthread_t tid;
118     struct epoll_event events[1024];
119
120     int nServerFd = InitServer( 7777 );
121     if ( nServerFd < 0 )
122     {
123         perror( "nServerFd" );
124         exit(-1);
125     }
126
127     int epfd = epoll_create( 1024 );
128
129     g_epfd = epfd;
130
131     int nReadyNums;
132
133     if ( AddFd( epfd, nServerFd, 0 ) < 0 )
134     {
135         printf("AddFd error\n");
136         exit(-1);
137     }
138
139     while( 1 )
140     {
141          nReadyNums = epoll_wait( epfd, events, 1024, -1 );
142
143          if ( nReadyNums < 0 )
144          {
145              printf("epoll_wait error\n");
146              exit(-1);
147          }
148
149          for ( i = 0; i <  nReadyNums; ++i)
150          {
151              if ( events[i].data.fd == nServerFd )
152              {
153                  nClientFd = accept( nServerFd, NULL, NULL );
154
155                  AddFd( epfd, nClientFd, 1 );
156
157              }else if ( events[i].events & EPOLLIN )
158              {
159                 // Can be implemented by threadpool
160                  //Read data from client
161                 pthread_create( &tid, NULL, ReadFromClient, (void *)(events[i].data.fd) );
162
163              }else if ( events[i].events & EPOLLRDHUP )
164              {
165                  //Close By Peer
166                 printf("Close By Peer\n");
167                 close( events[i].data.fd );
168              }else
169              {
170                 printf("Some thing happened\n");
171              }
172
173          }
174     }
175
176     return 0;
177 }

测试内容:

注:客户端IP: 192.168.10.108  服务器IP&Port: 192.168.10.110:7777

a. 客户端发送一个报文至服务端,然后断网。(这里对程序做了点改动,这次实验注释了write响应,防止write影响测试,后面一个实验会使用write)。

   客户端断网后,使用netstat查看网络连接状态发送客户端与服务端还处于established状态,如图所示。

a. 实验结果

  服务端没有检测到客户端断网,依然处于连接状态。

b. 客户端发送一个报文至服务端,然后断网,关闭客户端,再重复一次。

  这次试验测试重新联网,程序再次建立Socket连接是否会导致之前的连接被检测到。

b. 实验结论:

  重新联网,程序再次建立Socket连接之前的连接不会被检测到。

c. 客户端发送一个报文至服务端,然后断网。(这次实验使用了write响应,查看write后的结果)。

  这里查看到Write居然成功了,成功了....。

c. 实验结论:

  这次使用write不会检测对端是否已经断了。

3. 解决方案

  临时:使用TCP的选项SO_KEEPALIVE检测客户端是否已异常掉了(setsockopt)。

  后续改进:使用心跳包来检测长连接存活问题。

注:SO_KEEPALIVE明天再补充,回家了,只有一台笔记本直接装了Ubuntu,没装虚拟机,伤不起。

4. 补充

  如果什么不对的或者建议直接说,多讨论讨论比较好。

时间: 2024-10-10 02:23:33

服务器后台TCP连接存活问题的相关文章

server后台TCP连接存活问题

公司的server后台部署在某一个地方,接入的是用户的APP,而该地方的网络信号较差,导致了server后台在执行一段时间后用户无法接入,那边的同事反馈使用netstat查看系统.存在较多的TCP连接. 1. 问题分析 首先在公司内部測试server上部署,使用LoadRunner做压力測试,能正常执行,然后那边的同事反馈该地方信号较差.考虑到接入的问题.有可能接入进程的FD资源耗尽,导致accept失败.推论的根据是对于TCP连接来说,假设client那边因为一些异常情况导致断网而未能向ser

单台服务器最大tcp连接

如果对服务器进行压力测试,常常出现这种情况 tcp连接数过多 netstat   -an    windows查看tcp连接数 那么怎么增加单台服务器的最大连接数呢?       最简单的办法,增加内存,socket连接大概10多kb一个 详情   https://www.cnblogs.com/duanxz/p/4464178.html 原文地址:https://www.cnblogs.com/a1304908180/p/10606551.html

用python模拟TCP 服务器跟 TCP连接

import socket target_host = "127.0.0.1" target_port = 9999 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect((target_host,target_port)) client.send("GET / HTTP/1.1\r\nHost:baidu.com\r\n\r\n") response = client.re

(二十七)TCP和UDP,TCP连接和断开服务器

一.TCP和UDP的区别 TCP(Transmission Control Protocol)可靠的.面向连接的协议(eg:打电话).传输效率低全双工通信(发送缓存&接收缓存).面向字节流.使用TCP的应用:Web浏览器:文件传输程序. UDP(User Datagram Protocol)不可靠的.无连接的服务,传输效率高(发送前时延小),一对一.一对多.多对一.多对多.面向报文(数据包),尽最大努力服务,无拥塞控制.使用UDP的应用:域名系统 (DNS):视频流:IP语音(VoIP). 通过

关于系统TCP连接的过多问题。

在日常的运维工程中经常遇到一些服务器的TCP连接过高,头疼不已.针对一些日常出现的问题,做一些总结,希望能抛砖引玉. 现在网站大部分都使用nginx和tomcat,利用nginx高效的使用系统资源以及tomcat的处理事物高效来特点完成日常的网站运作.尝尝采用前端nginx进行转发后端tomcat进行处理的方式. 很多时候会发现一些服务器本身的tcp过高,有些甚至过万.客户端那边反应请求过慢或者超时,服务器端显示调用接口超时等一些问题. 其中有一种情况如下: $ netstat  -antp 

TCP连接

#xiaodeng #TCP连接 #HTTP权威指南 83 #HTTP连接时HTTP报文传输的关键通道.编写http应用程序的程序员需要理解http连接的来龙去脉及如何使用这些连接. #TCP连接: #世界上几乎所有的http通信都是tcp/ip承载.客户端应用横须可以打开一条tcp/ip连接,连接到可能运行在世界任何地方的服务器应用程序.一旦连接建立起来,在客户端和服务器的计算机之间交换的报文就不会丢失. #如:http://www.joes-hardware.com:80/power-too

对TCP连接被重置解决方案的探究

分类: 网络与安全 对TCP连接被重置解决方案的探究——跨过GFW通向自由网络的可行途径 2010年05月25日 星期二 上午 00:19 这个标题有点长——其实开始只想写破折号之前的部分,因为这种技术文章说的隐晦一点没有坏处,但又担心大家不明白是怎么回事,硬着头皮还是补上了后面的部分. 中 国的网络环境很复杂,同时中国也是对互联网高度控制的国家之一,当然仅限于大陆.而控制中国网民自由上网的网络海关正是大名鼎鼎的GFW(Great Fire Wall,长城防火墙),GFW的工作原理就是重置TCP

TCP连接中time_wait在开发中的影响-搜人以鱼不如授之以渔

根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的

[数据库]服务器监听连接不上

标题 是大部分 都能百度出 解决方案的,但是 就是那么的不巧 百度出的任何方案都不能觉得问题.... 事情发生在上周五的晚上,魔都的天气冻死个人,事情也出现的让人心哇凉哇凉的..... 首先,部里的人 说我们没传数据过去,本来是抵着万般不情愿的心情的,结果肿是那么不近人意,还真是我们的数据库问题啊, TNS:无监听 第一想法:监听程序异常了? 查看了监听及相关配置三大文件,etc文件 都很乖的,很正常的 监听程序也启动着呢, 本地sqlplus也连接正常啊,怎么就jdbc连接不成功呢? 第二想法