├── .gitignore ├── CMakeLists.txt ├── README.md ├── deps └── cmake │ ├── FindLibNetConfig.cmake │ └── FindPCAPConfig.cmake └── src └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-debug 2 | build 3 | .idea 4 | .vagrant -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(tcp-client) 3 | set(EXE_NAME tcp-client) 4 | set(CMAKE_PREFIX_PATH ${PROJECT_SOURCE_DIR}/deps/cmake) 5 | set(CMAKE_C_STANDARD 99) 6 | aux_source_directory(./src SOURCE_FILES) 7 | #优先static 8 | SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) 9 | 10 | #数学库 11 | list(APPEND DEPS_LIB m) 12 | 13 | add_executable(${EXE_NAME} ${SOURCE_FILES}) 14 | 15 | #多线程支持 16 | find_package(Threads REQUIRED) 17 | if (THREADS_HAVE_PTHREAD_ARG) 18 | target_compile_options(PUBLIC ${EXE_NAME} "-pthread") 19 | endif () 20 | if (CMAKE_THREAD_LIBS_INIT) 21 | list(APPEND DEPS_LIB ${CMAKE_THREAD_LIBS_INIT}) 22 | endif () 23 | 24 | find_package(FindPCAP) 25 | if (PCAP_FOUND) 26 | include_directories(${PCAP_INCLUDE_DIR}) 27 | list(APPEND DEPS_LIB ${PCAP_LIBRARY}) 28 | else () 29 | message(FATAL_ERROR "libpcap not found!") 30 | endif () 31 | find_package(FindLibNet) 32 | if (LIBNET_FOUND) 33 | include_directories(${LIBNET_INCLUDE_DIR}) 34 | list(APPEND DEPS_LIB ${LIBNET_LIBRARIES}) 35 | else () 36 | message(FATAL_ERROR "libnet not found!") 37 | endif () 38 | target_link_libraries(${EXE_NAME} ${DEPS_LIB}) 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | * 百万级TCP测试工具 2 | 3 | 参考《模拟百万级TCP并发》 4 | 5 | mkdir -p build 6 | cd build && cmake .. 7 | make 8 | 然后执行 9 | ./tcp-client 10 | 11 | 程序依赖libpcap和libnet请自行安装依赖ubuntu是执行`apt-get install libpcap-dev libnet-dev` -------------------------------------------------------------------------------- /deps/cmake/FindLibNetConfig.cmake: -------------------------------------------------------------------------------- 1 | # - Find libnet 2 | # 3 | # -*- cmake -*- 4 | # 5 | # Find the libnet module 6 | # 7 | # LIBNET_INCLUDE_DIR - where to find libnet.h, etc. 8 | # LIBNET_LIBRARIES - List of libraries when using LibNet. 9 | # LIBNET_FOUND - True if libnet found. 10 | 11 | IF (LIBNET_INCLUDE_DIR) 12 | # Already in cache, be silent 13 | SET(LIBNET_FIND_QUIETLY TRUE) 14 | ENDIF (LIBNET_INCLUDE_DIR) 15 | 16 | FIND_PATH(LIBNET_INCLUDE_DIR libnet.h 17 | /usr/include 18 | ) 19 | 20 | SET(LIBNET_NAMES net) 21 | FIND_LIBRARY(LIBNET_LIBRARY 22 | NAMES ${LIBNET_NAMES} 23 | PATHS /usr/lib /usr/local/lib 24 | PATH_SUFFIXES libnet 25 | ) 26 | 27 | ADD_DEFINITIONS(-DLIBNET_LIL_ENDIAN=1) 28 | 29 | IF (LIBNET_INCLUDE_DIR AND LIBNET_LIBRARY) 30 | SET(LIBNET_FOUND TRUE) 31 | SET(LIBNET_LIBRARIES ${LIBNET_LIBRARY} ) 32 | 33 | ELSE (LIBNET_INCLUDE_DIR AND LIBNET_LIBRARY) 34 | SET(LIBNET_FOUND FALSE) 35 | SET(LIBNET_LIBRARIES ) 36 | ENDIF (LIBNET_INCLUDE_DIR AND LIBNET_LIBRARY) 37 | 38 | IF (LIBNET_FOUND) 39 | IF (NOT LIBNET_FIND_QUIETLY) 40 | MESSAGE(STATUS "Found LibNet: ${LIBNET_LIBRARY}") 41 | ENDIF (NOT LIBNET_FIND_QUIETLY) 42 | ELSE (LIBNET_FOUND) 43 | IF (LIBNET_FIND_REQUIRED) 44 | MESSAGE(STATUS "Looked for LibNet libraries named ${LIBNET_NAMES}.") 45 | MESSAGE(FATAL_ERROR "Could NOT find LibNet library") 46 | ENDIF (LIBNET_FIND_REQUIRED) 47 | ENDIF (LIBNET_FOUND) 48 | 49 | MARK_AS_ADVANCED( 50 | LIBNET_LIBRARY 51 | LIBNET_INCLUDE_DIR 52 | ) 53 | -------------------------------------------------------------------------------- /deps/cmake/FindPCAPConfig.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libpcap include dirs and libraries 2 | # 3 | # Usage of this module as follows: 4 | # 5 | # find_package(PCAP) 6 | # 7 | # Variables used by this module, they can change the default behaviour and need 8 | # to be set before calling find_package: 9 | # 10 | # PCAP_ROOT_DIR Set this variable to the root installation of 11 | # libpcap if the module has problems finding the 12 | # proper installation path. 13 | # 14 | # Variables defined by this module: 15 | # 16 | # PCAP_FOUND System has libpcap, include and library dirs found 17 | # PCAP_INCLUDE_DIR The libpcap include directories. 18 | # PCAP_LIBRARY The libpcap library (possibly includes a thread 19 | # library e.g. required by pf_ring's libpcap) 20 | # HAVE_PF_RING If a found version of libpcap supports PF_RING 21 | 22 | find_path(PCAP_ROOT_DIR 23 | NAMES include/pcap.h 24 | ) 25 | 26 | find_path(PCAP_INCLUDE_DIR 27 | NAMES pcap.h 28 | HINTS ${PCAP_ROOT_DIR}/include 29 | ) 30 | 31 | find_library(PCAP_LIBRARY 32 | NAMES pcap 33 | HINTS ${PCAP_ROOT_DIR}/lib 34 | ) 35 | 36 | include(FindPackageHandleStandardArgs) 37 | find_package_handle_standard_args(PCAP DEFAULT_MSG 38 | PCAP_LIBRARY 39 | PCAP_INCLUDE_DIR 40 | ) 41 | 42 | include(CheckCSourceCompiles) 43 | set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY}) 44 | check_c_source_compiles("int main() { return 0; }" PCAP_LINKS_SOLO) 45 | set(CMAKE_REQUIRED_LIBRARIES) 46 | 47 | # check if linking against libpcap also needs to link against a thread library 48 | if (NOT PCAP_LINKS_SOLO) 49 | find_package(Threads) 50 | if (THREADS_FOUND) 51 | set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) 52 | check_c_source_compiles("int main() { return 0; }" PCAP_NEEDS_THREADS) 53 | set(CMAKE_REQUIRED_LIBRARIES) 54 | endif () 55 | if (THREADS_FOUND AND PCAP_NEEDS_THREADS) 56 | set(_tmp ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) 57 | list(REMOVE_DUPLICATES _tmp) 58 | set(PCAP_LIBRARY ${_tmp} 59 | CACHE STRING "Libraries needed to link against libpcap" FORCE) 60 | else () 61 | message(FATAL_ERROR "Couldn't determine how to link against libpcap") 62 | endif () 63 | endif () 64 | 65 | include(CheckFunctionExists) 66 | set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY}) 67 | check_function_exists(pcap_get_pfring_id HAVE_PF_RING) 68 | set(CMAKE_REQUIRED_LIBRARIES) 69 | 70 | mark_as_advanced( 71 | PCAP_ROOT_DIR 72 | PCAP_INCLUDE_DIR 73 | PCAP_LIBRARY 74 | ) -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by fireflyc on 2016/12/26. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define TRUE (1==1) 10 | #define FALSE (1==0) 11 | #define IP2UINT32(up) *((uint32_t *)&up) 12 | 13 | typedef struct tcp_packet { 14 | uint32_t seq; 15 | uint32_t ack; 16 | uint8_t control; 17 | 18 | uint16_t dport; 19 | uint16_t sport; 20 | in_addr_t dst_addr; 21 | in_addr_t src_addr; 22 | } tcp_packet_t; 23 | 24 | typedef struct arphdr { 25 | u_int16_t htype; /* Hardware Type */ 26 | u_int16_t ptype; /* Protocol Type */ 27 | u_char hlen; /* Hardware Address Length */ 28 | u_char plen; /* Protocol Address Length */ 29 | u_int16_t oper; /* Operation Code */ 30 | u_char sha[6]; /* Sender hardware address */ 31 | u_char spa[4]; /* Sender IP address */ 32 | u_char tha[6]; /* Target hardware address */ 33 | u_char tpa[4]; /* Target IP address */ 34 | } arphdr_t; 35 | 36 | typedef struct client_context { 37 | const char *dev; 38 | pcap_t *pcap; 39 | 40 | uint16_t request_port; 41 | struct in_addr request_ip; 42 | 43 | struct in_addr self; 44 | } client_context_t; 45 | 46 | int send_tcp(tcp_packet_t *tcp_pkt, client_context_t *context, char *errbuf) { 47 | libnet_t *libnet = libnet_init(LIBNET_RAW4, context->dev, errbuf);//raw socket 48 | if (libnet == NULL) { 49 | snprintf(errbuf, BUFSIZ, "open libnet error %s", errbuf); 50 | return FALSE; 51 | } 52 | libnet_ptag_t t = libnet_build_tcp( 53 | tcp_pkt->sport, // source port 54 | tcp_pkt->dport, // dest port 55 | tcp_pkt->seq, // sequence number 56 | tcp_pkt->ack, // ack number 57 | tcp_pkt->control, // flags //ACK确认用户发送的请求数据包 push立即发送 FIN只在最后一条数据设置 58 | 255, // window size 59 | 0, // checksum 60 | 0, // urg ptr // 61 | (uint16_t) LIBNET_TCP_H, // total length of the TCP packet 62 | NULL, // response 63 | 0, // response_length 64 | libnet, // libnet_t pointer 65 | 0 // ptag 66 | ); 67 | if (t == -1) { 68 | snprintf(errbuf, BUFSIZ, "Can't build TCP header: %s", libnet_geterror(libnet)); 69 | goto bad; 70 | } 71 | t = libnet_build_ipv4( 72 | (uint16_t) (LIBNET_IPV4_H + LIBNET_TCP_H), // length 73 | // TOS bits 最小延时、最大吞吐量、最高可靠性 最小费用 这个字段一般会被设备忽略。 74 | 0, //不设置 75 | (uint16_t) libnet_get_prand(LIBNET_PRu16), // IPID 16位随机数 76 | IP_DF, // fragmentation 不分片 77 | 64, // TTL 一般设置为64达到64就可以死了延时太高了 78 | IPPROTO_TCP, // protocol, 表示使用TCP协议 79 | 0, // checksum 80 | tcp_pkt->src_addr, // source address 81 | tcp_pkt->dst_addr, // dest address 82 | NULL, // response 83 | 0, // response length // 84 | libnet, // libnet_t pointer 85 | 0 86 | ); 87 | if (t == -1) { 88 | snprintf(errbuf, BUFSIZ, " Can't build IP header: %s", libnet_geterror(libnet)); 89 | goto bad; 90 | } 91 | int write_size = libnet_write(libnet); 92 | if (write_size == -1) { 93 | snprintf(errbuf, BUFSIZ, "Writer error %s", libnet_geterror(libnet)); 94 | goto bad; 95 | } 96 | libnet_destroy(libnet); 97 | return TRUE; 98 | bad: 99 | libnet_destroy(libnet); 100 | return FALSE; 101 | } 102 | 103 | 104 | int send_arp(struct libnet_ethernet_hdr *eth_hdr, arphdr_t *arp_hdr, client_context_t *context, char *errbuf) { 105 | libnet_t *net = libnet_init(LIBNET_LINK, context->dev, errbuf); 106 | struct libnet_ether_addr *mac_addr = libnet_get_hwaddr(net); 107 | libnet_ptag_t t = libnet_autobuild_arp( 108 | ARPOP_REPLY, /* operation type */ 109 | (const uint8_t *) mac_addr, /* sender hardware addr */ 110 | (uint8_t *) &arp_hdr->tpa, /* sender protocol addr */ 111 | eth_hdr->ether_shost, /* target hardware addr */ 112 | (uint8_t *) &arp_hdr->spa, /* target protocol addr */ 113 | net); /* libnet id */ 114 | 115 | if (t == -1) { 116 | snprintf(errbuf, BUFSIZ, "Can't build ARP header: %s", libnet_geterror(net)); 117 | goto bad; 118 | } 119 | t = libnet_build_ethernet( 120 | arp_hdr->sha, /* ethernet destination */ 121 | (const uint8_t *) mac_addr, 122 | ETHERTYPE_ARP, /* protocol type */ 123 | NULL, 124 | 0, 125 | net, /* libnet handle */ 126 | 0); 127 | if (t == -1) { 128 | snprintf(errbuf, BUFSIZ, "Can't build ethernet header: %s", libnet_geterror(net)); 129 | goto bad; 130 | } 131 | int write_size = libnet_write(net); 132 | if (write_size == -1) { 133 | snprintf(errbuf, BUFSIZ, "Writer error %s", libnet_geterror(net)); 134 | goto bad; 135 | } 136 | libnet_destroy(net); 137 | return TRUE; 138 | 139 | bad: 140 | libnet_destroy(net); 141 | return FALSE; 142 | } 143 | 144 | void on_packet(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet) { 145 | client_context_t *context = (client_context_t *) arg; 146 | char errbuf[BUFSIZ]; 147 | 148 | struct libnet_ethernet_hdr *ether_hdr = (struct libnet_ethernet_hdr *) packet; 149 | if (ntohs(ether_hdr->ether_type) == ETHERTYPE_ARP) { 150 | arphdr_t *arp_hdr = (arphdr_t *) (packet + LIBNET_ETH_H); 151 | if (ntohs(arp_hdr->oper) == ARPOP_REQUEST && 152 | (context->self.s_addr == IP2UINT32(arp_hdr->spa) || context->self.s_addr == IP2UINT32(arp_hdr->tpa))) { 153 | printf("received arp query\n"); 154 | //收到ARP 155 | memset(errbuf, 0, BUFSIZ); 156 | if (!send_arp(ether_hdr, arp_hdr, context, errbuf)) { 157 | printf("send response error %s\n", errbuf); 158 | } 159 | } 160 | return; 161 | } 162 | 163 | struct libnet_ipv4_hdr *ip_hdr = (struct libnet_ipv4_hdr *) (packet + LIBNET_ETH_H); 164 | uint ip_size = (uint) ip_hdr->ip_hl * 4; 165 | if (ip_size < 20) { 166 | return; 167 | } 168 | if (ip_hdr->ip_p != IPPROTO_TCP) { 169 | return; 170 | } 171 | //确定是TCP数据包 172 | struct libnet_tcp_hdr *tcp_hdr = (struct libnet_tcp_hdr *) (packet + LIBNET_ETH_H + ip_size); 173 | uint tcp_size = (uint) (tcp_hdr->th_off * 4); 174 | if (tcp_size < 20) { 175 | return; 176 | } 177 | uint payload_size = (uint) (ntohs(ip_hdr->ip_len) - (ip_size + tcp_size)); 178 | if (payload_size == 0) { 179 | if (tcp_hdr->th_flags == (TH_ACK | TH_SYN) && ntohs(tcp_hdr->th_sport) == context->request_port) { 180 | //握手数据包 181 | tcp_packet_t pkt; 182 | pkt.control = TH_ACK; 183 | pkt.seq = ntohl(tcp_hdr->th_ack); 184 | pkt.ack = ntohl(tcp_hdr->th_seq) + 1; 185 | pkt.src_addr = ip_hdr->ip_dst.s_addr; 186 | pkt.dst_addr = ip_hdr->ip_src.s_addr; 187 | pkt.sport = ntohs(tcp_hdr->th_dport); 188 | pkt.dport = ntohs(tcp_hdr->th_sport); 189 | if (!send_tcp(&pkt, context, errbuf)) { 190 | printf("ack error %s\n", errbuf); 191 | } 192 | return; 193 | } 194 | } 195 | } 196 | 197 | int tcp_syn(uint16_t local_port, client_context_t *context) { 198 | char errbuf[BUFSIZ]; 199 | tcp_packet_t pkt; 200 | pkt.seq = libnet_get_prand(LIBNET_PRu32); //随机seq 201 | pkt.control = TH_SYN; //SYN 202 | pkt.src_addr = context->self.s_addr; //"我"的IP 203 | pkt.dst_addr = context->request_ip.s_addr;//目标IP 204 | pkt.sport = local_port; //本机端口 205 | pkt.dport = context->request_port;//目标端口 206 | if (!send_tcp(&pkt, context, errbuf)) { 207 | printf("sync error %s\n", errbuf); 208 | return FALSE; 209 | } 210 | return TRUE; 211 | } 212 | 213 | 214 | pcap_t *init_pcap(const char *dev, const char *filter_exp, char *errbuf) { 215 | bpf_u_int32 netp; 216 | bpf_u_int32 maskp; 217 | if (pcap_lookupnet(dev, &netp, &maskp, errbuf) == -1) { 218 | snprintf(errbuf, BUFSIZ, "lookup %s failed", dev); 219 | return NULL; 220 | } 221 | pcap_t *pcap = pcap_open_live(dev, 1500, 1, 1, errbuf); 222 | if (pcap == NULL) { 223 | return NULL; 224 | } 225 | if (filter_exp != NULL) { 226 | struct bpf_program fp; 227 | if (pcap_compile(pcap, &fp, filter_exp, 1, netp) == -1) { 228 | snprintf(errbuf, BUFSIZ, "Compile filter expression failed %s cause: %s", filter_exp, 229 | pcap_geterr(pcap)); 230 | pcap_close(pcap); 231 | return NULL; 232 | } 233 | if (pcap_setfilter(pcap, &fp) == -1) { 234 | snprintf(errbuf, BUFSIZ, "Install filter failed %s", pcap_geterr(pcap)); 235 | pcap_close(pcap); 236 | return NULL; 237 | } 238 | } 239 | return pcap; 240 | } 241 | 242 | /** 243 | * 发送TCP SYN(1-65535端口),在on_packet中处理三次握手的第二个数据包和ARP数据包 244 | * */ 245 | void *fun(void *context) { 246 | for (uint16_t i = 1; i < 65535; i++) { 247 | tcp_syn(i, context); 248 | printf("send in port %d\n", i); 249 | } 250 | return 0; 251 | } 252 | 253 | client_context_t *context; 254 | 255 | static void termination(int signum) { 256 | if (context == NULL) { 257 | return; 258 | } 259 | if (context->pcap != NULL) { 260 | pcap_close(context->pcap); 261 | context->pcap = NULL; 262 | } 263 | free(context); 264 | } 265 | 266 | int main(int argc, char **argv) { 267 | char errbuf[BUFSIZ]; 268 | if (argc != 5) { 269 | printf("usage tcp-client \n example: tcp-client ens33 172.16.46.200 172.16.46.127 8888\n"); 270 | return EXIT_FAILURE; 271 | } 272 | const char *dev = argv[1]; 273 | //设置ctrl+c和kill的信号回调,正常释放资源 274 | signal(SIGINT, termination); 275 | signal(SIGTERM, termination); 276 | //context记录了一些乱七八糟的数据 277 | context = (client_context_t *) malloc(sizeof(client_context_t)); 278 | inet_aton(argv[2], &context->self);//自己的IP地址 279 | inet_aton(argv[3], &context->request_ip);//目标IP地址 280 | context->request_port = atoi(argv[4]);//目标端口 281 | context->dev = dev;//使用那块网卡 282 | context->pcap = init_pcap(dev, NULL, errbuf); 283 | 284 | if (context->pcap == NULL) { 285 | printf("init pcap error %s\n", errbuf); 286 | return EXIT_FAILURE; 287 | } 288 | 289 | pthread_t thread; 290 | pthread_create(&thread, NULL, fun, context);//发送数据的线程 291 | 292 | //开始循环监听数据包,这句话代码会阻塞当前线程 293 | pcap_loop(context->pcap, -1, on_packet, (u_char *) context); 294 | 295 | return EXIT_SUCCESS; 296 | } --------------------------------------------------------------------------------