加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

select实现高精度定时器

(2011-08-04 16:45:03)
标签:

select

timer

分类: windows

select实现高精度定时器

 

此博文禁止转载,谢谢

 


因为各种各样的原因,有时候必须把超时和socket的事件放在一个线程里,也就是说不可避免的要用select做超时功能,
经过测试,select在windows下的timeout功能精度太低,大概在15ms左右,在linux下精度在1ms,我记得几年前就是这种情况,几年过去了,MS还是这个德性,一点长进都没有。

select函数本身限制够多的,比如可查询的handle数,linux下不重新编译内核的话,是1024,但是linux下有没有这个限制,而且性能更高的函数可选择。windows根本就没辙,就这样了,自己想点其他办法。

因为这个原因,几年前毅然选择了linux。

但是现在没辙了,大环境在windows,只能想点歪门邪道了。


在windows下,经测试select超时精度在15ms,timeval.tv_usec设为1,1000,10000,都一样,14ms多超时,说明精度在15ms,但是为0的话,一般10us内超时。

但是我的要求是select的精度在1ms。得想办法来。

select的句柄有事件的话,select的返回挺快的,这是不是有可利用的地方,答案就是这个。

create两个dummy handle与windows的1ms精度的多媒体定时器配合使用,使select超时达到1ms的精度。

zhaohongxian@gmail.com

 

伪代码如下:
1. create ReadDummySocket
2. bind  ReadDummySocket
3. create WriteDummySocket
4. call timeGetDevCaps() get windows multimedia timer resolution
5.  steps in select timeout function
5.1 calculate timeout for select() function;
5.2 call timeSetEvent() function to set a windows multimedia timer.
5.3 add ReadDummySocket to readset fdset, and call select() to query handle's events or timeout
5.4 send a dummy packet to ReadDummySocket through WriteDummySocket in the CALLBACK functin of windows multimedia timer
5.5 select return when ReadDummySocket has a readable event
5.6 call timeKillEvent() to kill the windows multimedia timer id return in step 5.2


parts of .h file
int dummyReadSocket;
int dummyWriteSocket;
unsigned uTimeResolution;
unsigned nTimeId;
int readPort;

parts of .cpp file
extern "C" void CALLBACK TestTimeProc(UINT id, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2 )
{
 NotifyDummyReadSocket();
}


void InitMMTimer()
{
 dummyReadSocket = socket(AF_INET, SOCK_DGRAM, 0);
 dummyWriteSocket = socket(AF_INET, SOCK_DGRAM, 0);

 struct sockaddr_in readAddr;
 readAddr.sin_family = AF_INET;
 readAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 readPort = -1;

 for( int i=31313; i<35000; i++)
 {
  readAddr.sin_port = htons(i);

  if( 0 == bind(dummyReadSocket, (struct sockaddr*)&readAddr, sizeof(readAddr)) )
  {
   readPort = i;
   break;
  }
 }

 FD_SET((unsigned)dummyReadSocket, &fReadSet);

 TIMECAPS tc;
 if( TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(tc)) )
 {
  uTimeResolution = min(max(tc.wPeriodMin,1), tc.wPeriodMax);
  timeBeginPeriod(uTimeResolution);
 }
}


void SetMMTimer(UINT microSecond)
{
 UINT milliSeccond = microSecond / 1000;

 if( milliSeccond == 0 )
 {
  return;
 }

 if( milliSeccond < uTimeResolution )
 {
  milliSeccond = uTimeResolution;
 }

 if( milliSeccond > 320 )
 {
  milliSeccond = 320;
 }

 nTimeId = timeSetEvent(milliSeccond, uTimeResolution, TestTimeProc, (DWORD_PTR)this, TIME_ONESHOT);
}


void KillMMTimer()
{
 if( nTimeId != NULL )
 {
  timeKillEvent(nTimeId);
 }
}


void NotifyDummyReadSocket()
{
 if( readPort == -1 )
 {
  return;
 }

 struct sockaddr_in addr;
 addr.sin_family = AF_INET;
 addr.sin_addr.s_addr = inet_addr("127.0.0.1");
 addr.sin_port = htons(readPort);

 sendto(dummyWriteSocket, "1", 1, 0, (struct sockaddr*)&addr, sizeof(addr));
}


void SelectThreadProc()
{
 ...
 
  unsigned uTimeout = tv_timeToDelay.tv_sec * 1000000 + tv_timeToDelay.tv_usec;

  SetMMTimer(uTimeout);

  //int nDiff = 0;
  //gettimeofday(&start, NULL);

  int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
   
  ...
     
  KillMMTimer();
 
  ...
 
  if( FD_ISSET(dummyReadSocket, &readSet) )
  {
   char buf[128];
   recvfrom(dummyReadSocket, buf, 128, 0, NULL, NULL);
  }
}

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有