Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1073491
  • 博文数量: 252
  • 博客积分: 4561
  • 博客等级: 上校
  • 技术积分: 2833
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-15 08:23
文章分类

全部博文(252)

文章存档

2015年(2)

2014年(1)

2013年(1)

2012年(16)

2011年(42)

2010年(67)

2009年(87)

2008年(36)

分类: LINUX

2012-02-22 16:41:11

(2010-04-04 01:07)
分类: 网络编程

根据上一篇文章PACKET_MMAP实现原理分析中PACKET_MMAP使用一节,写了一个简单的演示程序。


#include
#include
#include
#include
#include
#include
#include /* the L2 protocols */

void CallBackPacket(char *data)
{
    printf("Recv A Packet.\n");
}

int main()
{
    int fd = 0, ret = 0;
    char *buff = NULL;

    //fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    //可以使用ARP进行一下测试
    fd = socket(PF_PACKET, SOCK_DGRAM, htons (ETH_P_ARP));
    if(fd<0)
    {
        perror("socket");
        goto failed_2;
    }

//PACKET_VERSION和SO_BINDTODEVICE可以省略
#if 0
    const int tpacket_version = TPACKET_V1;
    /* set tpacket hdr version. */
    ret = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &tpacket_version, sizeof (int));
    if(ret<0)
    {
        perror("setsockopt");
        goto failed_2;
    }

//#define NETDEV_NAME "wlan0"
#define NETDEV_NAME "eth0"
    /* bind to device. */
    ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NETDEV_NAME, sizeof (NETDEV_NAME));
    if(ret<0)
    {
        perror("setsockopt");
        goto failed_2;
    }
#endif

    struct tpacket_req req;
#define PER_PACKET_SIZE 2048
    const int BUFFER_SIZE = 1024*1024*16; //16MB的缓冲区
    req.tp_block_size = 4096;
    req.tp_block_nr = BUFFER_SIZE/req.tp_block_size;
    req.tp_frame_size = PER_PACKET_SIZE;
    req.tp_frame_nr = BUFFER_SIZE/req.tp_frame_size;

    ret = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *)&req, sizeof(req));
    if(ret<0)
    {
        perror("setsockopt");
        goto failed_2;
    }

    buff = (char *)mmap(0, BUFFER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if(buff == MAP_FAILED)
    {
        perror("mmap");
        goto failed_2;
    }

    int nIndex=0, i=0;
    while(1)
    {
        //这里在poll前先检查是否已经有报文被捕获了
        struct tpacket_hdr* pHead = (struct tpacket_hdr*)(buff+ nIndex*PER_PACKET_SIZE);
        //如果frame的状态已经为TP_STATUS_USER了,说明已经在poll前已经有一个数据包被捕获了,如果poll后不再有数据包被捕获,那么这个报文不会被处理,这就是所谓的竞争情况。
        if(pHead->tp_status == TP_STATUS_USER)
            goto process_packet;

        //poll检测报文捕获
        struct pollfd pfd;
        pfd.fd = fd;
        //pfd.events = POLLIN|POLLRDNORM|POLLERR;
        pfd.events = POLLIN;
        pfd.revents = 0;
        ret = poll(&pfd, 1, -1);
        if(ret<0)
        {
            perror("poll");
            goto failed_1;
        }

process_packet:
        //尽力的去处理环形缓冲区中的数据frame,直到没有数据frame了
        for(i=0; i
        {
            struct tpacket_hdr* pHead = (struct tpacket_hdr*)(buff+ nIndex*PER_PACKET_SIZE);

            //XXX: 由于frame都在一个环形缓冲区中,因此如果下一个frame中没有数据了,后面的frame也就没有frame了
            if(pHead->tp_status == TP_STATUS_KERNEL)
                break;

            //处理数据frame
            CallBackPacket((char*)pHead+pHead->tp_net);

            //重新设置frame的状态为TP_STATUS_KERNEL
            pHead->tp_len = 0;
            pHead->tp_status = TP_STATUS_KERNEL;

            //更新环形缓冲区的索引,指向下一个frame
            nIndex++;
            nIndex%=req.tp_frame_nr;
        }

    }

success:
    close(fd);
    munmap(buff, BUFFER_SIZE);
    return 0;

failed_1:
    munmap(buff, BUFFER_SIZE);
    
failed_2:
    close(fd);
    return -1;
}
阅读(922) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~