http://www.cppblog.com/saga/archive/2006/04/16/5654.aspx
最近自己因为实际需要,用winpcap开发一个程序里面有需要转发包.自己就写了一个,发现转发时延>500ms.也确实把自己吓到了.便把转发的那段代码开始精减,大致如下
while
((j
=
pcap_next_ex(slecadopt,
&
pkt_header,(
const
u_char
**
)
&
pkt_data))
>=
0
)
{
if(j==0)
{
continue;
}
if(*(unsigned short *)(pkt_data+12)==htons(ETH_IP)&&(!(memcmp(pkt_data+6,myip->mac,6)==0))&&*(unsigned long *)(pkt_data+30)!=myip->ip&&(!(memcmp(pkt_data,bcast,6)==0)))
{
for(z=Pipmachead;z!=NULL;z=z->next)
{
if(*(unsigned long *)(pkt_data+30)==z->ip)
{
memcpy(cmac->mac,z->mac,6);
memcpy(cmac->mac+6,myip->mac,6);
memcpy(sendbuf,pkt_data,pkt_header->caplen);
memcpy(sendbuf,cmac,12);
pcap_sendpacket(slecadopt,sendbuf,pkt_header->caplen);
}
continue;
}
}
}
过程很简单,pcap_next_ex()收包,在做判断处理,然后用pcap_sendpacket()发包.
怎么想都不会有那么大的时延.很不解就搜索了下关于pcap_sendpacket()发包效能的网页,其中有一个
http://community.csdn.net/Expert/TopicView3.asp?id=4153633
来里面有人说
{
if(j==0)
{
continue;
}
if(*(unsigned short *)(pkt_data+12)==htons(ETH_IP)&&(!(memcmp(pkt_data+6,myip->mac,6)==0))&&*(unsigned long *)(pkt_data+30)!=myip->ip&&(!(memcmp(pkt_data,bcast,6)==0)))
{
for(z=Pipmachead;z!=NULL;z=z->next)
{
if(*(unsigned long *)(pkt_data+30)==z->ip)
{
memcpy(cmac->mac,z->mac,6);
memcpy(cmac->mac+6,myip->mac,6);
memcpy(sendbuf,pkt_data,pkt_header->caplen);
memcpy(sendbuf,cmac,12);
pcap_sendpacket(slecadopt,sendbuf,pkt_header->caplen);
}
continue;
}
}
}
我用winpcap做个一个初步的试验,我得机器是奔4
2
。
6
,内存512,我一个线程接包,一个线程收包,用动态连表做缓存,一秒钟转发1-2个包没有多大问题,要是再多了,就会又丢包,转发上千万包要丢几百个。
还有人说
winpcap的sendpacket超级慢,而且延迟很大。
我试图实现一个bridge,即一个端口进入的包不经修改从一个端口出,反之亦然。结果ping的结果显示延迟为数百毫秒级别,个别时候甚至到了1秒。运行其它应用更是不可忍受。
我想使用sendQuene,结果sendQuene的具体用法只见过预先准备好所有的包然后整体发送的,没见过随着包来随着发送的例子。或许对于我的bridge,winpcap是一个错误的选择?又或者大家有什么新鲜的思路?愿意听大家说说。
使我也开始怀疑pcap_sendpacket()了.难道真实pcap_sendpacket()效率底.但是我还是不相信.堂堂winpcap怎么可能怎么慢.使我发现破绽的使贴子里最后一的人的说法:
我试图实现一个bridge,即一个端口进入的包不经修改从一个端口出,反之亦然。结果ping的结果显示延迟为数百毫秒级别,个别时候甚至到了1秒。运行其它应用更是不可忍受。
我想使用sendQuene,结果sendQuene的具体用法只见过预先准备好所有的包然后整体发送的,没见过随着包来随着发送的例子。或许对于我的bridge,winpcap是一个错误的选择?又或者大家有什么新鲜的思路?愿意听大家说说。
我也写了个类似的程序,延时稳定在20ms左右,带宽可以达到10M
我发现一个有趣的事情,即我在运行brigde的服务器上,运行一个fluke的抓包工具,居然发现运行后,通过bridge后的时延居然只有1ms左右了,停止抓包工具后,延时又回到20ms左右,试了多次情况都一样
打开网卡的句柄是按下面的参数执行的
pcap_open(d -> name, // name of the device
65536 , // portion of the packet to capture.
PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
1 , // read timeout
NULL, // remote authentication
errbuf // error buffer
);
对数据报的捕获是用pcap_loop函数,我试过将pcap_open的read timeout参数设置为 - 1 ,即读取立即返回,但这样对系统资源占用太大,cpu利用率增加到100%,此时bridge的延时就很不稳定了,从1ms到1s都有。
他在里面特意提到了pcap_open()的read timeout参数,我回想起我的设的1000ms,也就是一秒.嫌疑很大.(注:我用的是pcap_open_live())
我发现一个有趣的事情,即我在运行brigde的服务器上,运行一个fluke的抓包工具,居然发现运行后,通过bridge后的时延居然只有1ms左右了,停止抓包工具后,延时又回到20ms左右,试了多次情况都一样
打开网卡的句柄是按下面的参数执行的
pcap_open(d -> name, // name of the device
65536 , // portion of the packet to capture.
PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
1 , // read timeout
NULL, // remote authentication
errbuf // error buffer
);
对数据报的捕获是用pcap_loop函数,我试过将pcap_open的read timeout参数设置为 - 1 ,即读取立即返回,但这样对系统资源占用太大,cpu利用率增加到100%,此时bridge的延时就很不稳定了,从1ms到1s都有。
pcap_open_live(d
->
name,
1000
,
1
,
1000
, error)
然后就去查Winpcap的官方资料
http://www.winpcap.org/docs/docs31/html/group__wpcapfunc.html
里面说的很清楚:
to_ms specifies the read timeout in milliseconds. The read timeout is used to arrange that the read not necessarily
return
immediately when a packet is seen, but that it wait
for
some amount of time to allow more packets to arrive and to read multiple packets from the OS kernel in one operation.
当已经发现包之后还会等多一些的包一起返回,等待时间就是read timeout.明白了,我把read timeout设为1以后时延降到了<10ms.
其中还需要提醒的是用Ethereal等一些抓包工具看到收包到转发时延很大时就以为是pcap_next_ex()和pcap_sendpacket()函数之间的处理造成了时延.这里容易给我们产生一个假相.以为网卡收到包的时刻就是转发程序就要开始处理.实际上pcap_next_ex()还在等待更多的包一起返回.
所以事发的原因离事发现场比较远.在pcap_open()那.程序就就是怎么千丝万缕的关联着.
大家一定要多怀疑自己,别去怪罪工具.winpcap很无辜的.嘻嘻!!肚子饿了去吃饭!!
-----saga.constantine