服务端出现大量的SYN_RECV

bearnedzq 2010-03-16 11:02:43
最近在做性能测试,客户端向服务端发送http请求,短连接。过不了多久,服务端就没有回应了。在服务端上发现链接有很多处于SYN_RECV状态。客户端和服务端都是自己写的,在局域网内做测试,与恶意攻击无关。服务端的基本逻辑是:主进程在一个固定端口侦听,当收到一个请求,就建立一个新进程,由该进程发送回应,然后断开链接,退出子进程。客户端的逻辑是:连上服务端,发送请求,等待回应,收到回应后,关闭链接。一直重复这个过程。客户端是单线程。当客户端做了2万多次这种操作,就发现服务端再也连不上了。查看服务端存在大量的SYN_RECV。请高手给个思路。
...全文
4831 33 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
bearnedzq 2010-03-19
  • 打赏
  • 举报
回复
昨晚程序连续运行近11个小时,一切正常。

此问题讨论到此结束。
JUSTACY 2010-03-18
  • 打赏
  • 举报
回复
应该是服务端的程序存在问题,检查下。
cattycat 2010-03-18
  • 打赏
  • 举报
回复
那有可能是windows xp容易发生丢包,如果丢失SYN_ACK就会一直在SYN_RECN状态,客户端则是SYN_SEND状态。服务器端没有僵死进程,在windows server 2003和linux客户端测试良好,说明windows xp性能可能不好,你在别人的xp上测了没有。
bearnedzq 2010-03-18
  • 打赏
  • 举报
回复
答复楼上:Linux服务端没有僵死进程。

我将Windows客户端从 Windows XP 拷贝到 Windows 2003 Server,迭代10万此,运行良好,没有任何问题。迭代的速度高达每秒 372次。客户端,服务端均没有出现问题。(二进制文件拷贝)

同样的程序,在 Windows XP 上,迭代的速度不到 300,而且 3万左右就会出问题。
在 Windows 2003 Server 上,迭代的速度达 372,而且 10万条没有问题。
运行20万条,在19万多条的时候,出现问题。
在Linux上,写一个简单的程序,也是迭代,速度高达 870多每秒,总是稳定。

似乎可以下结论了:Windows的性能不行
bearnedzq 2010-03-18
  • 打赏
  • 举报
回复
最新测试结果:发送60万条,花了将近50分钟,平均每秒205条,一切正常。

个人认为:这个是关键
socket关闭后,不经历TIME_WAIT状态,逼着Windows立即释放相关资源。
没有改造前,客户端存在大量的TIME_WAIT,这个肯定要占用系统相关资源的,资源占尽,网络就不通了。
cattycat 2010-03-18
  • 打赏
  • 举报
回复
对,有可能是系统的资源用尽了。windows虽然你关闭socket,但清理资源不是立刻就进行的,频繁发送数据,CPU忙于处理发送数据了,系统调度没有时间去释放资源,当用尽后,网络也不通了。
bearnedzq 2010-03-18
  • 打赏
  • 举报
回复
没有在别人的XP上测试过。不过,自己的机器不行,就说明程序不行,换台机器意义不大。
经过一番折腾,有可能是xp上的系统资源被耗尽,导致没有收到服务端的ack。因为程序出现问题后,远程连接ssh都断了,而且ping也不通。

于是对客户端做了一些改造:
1 绑定端口,每次发送都使用固定的端口,调用bind
2 设置socket为端口复用
3 设置socket关闭后,不经历TIME_WAIT状态
4 在迭代中间,调用wait(1),即两次调用之间停顿0.001秒

经过改造,程序发送20万条信息,没有出现问题。在写这个帖子的时候,测试还正在进行中。
目前已经发了60多万条,依旧运行正常

yutaooo 2010-03-17
  • 打赏
  • 举报
回复

还有Fork()函数的实现,贴出来看看。
yutaooo 2010-03-17
  • 打赏
  • 举报
回复

我还是很迷惑,为什么只有这么一点僵死进程? LZ有用wait()系列的函数吗?
bearnedzq 2010-03-17
  • 打赏
  • 举报
回复
一般是发送了5万多条,就发生了这种情况。今天看来是解决不了了。
yutaooo 2010-03-17
  • 打赏
  • 举报
回复

“客户端处于 SYN_SENT 状态”

这个到是与“connect()失败,返回错误码在MSDN中查找,是服务端超时”匹配起来了。

看来是缺少了服务端发送的SYN+ACK分组。要往这个方向查。
bearnedzq 2010-03-17
  • 打赏
  • 举报
回复
服务端一直处于 SYN_RECV, 客户端处于 SYN_SENT 状态。
bearnedzq 2010-03-17
  • 打赏
  • 举报
回复
我又找了个速度很快的服务端,单线程迭代的发消息,能达到每秒800多个。
测试用的客户端是运行在linux下的,客户端和服务端不是在同一台机器上。

但是如果用windows上的客户端测试的话(依然是单线程迭代方式),仍旧会出错。
不过,还是出现 SYN_RECV 。我对客户端稍作改进,迭代的时候,一旦失败,循环停止。
所以服务端只会出现一个 SYN_RECV。Windows的效率比较低,迭代的速度只有200多。

服务端能承受800个请求每秒。服务端速度不存在问题。
SYN_RECV是一个中间状态,正常情况下,存在的时间极短,很难通过命令行看到。
但是,现在SYN_RECV状态持续很久,超过一分钟。
Linux上的客户端迭代没有问题,Windows上迭代就有问题,所以觉得是客户端有问题。

我对socket() connect()返回值都做了判断,connect()失败,返回错误码在MSDN中查找,
是服务端超时。这么看又是服务端的问题。

现在还不知道是哪里有问题。
ypb362148418 2010-03-17
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 mymtom 的回复:]

SYN_RECV 状态就是收到了连接请求,但是还没有accept.

应该是发送速度超过了服务端的处理速度。
要知道fork是很耗资源的。

建议楼主看一下服务器进程数。
[/Quote]

我觉得是这种可能
bearnedzq 2010-03-17
  • 打赏
  • 举报
回复
服务端运行在Tubro Linux下,客户端运行在Windows XP下。
没有大量僵死进程,只有一个或者两个进程。

因为客户端是迭代方式的,就算服务端速度慢,也不至于产生 SYN_RECV 状态啊,这个很不好理解。
而且我是通过 Secure CRT 连接Linux的,在服务端产生了 SYN_RECV 后,Secure CRT 与 Linux断开连接,通过cmd ping linux,ping都ping不通。

我理解 SYN_RECV 这种状态在TCP各种状态变迁中的位置,不明白的是迭代的请求,服务端为什么产生了SYN_RECV ?
mymtom 2010-03-16
  • 打赏
  • 举报
回复
SYN_RECV 状态就是收到了连接请求,但是还没有accept.

应该是发送速度超过了服务端的处理速度。
要知道fork是很耗资源的。

建议楼主看一下服务器进程数。
bearnedzq 2010-03-16
  • 打赏
  • 举报
回复
明明有排版的,一发帖,空格就消失了。
如果控制在每秒10TPS,服务端一点问题都没有。
发送速度加快,服务端就有问题(不到100TPS)。

服务端的代码是 《Unix Network programming》改编的。
具体是:tcpserv04.c
我修改了业务逻辑部分,网络监听,fork等等程序的基本框架没有做修改。

我觉得服务端的性能不应该这么低啊?而且SYN_RECV状态无法理解。
bearnedzq 2010-03-16
  • 打赏
  • 举报
回复
服务端的主进程:
for ( ; ; ) {

if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}

if ( (childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
exit(0);
}

Close(connfd); /* parent closes connected socket */

}

str_echo函数:
//往buf中写数据
read(sockfd, buf, MAXLINE);
Writen(sockfd, rsp, strlen(rsp));
Close(sockfd);

bearnedzq 2010-03-16
  • 打赏
  • 举报
回复
服务端的主进程:
for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}

if ( (childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
exit(0);
}
Close(connfd); /* parent closes connected socket */
printf("process count %u\n", ++count);
}

str_echo函数:
//往buf中写数据
read(sockfd, buf, MAXLINE);
Writen(sockfd, rsp, strlen(rsp));
Close(sockfd);

其实这些代码就是 《Unix Network programming》中的。

yutaooo 2010-03-16
  • 打赏
  • 举报
回复

LZ是用select()来监听的吗? 最好能把监听到fork()这部分代码片段贴出来。不要细节要框架,包括主进程关闭connected socket的这些代码。
加载更多回复(13)

70,019

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧