0. 背景

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

1. 问题分析

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

2. 实验测试

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

 #include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h> int g_epfd; int InitServer( unsigned short port )
{
int nServerFd = socket( AF_INET, SOCK_STREAM, ); struct sockaddr_in addr;
memset( &addr, , sizeof(addr) ); addr.sin_family = AF_INET;
addr.sin_port = htons( port );
addr.sin_addr.s_addr = ; if ( bind( nServerFd, (struct sockaddr *)&addr, sizeof(addr) ) < )
{
printf("bind error\n");
exit(-);
} if ( listen( nServerFd, ) < )
{
printf("listen error\n");
exit(-);
} return nServerFd;
} int AddFd( int epfd, int nFd , int nOneShot)
{
struct epoll_event event;
memset( &event, , sizeof( event) ); event.data.fd = nFd;
event.events |= EPOLLIN | EPOLLRDHUP | EPOLLET; if ( nOneShot ) event.events |= EPOLLONESHOT; return epoll_ctl( epfd, EPOLL_CTL_ADD, nFd, &event );
} int ResetOneShot( int epfd, int nFd )
{
struct epoll_event event;
memset( &event, , sizeof(event) ); event.data.fd = nFd;
event.events |= EPOLLIN | EPOLLRDHUP | EPOLLONESHOT; return epoll_ctl( epfd, EPOLL_CTL_MOD, nFd, &event);
} void * ReadFromClient( void * arg )
{
int nClientFd = (int)arg;
unsigned char buf[];
const int nBufSize = sizeof( buf );
int nRead;
int nTotal;
int nDataLen; printf("ReadFromClient Enter\n"); if ( (nRead = read( nClientFd, buf, )) != )
{
printf("Read Data Len error\n");
pthread_exit(NULL);
} nDataLen = *(unsigned short *)buf;
printf("nDataLen [%d]\n", nDataLen);
nDataLen = buf[]* + buf[];
printf("nDataLen [%d]\n", nDataLen); nRead = ;
nTotal = ;
while( )
{
nRead = read( nClientFd, buf + nRead, nBufSize );
if ( nRead < )
{
printf("Read Data error\n");
pthread_exit( NULL );
}
nTotal += nRead;
if ( nTotal >= nDataLen )
{
break;
}
}
printf("nTotal [%d]\n", nTotal); sleep(); int nWrite = write( nClientFd, buf, nTotal );
printf("nWrite[%d]\n", nWrite); printf("Not Write ResetOneShot [%d]\n", ResetOneShot(g_epfd, nClientFd)); return NULL;
} int main(int argc, char const *argv[])
{
int i;
int nClientFd;
pthread_t tid;
struct epoll_event events[]; int nServerFd = InitServer( );
if ( nServerFd < )
{
perror( "nServerFd" );
exit(-);
} int epfd = epoll_create( ); g_epfd = epfd; int nReadyNums; if ( AddFd( epfd, nServerFd, ) < )
{
printf("AddFd error\n");
exit(-);
} while( )
{
nReadyNums = epoll_wait( epfd, events, , -1 ); if ( nReadyNums < )
{
printf("epoll_wait error\n");
exit(-);
} for ( i = ; i < nReadyNums; ++i)
{
if ( events[i].data.fd == nServerFd )
{
nClientFd = accept( nServerFd, NULL, NULL ); AddFd( epfd, nClientFd, ); }else if ( events[i].events & EPOLLIN )
{
// Can be implemented by threadpool
//Read data from client
pthread_create( &tid, NULL, ReadFromClient, (void *)(events[i].data.fd) ); }else if ( events[i].events & EPOLLRDHUP )
{
//Close By Peer
printf("Close By Peer\n");
close( events[i].data.fd );
}else
{
printf("Some thing happened\n");
} }
} return ;
}

测试内容:

注:客户端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. 补充

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

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

  1. server后台TCP连接存活问题

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

  2. 单台服务器最大tcp连接

    如果对服务器进行压力测试,常常出现这种情况 tcp连接数过多 netstat   -an    windows查看tcp连接数 那么怎么增加单台服务器的最大连接数呢?       最简单的办法,增加内 ...

  3. 【故障公告】推荐系统中转站撑爆服务器 TCP 连接引发的故障

    上周五下午,我们在博客中部署了推荐系统,在博文下方显示“最新IT新闻”的地方显示自动推荐的关联博文.我们用的推荐系统是第四范式的推荐服务,我们自己只是搭建了一个推荐系统中转站(基于 ASP.NET C ...

  4. TCP 连接

    面试题传送 TCP 报文格式 此处介绍建立或者断开TCP连接时,需要了解的TCP报文段首部字段含义: 序列号 seq:占4个字节(32位),用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上 ...

  5. 简述TCP连接的建立与释放(三次握手、四次挥手)

    在介绍TCP连接的建立与释放之前,先回顾一下相关知识. TCP是面向连接的运输层协议,它提供可靠交付的.全双工的.面向字节流的点对点服务.HTTP协议便是基于TCP协议实现的.(虽然作为应用层协议,H ...

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

    根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),T ...

  7. TCP连接的建立与释放(三次握手与四次挥手)

    TCP连接的建立与释放(三次握手与四次挥手) TCP是面向连接的运输层协议,它提供可靠交付的.全双工的.面向字节流的点对点服务.HTTP协议便是基于TCP协议实现的.(虽然作为应用层协议,HTTP协议 ...

  8. 网络学习笔记(一):TCP连接的建立与关闭

      五层网络模型分为:物理层.数据链路层.网络层.传输层.应用层.其中,传输层有两种主要协议:面向连接的TCP(Transmission Control Protocol 传输控制协议).无连接的UD ...

  9. TCP连接与释放

    TCP连接的建立 三次握手 TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态. TCP客户进程也是先创建传输控制块TCB,然后向服务器 ...

随机推荐

  1. [转]session 持久化问题(重启服务器session 仍然存在)

    转:http://xiaolongfeixiang.iteye.com/blog/560800 关于在线人数统计,大都使用SessionListener监听器实现. SessionListener 触 ...

  2. ActiveMQ_安全配置(五)

    如果Activemq不加安全配置,那么任何知道队列所在服务器IP的人都可以发送接收队列消息 安全配置主要是通过配置账号密码增强安全性 参考资料:http://activemq.apache.org/s ...

  3. ES6新特性:Proxy代理器

    ES6新特性:Proxy: 要使用的话, 直接在浏览器中执行即可, node和babel目前还没有Proxy的polyfill;,要使用的话,直接在浏览器中运行就好了, 浏览器的兼容性为:chrome ...

  4. The resource identified by this request is only capable of generating responses with characteristics

    [转]今天在调试springMVC的时候,在将一个对象返回为json串的时候,浏览器中出现异常: The resource identified by this request is only cap ...

  5. java-原生爬虫机制源码

    这是一个web搜索的基本程序,从命令行输入搜索条件(起始的URL.处理url的最大数.要搜索的字符串),它就会逐个对Internet上的URL进行实时搜索,查找并输出匹配搜索条件的页面. 这个程序的原 ...

  6. Js-加密

    1.base64加密 在页面中引入base64.js文件,调用方法为: <!DOCTYPE HTML> <html> <head> <meta charset ...

  7. shell实现两个数的相加

    刚开始的时候写,一直写不对:看似简单的功能,但是一定要小心:函数的定义: funciton functionName {.....}在functionName和{之间一定有空格啊! 我就是没加空格,就 ...

  8. 我所经历的JS性能优化

    转自http://www.cnblogs.com/koking/archive/2011/10/17/2215665.html 折腾了好几天,纠结了好几天,郁闷了好几天,终于在今天可以释怀了,留下其中 ...

  9. 快速提高 Xcode 编译速度的方法(转载自网上一个大神的方法)

    1.,中的 Debug Information Format 的选项中选择 DWARF ,平时调试就是用整个选项,经过测试,速度确实有很大的提升,等发行版本的时候在调回 DWARF with dsYM ...

  10. Linq Like

    Like的操作,有点像in,但是,方向变了.什么意思呢.就是你给定一个字符串,去寻找数据中某个字段包含这个字符串.就是给定的字符串是某字段的子集.Sql Script是这么写的. Selec * fr ...