fireflyc / million-tcp-client(模拟百万级TCP并发)(代码)

Posted Dontla

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了fireflyc / million-tcp-client(模拟百万级TCP并发)(代码)相关的知识,希望对你有一定的参考价值。

mkdir -p build cd build && cmake .. make 然后执行 ./tcp-client

程序依赖libpcap和libnet请自行安装依赖ubuntu是执行apt-get install libpcap-dev libnet-dev

(main.c)

//
// Created by fireflyc on 2016/12/26.
//
#include <stdlib.h>
#include <libnet.h>
#include <pcap.h>
#include <pthread.h>

#define TRUE (1==1)
#define FALSE (1==0)
#define IP2UINT32(up) *((uint32_t *)&up)

typedef struct tcp_packet 
    uint32_t seq;
    uint32_t ack;
    uint8_t control;

    uint16_t dport;
    uint16_t sport;
    in_addr_t dst_addr;
    in_addr_t src_addr;
 tcp_packet_t;

typedef struct arphdr 
    u_int16_t htype;    /* Hardware Type           */
    u_int16_t ptype;    /* Protocol Type           */
    u_char hlen;        /* Hardware Address Length */
    u_char plen;        /* Protocol Address Length */
    u_int16_t oper;     /* Operation Code          */
    u_char sha[6];      /* Sender hardware address */
    u_char spa[4];      /* Sender IP address       */
    u_char tha[6];      /* Target hardware address */
    u_char tpa[4];      /* Target IP address       */
 arphdr_t;

typedef struct client_context 
    const char *dev;
    pcap_t *pcap;

    uint16_t request_port;
    struct in_addr request_ip;

    struct in_addr self;
 client_context_t;

int send_tcp(tcp_packet_t *tcp_pkt, client_context_t *context, char *errbuf) 
    libnet_t *libnet = libnet_init(LIBNET_RAW4, context->dev, errbuf);//raw socket
    if (libnet == NULL) 
        snprintf(errbuf, BUFSIZ, "open libnet error %s", errbuf);
        return FALSE;
    
    libnet_ptag_t t = libnet_build_tcp(
            tcp_pkt->sport, // source port
            tcp_pkt->dport, // dest port
            tcp_pkt->seq, // sequence number
            tcp_pkt->ack, // ack number
            tcp_pkt->control, // flags //ACK确认用户发送的请求数据包 push立即发送 FIN只在最后一条数据设置
            255, // window size
            0, // checksum
            0, // urg ptr //
            (uint16_t) LIBNET_TCP_H, // total length of the TCP packet
            NULL, // response
            0, // response_length
            libnet, // libnet_t pointer
            0 // ptag
    );
    if (t == -1) 
        snprintf(errbuf, BUFSIZ, "Can't build TCP header: %s", libnet_geterror(libnet));
        goto bad;
    
    t = libnet_build_ipv4(
            (uint16_t) (LIBNET_IPV4_H + LIBNET_TCP_H), // length
            // TOS bits 最小延时、最大吞吐量、最高可靠性 最小费用 这个字段一般会被设备忽略。
            0, //不设置
            (uint16_t) libnet_get_prand(LIBNET_PRu16), // IPID 16位随机数
            IP_DF, // fragmentation 不分片
            64, // TTL 一般设置为64达到64就可以死了延时太高了
            IPPROTO_TCP, // protocol, 表示使用TCP协议
            0, // checksum
            tcp_pkt->src_addr, // source address
            tcp_pkt->dst_addr, // dest address
            NULL, // response
            0, // response length //
            libnet, // libnet_t pointer
            0
    );
    if (t == -1) 
        snprintf(errbuf, BUFSIZ, " Can't build IP header: %s", libnet_geterror(libnet));
        goto bad;
    
    int write_size = libnet_write(libnet);
    if (write_size == -1) 
        snprintf(errbuf, BUFSIZ, "Writer error %s", libnet_geterror(libnet));
        goto bad;
    
    libnet_destroy(libnet);
    return TRUE;
    bad:
    libnet_destroy(libnet);
    return FALSE;



int send_arp(struct libnet_ethernet_hdr *eth_hdr, arphdr_t *arp_hdr, client_context_t *context, char *errbuf) 
    libnet_t *net = libnet_init(LIBNET_LINK, context->dev, errbuf);
    struct libnet_ether_addr *mac_addr = libnet_get_hwaddr(net);
    libnet_ptag_t t = libnet_autobuild_arp(
            ARPOP_REPLY,                           /* operation type */
            (const uint8_t *) mac_addr,                              /* sender hardware addr */
            (uint8_t *) &arp_hdr->tpa,       /* sender protocol addr */
            eth_hdr->ether_shost,                  /* target hardware addr */
            (uint8_t *) &arp_hdr->spa,       /* target protocol addr */
            net);                                     /* libnet id */

    if (t == -1) 
        snprintf(errbuf, BUFSIZ, "Can't build ARP header: %s", libnet_geterror(net));
        goto bad;
    
    t = libnet_build_ethernet(
            arp_hdr->sha,                           /* ethernet destination */
            (const uint8_t *) mac_addr,
            ETHERTYPE_ARP,                          /* protocol type */
            NULL,
            0,
            net, /* libnet handle */
            0);
    if (t == -1) 
        snprintf(errbuf, BUFSIZ, "Can't build ethernet header: %s", libnet_geterror(net));
        goto bad;
    
    int write_size = libnet_write(net);
    if (write_size == -1) 
        snprintf(errbuf, BUFSIZ, "Writer error %s", libnet_geterror(net));
        goto bad;
    
    libnet_destroy(net);
    return TRUE;

    bad:
    libnet_destroy(net);
    return FALSE;


void on_packet(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet) 
    client_context_t *context = (client_context_t *) arg;
    char errbuf[BUFSIZ];

    struct libnet_ethernet_hdr *ether_hdr = (struct libnet_ethernet_hdr *) packet;
    if (ntohs(ether_hdr->ether_type) == ETHERTYPE_ARP) 
        arphdr_t *arp_hdr = (arphdr_t *) (packet + LIBNET_ETH_H);
        if (ntohs(arp_hdr->oper) == ARPOP_REQUEST &&
            (context->self.s_addr == IP2UINT32(arp_hdr->spa) || context->self.s_addr == IP2UINT32(arp_hdr->tpa))) 
            printf("received arp query\\n");
            //收到ARP
            memset(errbuf, 0, BUFSIZ);
            if (!send_arp(ether_hdr, arp_hdr, context, errbuf)) 
                printf("send response error %s\\n", errbuf);
            
        
        return;
    

    struct libnet_ipv4_hdr *ip_hdr = (struct libnet_ipv4_hdr *) (packet + LIBNET_ETH_H);
    uint ip_size = (uint) ip_hdr->ip_hl * 4;
    if (ip_size < 20) 
        return;
    
    if (ip_hdr->ip_p != IPPROTO_TCP) 
        return;
    
    //确定是TCP数据包
    struct libnet_tcp_hdr *tcp_hdr = (struct libnet_tcp_hdr *) (packet + LIBNET_ETH_H + ip_size);
    uint tcp_size = (uint) (tcp_hdr->th_off * 4);
    if (tcp_size < 20) 
        return;
    
    uint payload_size = (uint) (ntohs(ip_hdr->ip_len) - (ip_size + tcp_size));
    if (payload_size == 0) 
        if (tcp_hdr->th_flags == (TH_ACK | TH_SYN) && ntohs(tcp_hdr->th_sport) == context->request_port) 
            //握手数据包
            tcp_packet_t pkt;
            pkt.control = TH_ACK;
            pkt.seq = ntohl(tcp_hdr->th_ack);
            pkt.ack = ntohl(tcp_hdr->th_seq) + 1;
            pkt.src_addr = ip_hdr->ip_dst.s_addr;
            pkt.dst_addr = ip_hdr->ip_src.s_addr;
            pkt.sport = ntohs(tcp_hdr->th_dport);
            pkt.dport = ntohs(tcp_hdr->th_sport);
            if (!send_tcp(&pkt, context, errbuf)) 
                printf("ack error %s\\n", errbuf);
            
            return;
        
    


int tcp_syn(uint16_t local_port, client_context_t *context) 
    char errbuf[BUFSIZ];
    tcp_packet_t pkt;
    pkt.seq = libnet_get_prand(LIBNET_PRu32); //随机seq
    pkt.control = TH_SYN; //SYN
    pkt.src_addr = context->self.s_addr; //"我"的IP
    pkt.dst_addr = context->request_ip.s_addr;//目标IP
    pkt.sport = local_port; //本机端口
    pkt.dport = context->request_port;//目标端口
    if (!send_tcp(&pkt, context, errbuf)) 
        printf("sync error %s\\n", errbuf);
        return FALSE;
    
    return TRUE;



pcap_t *init_pcap(const char *dev, const char *filter_exp, char *errbuf) 
    bpf_u_int32 netp;
    bpf_u_int32 maskp;
    if (pcap_lookupnet(dev, &netp, &maskp, errbuf) == -1) 
        snprintf(errbuf, BUFSIZ, "lookup %s failed", dev);
        return NULL;
    
    pcap_t *pcap = pcap_open_live(dev, 1500, 1, 1, errbuf);
    if (pcap == NULL) 
        return NULL;
    
    if (filter_exp != NULL) 
        struct bpf_program fp;
        if (pcap_compile(pcap, &fp, filter_exp, 1, netp) == -1) 
            snprintf(errbuf, BUFSIZ, "Compile filter expression failed %s cause: %s", filter_exp,
                     pcap_geterr(pcap));
            pcap_close(pcap);
            return NULL;
        
        if (pcap_setfilter(pcap, &fp) == -1) 
            snprintf(errbuf, BUFSIZ, "Install filter failed %s", pcap_geterr(pcap));
            pcap_close(pcap);
            return NULL;
        
    
    return pcap;


/**
 * 发送TCP SYN(1-65535端口),在on_packet中处理三次握手的第二个数据包和ARP数据包
 * */
void *fun(void *context) 
    for (uint16_t i = 1; i < 65535; i++) 
        tcp_syn(i, context);
        printf("send in port %d\\n", i);
    
    return 0;


client_context_t *context;

static void termination(int signum) 
    if (context == NULL) 
        return;
    
    if (context->pcap != NULL) 
        pcap_close(context->pcap);
        context->pcap = NULL;
    
    free(context);


int main(int argc, char **argv) 
    char errbuf[BUFSIZ];
    if (argc != 5) 
        printf("usage tcp-client <dev> <myip> <toip> <port>\\n example: tcp-client ens33 172.16.46.200 172.16.46.127 8888\\n");
        return EXIT_FAILURE;
    
    const char *dev = argv[1];
    //设置ctrl+c和kill的信号回调,正常释放资源
    signal(SIGINT, termination);
    signal(SIGTERM, termination);
    //context记录了一些乱七八糟的数据
    context = (client_context_t *) malloc(sizeof(client_context_t));
    inet_aton(argv[2], &context->self);//自己的IP地址
    inet_aton(argv[3], &context->request_ip);//目标IP地址
    context->request_port = atoi(argv[4]);//目标端口
    context->dev = dev;//使用那块网卡
    context->pcap = init_pcap(dev, NULL, errbuf);

    if (context->pcap == NULL) 
        printf("init pcap error %s\\n", errbuf);
        return EXIT_FAILURE;
    

    pthread_t thread;
    pthread_create(&thread, NULL, fun, context);//发送数据的线程

    //开始循环监听数据包,这句话代码会阻塞当前线程
    pcap_loop(context->pcap, -1, on_packet, (u_char *) context);

    return EXIT_SUCCESS;

参考文章1:模拟百万级TCP并发

参考文章2:fireflyc / million-tcp-client

参考文章3:KYSL linux 模拟多个ip socket tcp连接服务端(测试java服务器是否支持同时处理100个摄像头TCP连接)

以上是关于fireflyc / million-tcp-client(模拟百万级TCP并发)(代码)的主要内容,如果未能解决你的问题,请参考以下文章

C40PL 适配车牌识别应用

搞懂分布式技术10:LVS实现负载均衡的原理与实践