100分难题:为何我的socket 在本机上 send 到 recieve 的时候有 4 到 20 ms 的延时?

metero 2008-12-05 02:51:09
分难题:为何我的socket 在send 到 recieve 的时候有 4 到 20 ms 的延时?

我的测试程序如下:

1、基于 winsocek 的 MFC dialog 程序,VC++ 6.0,在本机上测试运行
两个程序,一个作为 server,一个作为 client

2、Dialog 在 OnTimer 中有一个定时事件,每秒钟执行 50 次,每次占用CPU时间为 10ms
在定时事件中,用了一个while loop 独占时间(实际工作的时候也是独占时间)

3、在发送的时候获得 CPU 时间,在 recieve 的时候再获得CPU时间,两者相减为传送时间
计算并显示差值。



已经做过的几个测试如下:

1、如果将 ontimer 中的 delay 去掉,则 send -> recieve 仅需要 200 us

2、如果更改为 Sleep(10) 则时间不稳定,在400 us 到 2 ms 之间

3、用接收线程来做 send -> recieve 延时仍然一样

4、将 CSocket 更换为 CAsyncSocket 也一样


实在没有辙了,大家帮我想想办法吧
...全文
382 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
jyh_baoding 2008-12-18
  • 打赏
  • 举报
回复
可能没有问题啊,用系统的方法测试一下你的网络
sigh02 2008-12-18
  • 打赏
  • 举报
回复
你不就想send以后,那边立刻recv到么?
recv放到一个线程里先启动,立刻阻塞调用recv
send可以放到主线程里,一点按钮就执行
两边都用QueryPerformanceCounter存一个数,然后算一下时间看看
雪狼__ 2008-12-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 jimoguilai 的回复:]
Sleep(10)本来就不是非常准,因为sleep就等于放弃cpu的执行权,当它醒来后,并不一定能力克获得cpu的执行全,还需要判断cpu是否空闲,以及其他程序的优先级是否比他高的问题
如果你需要精确的延时,可以使用QueryPerformanceCounter+QueryPerformanceFrequency
具体用法网上很多
[/Quote]

这个有道理
fandh 2008-12-18
  • 打赏
  • 举报
回复
你最好查查机器在干什么了,还有,不行的换一台机器试试
metero 2008-12-17
  • 打赏
  • 举报
回复
再来顶一下。

各位能否提示一下有没有遇到同类的 sample 程序借鉴一下?

我现在虽然可以暂时用上面的方法缓解一下困难,但这仍然不是最好的解决方案。
metero 2008-12-13
  • 打赏
  • 举报
回复
楼上的方法我也试了一下,仍然是同样一个效果,也许这个问题无法进行处理了?

不过,我现在改进了一点,就是将 server 与 client 进行了时钟校正,这样尽可能让 server 在接收到信息之后,
可以知道已经延时了多少,然后根据延时进行处理。
现在考虑将主进程中的延时放到另一个进程中去处理,保证在接收的时候能够实时收到消息
zhyuanshan 2008-12-11
  • 打赏
  • 举报
回复
主线程的优先级也许比较高,可以试试设置线程优先级,或者让处理网络的代码在主线程执行,其他线程干堵塞的事
metero 2008-12-10
  • 打赏
  • 举报
回复
谢谢 zhyuanshan 的回复。
我到现在为止还是没有找到解决网络接收端立即响应的方法。


我前面已经测试过,如果将 ontimer 中的 delay 去掉,网络接收端会立即响应接收到消息。
如果加上就不行了,也许真的是网络消息被堵塞住了。

可问题就是我必须要在这样可能堵塞消息的程序中,让网络数据可以立即响应,就像中断一样,
除非上一个网络数据未处理完成,它的优先级应该很高的,但现在看来好像很低,任意一个任务
就可以让网络传送被堵塞:(

zhyuanshan 2008-12-09
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 metero 的回复:]
引用 23 楼 zhyuanshan 的回复:
楼主的模型不对,你的Timer肯定会影响你的测试啊,Timer延时的时候主线程都不执行了,recv又怎么执行?


嗯,有道理,的确有可能是被堵塞住了。
是否其它的网络程序都对堵塞不是很敏感呢?比如我们玩的网络游戏?
[/Quote]

那些游戏都是经过好几年的打造的,效率高不奇怪,而且我们玩游戏的时候有100ms的延迟也不影响啊
zhyuanshan 2008-12-09
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 metero 的回复:]
如果是接收消息被堵塞,那么我将 socket 的 recieve 消息响应函数去掉,然后用一个接收线程不断进行接收。
程序代码如下所示,是否可以解决这个问题呢?

但是,我运行测试程序以后,线程接收仍然会有 4 -10 ms 延时,没有任何改善。
大家能否对这个问题提出几点解决的办法吗?
我目的是想让接收端不管在做什么,只要有网络数据送到就要立即接收响应(不知道有无像中断一样的功能可以使用呢?)


UINT NetServerRecv(LP…
[/Quote]

楼主首先要排除所有不利条件,Timer去掉、不用Debug的运行,用Release的……,其次在一个机器上用线程不一定就效率高,因为CPU个数不变,楼主慢慢感觉吧,很多因素会影响结果
zhyuanshan 2008-12-08
  • 打赏
  • 举报
回复
楼主的模型不对,你的Timer肯定会影响你的测试啊,Timer延时的时候主线程都不执行了,recv又怎么执行?
metero 2008-12-08
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 zhyuanshan 的回复:]
楼主的模型不对,你的Timer肯定会影响你的测试啊,Timer延时的时候主线程都不执行了,recv又怎么执行?
[/Quote]

嗯,有道理,的确有可能是被堵塞住了。
是否其它的网络程序都对堵塞不是很敏感呢?比如我们玩的网络游戏?
metero 2008-12-08
  • 打赏
  • 举报
回复
如果是接收消息被堵塞,那么我将 socket 的 recieve 消息响应函数去掉,然后用一个接收线程不断进行接收。
程序代码如下所示,是否可以解决这个问题呢?

但是,我运行测试程序以后,线程接收仍然会有 4 -10 ms 延时,没有任何改善。
大家能否对这个问题提出几点解决的办法吗?
我目的是想让接收端不管在做什么,只要有网络数据送到就要立即接收响应(不知道有无像中断一样的功能可以使用呢?)




UINT NetServerRecv(LPVOID pParam )
{
unsigned char buf[1024];
while (1)
{
int rtn = recv( *g_SvSocket->pListenSocket, (char*)buf, 1024, 0 );

if (rtn == 1024)
{
TRACE("Server reveived ok ... %d \n",rtn);

nt_sv_recv1=GetCPUTicks();
double tm1=nt_sv_recv1-nt_cl_send1;

TRACE("nt_sv_recv1 : %f \n",nt_sv_recv1);
TRACE("nt_cl_send1 : %f \n",nt_cl_send1);

TRACE("Server reveived time diff : %f -us: %f \n",tm1,tm1/1.8/1000);
}
}

return(0);
}

cnzdgs 2008-12-06
  • 打赏
  • 举报
回复
再简单描述一下你计时的过程。
fengjl026 2008-12-06
  • 打赏
  • 举报
回复
个人感觉你的实现的模型有问题,对于windows来说,你send 跟 recive的执行在不同的进程中,这注定是要发生10ms的切换时间的误差的
metero 2008-12-06
  • 打赏
  • 举报
回复
首先谢谢各位的回复。

不过,现在大家将问题的重心放到讨论 timer 的精确性上去了,这样偏离了问题的重心。
我再次让我的问题说得更清晰一些:

1、我现在需要的精确时间是在 net client 发送到 net server 这一个时间,也就是说,我想在 net client 发送一个数据包之后(1K大小),然后 net server 可以在 1 ms(1 毫秒)之内就可以收到这个数据包,至于 timer 是什么样精度我根本不关心,而且它也的确是误差非常大

2、我现在的测试在本机上做的,理论是可以在 1ms 以内收到,但我当在 dialog 的 ontimer 中加入一个需要耗费 10 ms 以上的任务之后(这里为了简单,我用了一个 delay 来完成,由于 Sleep 不准确,我改用了一个 for loop 进行堵塞延时,而且也是用 RDTSC 获得CPU 时间来做的,精度在 us 级上,所以不用怀疑这里的延时误差),但当我在 OnTimer 中加入这个延时之后,我的 net server 在接收 net client 发送数据时,在这之间有了一个 4 到 15 ms 左右的延时,
但在我现在的系统中不能有这样的延时,我要求在 net client 一发送数据,net server 就能够在 1ms 内接收。
所以我的问题就是:这中间的延时从何而来?又有什么样的方法可以去掉?

3、大家可能会说,这是 windows 任务切换时的延时,但我认为这样不正确,一定有其他的原因,因为在 socket 接收与发送上实际是 windows 内核用线程来做的, 所以理论是应该没有延时才对。
我现在在 dialog (主线程)中的 ontimer 加了一个占用一定CPU时间的任务,这时会出现网络延时(数据堵塞还是任务被堵塞?),这到底是什么原因呢?
我现在在 OnTimer 中有一个刷新显示的功能,这个不能去掉,大约每次占用时间为 10 ms,又有什么样的方法可以放到其它地方来实现呢?
drenix 2008-12-06
  • 打赏
  • 举报
回复
直接用send recv试试,别用MFC的类
cnzdgs 2008-12-06
  • 打赏
  • 举报
回复
CAsyncSocket的OnReceive函数是通过消息来执行的,如果程序不去取消息是不会执行的,因为OnTimer中加入了延时,所以要等延时过后,程序返回消息循环后,OnReceive才回被调用。
另外,使用软延时的方式会占用大量CPU时间,导致线程轮寻的周期变长。
metero 2008-12-06
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 cnzdgs 的回复:]
再简单描述一下你计时的过程。
[/Quote]

你好,我在第 7 楼中已经简单写出来了,你可以再看一下。
我这里再补充一下:

1、当网络连接OK以后,点击发送至 server 按钮,这时会响应 OnButton1ToSrv
这里会记下 cpu 的时钟计数器的值=nt_cl_send1(以 CPU 频率HZ为单位,非常准确)

2、当 server 的接收到 client 的发送消息时,会进入到 CListenSocket::OnReceive(int nErrorCode)
里进行处理,这时再记下 cpu 的时钟计数器的值=nt_sv_recv1

3、用接收的时间 - 发送的时间就等于这个过程使用的时间:
double tm1=nt_sv_recv1-nt_cl_send1;

4、tm1 是以 CPU 频率HZ为单位,比如我是 1.8G CPU,它1秒钟的计数就为: 1.8 000 000 000 HZ
计算为 us 时间为: tm1/1.8/1000 (us)

5、我的延时用 Sleep 与 for loop 均测试过,效果一样。

void CNettestDlg::OnButton1ToSrv()
{
TRACE("client send to server .... \n");
unsigned char buf[1024];

<*********** 这里获得开始发送的时间,并向 server 端发送 1K 字节的数据
注意:这里的程序在本机下运行,分别在两个VC IDE 中执行

GetCPUTicks(); 是以CPU频率获得计时数值,非常准确,理论是可以达到 us 级的精度,大家不用怀疑我这里的计时器是否准确了。



nt_cl_send1=GetCPUTicks();
g_CSocket->Send(buf,1024,0);

TRACE("nt_cl_send1 : %f \n",nt_cl_send1);
}


void CListenSocket::OnReceive(int nErrorCode)
{


<*********** 这里获得 server 响应接收的时间,理论上在本机上执行,
应该是一发送就可以接收到,如果我去掉 OnTimer 的 delay,这里的时间间隔为 500 微秒以下
但如果打开 delay,则有时需要长达 15 ms 的时间,由于我的程序是一个精确数据采集的程序,
依据网络端口的数据进行计时,要求在 1 个 ms 级的精度上,但我发现如果在程序中有一个
地方在显示数据流的话,就会发生数据接收时的堵塞。




nt_sv_recv1=GetCPUTicks();

double tm1=nt_sv_recv1-nt_cl_send1;

TRACE("nt_sv_recv1 : %f \n",nt_sv_recv1);
TRACE("nt_cl_send1 : %f \n",nt_cl_send1);

TRACE("Server reveived time diff : %f -us: %f \n",tm1,tm1/1.8/1000);

unsigned char buf[1024];
int m_nLength = Receive((void*)buf, 1024,0);

TRACE("Server reveived ok ... %d \n",m_nLength);
CAsyncSocket::OnReceive(nErrorCode);
}

metero 2008-12-06
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 fengjl026 的回复:]
个人感觉你的实现的模型有问题,对于windows来说,你send 跟 recive的执行在不同的进程中,这注定是要发生10ms的切换时间的误差的
[/Quote]



这应该没有,我如果将 OnTimer 中的 delay(10) 去掉,这时 send 到 recieve 仅有 200 us(微秒)的延时,几乎可以认为是发送后立即接收到。


我在几种情况下运行过 server 与 client
1、本机上以两个程序分别运行
无 delay,则间隔在 200 us 之间,加入 delay(10) 则有4-15ms 的不固定延时

2、在本机上同一个程序中运行,它既运行 server 也运行 client
有无delay时间间隔均在200 us 之内(不明白这是为什么,当然,这个方法根本不能被我使用,不用考虑,只是奇怪为什么会这样)

3、在以路由器+ 10米 网线+100MB网卡
无 delay,则间隔在 500 us 以下(稍微长了一点),加入 delay(10) 则有4-15ms 的不固定延时

所以应该是 socket 哪里有问题,但我现在实在找不出
加载更多回复(15)

18,357

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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