Ø 应用软件每次发送一个数据包一次;
Ø 应用软件每次发送一个数据包大于一次,次数预先设定;
为了能够发送大量的数据包, WinPcap 提供了基于发送队列发送的方式。通过发送队列提供了一种高级的,强大的,结构更优的方式来发送一组数据包。
wpcap.dll 中与发送队列相关的函数所使用的 pcap_send_queue 结构体,是存储原始数据包 ( 将被 pcap_sendqueue_transmit 函数发送到网络上的数据包 ) 的数据结构,具体定义如下:

struct pcap_send_queue

u_int maxlen; // 队列最大长度 ( 字节数 ) ,描述了 buffer 成员的容量

u_int len; // 队列当前的字节数

char *buffer; // 储存待发数据包的缓冲区

typedef struct pcap_send_queue pcap_send_queue;

1.6.2.1 pcap_sendqueue_alloc pcap_sendqueue_destroy 函数

发送队列通过调用 pcap_sendqueue_alloc 函数创建,并且需要指定队列的大小。函数的原型如下:
pcap_send_queue* pcap_sendqueue_alloc(u_int memsize);
函数分配存储发送数据包队列的内存空间 ,发送队列是一个容器,它能容纳不同数量的数据包,这些数据包将被 pcap_sendqueue_transmit 函数发送到网络上。其中参数 memsize 是队列的大小(以字节为单位),决定了发送队列能存储数据包的最大容量。
函数成功返回所分配队列的内存指针,否则返回 NULL

函数的具体实现如下:

pcap_send_queue* pcap_sendqueue_alloc(u_int memsize)

pcap_send_queue *tqueue;

/* 分配发送队列结构体的内存空间 */

tqueue =

(pcap_send_queue*)malloc( sizeof (pcap_send_queue));

if (tqueue == NULL){

return NULL;

/* 分配发送队列 buffer 成员的内存空间 */

tqueue->buffer = ( char *)malloc(memsize);

if (tqueue->buffer == NULL){

free(tqueue);

return NULL;

tqueue->maxlen = memsize; // 设置发送队列的最大存储空间

tqueue->len = 0; // 初始化发送队列的已用存储空间

return tqueue;

注意内存空间大小 memsize 参数应该包括每个数据包的头信息 ( struct pcap_pkthdr ) 空间,使用示例如下:

squeue=pcap_sendqueue_alloc(

(unsigned int)((MaxPacketLen+sizeof(struct pcap_pkthdr))*npacks) );

当发送队列不再需要时,需要使用 pcap_sendqueue_destroy 函数来 释放它所占用的内存,该函数原型如下:

void pcap_sendqueue_destroy(pcap_send_queue *queue);

函数的具体实现如下:

void pcap_sendqueue_destroy(pcap_send_queue* queue)

free(queue->buffer); // 释放发送队列 buffer 成员的内存空间

free(queue); // 释放发送队列结构体的空间

1.6.2.2 pcap_sendqueue_queue 函数

一旦发送队列被创建,就可以通过 pcap_sendqueue_queue 函数将数据包添加到发送队列中,该函数把一个数据包添加到 queue 参数所指的发送队列的尾部。函数原型如下:

int pcap_sendqueue_queue(pcap_send_queue *queue,

const struct pcap_pkthdr *pkt_header,

const u_char *pkt_data)

其中参数 queue 就是调用 pcap_sendqueue_alloc 函数所分配的发送队列,参数 pkt_header WinPcap 为待发数据包所附加的数据头信息,用来说明数据包的长度与发送时间戳,参数 pkt_data 为待发数据包。

函数成功返回 0 ,否则返回 -1

bpf_u_int32 caplen ; // 所捕获数据包的长度

bpf_u_int32 len ; // 数据包原始长度

其中结构体 timeval 的具体定义分别如下:

struct timeval {

long tv_sec ; // 秒数

long tv_usec ; // 微秒

函数 pcap_sendqueue_queue 的具体实现如下:

int pcap_sendqueue_queue(pcap_send_queue* queue,

const struct pcap_pkthdr *pkt_header,

const u_char *pkt_data)

/* 发送队列的容量不够,函数返回 -1*/

if (queue->len + sizeof ( struct pcap_pkthdr) +

pkt_header->caplen > queue->maxlen)

return -1;

/* 复制数据包头信息 pcap_pkthdr*/

memcpy(queue->buffer + queue->len, pkt_header, sizeof ( struct pcap_pkthdr));

queue->len += sizeof ( struct pcap_pkthdr);

/* 复制数据包 */

memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen);

queue->len += pkt_header->caplen;

return 0;

1.6.2.3 pcap_sendqueue_transmit 函数

函数 pcap_sendqueue_transmit 来发送一个发送队列,该函数原型如下:

u_int pcap_sendqueue_transmit(pcap_t *p,

pcap_send_queue *queue,int sync)

该函数发送一个原始数据包的队列到网络。参数 p 指向一个适配器,数据包将通过该适配器发送,参数 queue 指向一个容纳待发送数据包的 pcap_send_queue 结构体(参见 pcap_sendqueue_alloc pcap_sendqueue_queue 函数),参数 sync 决定了发送操作是否是同步的;如果不为 0 ,根据时间戳发送数据包,否则不根据时间戳,而是尽所能的快速发送各数据包。

函数返回值为实际所发送的字节数,如果该值小于希望发送的大小,那么发送过程中出现了错误。错误可能由驱动程序 / 适配器的问题或冲突的 / 假的发送队列导致。

函数的具体实现代码如下:

u_int pcap_sendqueue_transmit(pcap_t *p,

pcap_send_queue* queue, int sync)

u_int res;

DWORD error;

int errlen;

if (p->adapter==NULL)

sprintf(p->errbuf,

"Cannot transmit a queue to an offline capture

or to a TurboCap port" );

return 0;

// 发送数据包队列

res = PacketSendPackets (p->adapter,

queue->buffer,

queue->len,

(BOOLEAN)sync);

if (res != queue->len)

// 错误,记录错误信息

error = GetLastError();

FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,

error,0,p->errbuf,PCAP_ERRBUF_SIZE,NULL);

errlen = strlen(p->errbuf);

if (errlen >= 2) {

p->errbuf[errlen - 1] = '\0' ;

p->errbuf[errlen - 2] = '\0' ;

snprintf(p->errbuf, PCAP_ERRBUF_SIZE,

"Error opening adapter: %s" , p->errbuf);

return res;

请注意,使用 pcap_sendqueue_transmit 函数要比 pcap_sendpacket 函数来发送一系列数据更加有效率,因为发送队列保存在内核层驱动程序的缓冲区中,因此,减少了上下文交换的次数,获得更好的吞吐量。

同时请注意第三个参数 sync 。如果为非零值,那么发送过程将是同步进行,也就是说,只有与时间戳相符的数据包才会被处理。该同步操作由内核驱动程序“忙等待( busy wait )”循环来实现,所以这个操作需要消耗大量的 CPU 资源。尽管这个操作对 CPU 的要求很高,但它对数据包发送的处理结通常是很精确的 ( 通常在可达数微秒左右,或更小 ) 。 如此一个精度是采用 pcap_sendpacket 函数不可能达到的。

本文出自 “ 千江月 ” 博客,请务必保留此出处 http://eslxf.blog.51cto.com/918801/213826