一.大致流程:
建立一个client端,一个server端,自己构建IP头和UDP头,写入数据(hello,world!)后通过原始套接字(SOCK_RAW)将包发出去。
server端收到数据后,打印UDP数据并发送确认消息(yes),client收到yes后将其打印。
二.其中:
client端IP:192.168.11.104 端口:8600
server端IP:192.168.11.105 端口:8686
三.注意事项:
1.运行原始套接字socket需要有root权限。
2.注意主机字节序和网络字节序的转
四.涉及的数据结构
1.ip部分的结构图:
2.ip结构体定义:
struct iphdr /* 该结构体在<netinet/ip.h>中定义 */ { #if __BYTE_ORDER == __LITTLE_ENDIAN /* 如果是小端字节序 */ unsigned int ihl:4; /*首部长度*/ unsigned int version:4; /* 版本 */ #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version:4; unsigned int ihl:4; #else # error "Please fix <bits/endian.h>" #endif u_int8_t tos; /* 区分服务 */ u_int16_t tot_len; /* 总长度 */ u_int16_t id; /* 标识 */ u_int16_t frag_off; /* 标志(3位)+片偏移(13位) */ u_int8_t ttl; /* 生存时间 */ u_int8_t protocol; /* 协议 */ u_int16_t check; /* 首部检验和 */ u_int32_t saddr; /* 源地址 */ u_int32_t daddr; /* 目的地址 */ /*The options start here. */ };
3.udp数据包的结构图:
4.udp结构体定义:
struct udphdr /* 该结构体在<netiniet/udp.h>中定义 */ { u_int16_t source; /*源端口*/ u_int16_t dest; /*目的端口*/ u_int16_t len; /*长度*/ u_int16_t check; /*校验和*/ };
五.实现如下:
client端:
1 /* 2 ============================================================================ 3 Name : test_client.c 4 Author : huh 5 Version : 6 Copyright : ---notice--- 7 Description : Hello World in C, Ansi-style 8 ============================================================================ 9 */ 10 11 #include <sys/types.h> 12 #include <sys/socket.h> 13 #include <stdio.h> 14 #include <netinet/in.h> 15 #include <arpa/inet.h> 16 #include <unistd.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <netinet/ip_icmp.h> 20 #include <netinet/udp.h> 21 22 #define MAXLINE 1024*10 23 24 struct udp_front //udp 25 { 26 uint32_t srcip; 27 uint32_t desip; 28 u_int8_t zero; 29 u_int8_t protocol; 30 u_int16_t len; 31 }; 32 33 u_int16_t in_chksum(u_int16_t *addr, int len); 34 u_int16_t udp_check(char *sendbuf, int len, const struct udp_front front); 35 int make_message(char sendbuf[], int send_buf_len, uint32_t src_ip, u_int16_t src_port, uint32_t des_ip, u_int16_t des_port); 36 37 int main() 38 { 39 int raw_sockfd; 40 int size = 1024*50; 41 char send_message[MAXLINE]; 42 struct sockaddr_in server_address; 43 //创建原始套接字 44 raw_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); 45 //创建套接字地址 46 bzero(&server_address,sizeof(server_address)); 47 server_address.sin_family = AF_INET; 48 server_address.sin_addr.s_addr = inet_addr("192.168.11.105"); 49 //设置套接字为随数据包含IP首部(设置这个选项后需要我们手动写入IP头) 50 setsockopt(raw_sockfd, IPPROTO_IP, IP_HDRINCL, &size, sizeof(size)); 51 52 int len; 53 bzero(&send_message, sizeof(send_message)); 54 //拼接完整的UDP数据包(IP头+UDP头+数据) 55 int mesg_len = make_message(send_message, MAXLINE, inet_addr("192.168.11.104"), 8600, inet_addr("192.168.11.105"), 8686); 56 //将IP数据包发送出去 57 sendto(raw_sockfd, send_message, mesg_len, 0, (struct sockaddr *)&server_address, sizeof(server_address)); 58 close(raw_sockfd); 59 // 60 //下面我们开始接受服务器返回的包 61 int client_sockfd; 62 int server_len; 63 char recv_message[MAXLINE]; 64 struct sockaddr_in server_addr; 65 client_sockfd = socket(AF_INET, SOCK_DGRAM, 0); 66 server_addr.sin_family = AF_INET; 67 server_addr.sin_addr.s_addr = inet_addr("192.168.11.104"); 68 server_addr.sin_port = htons(8600); 69 server_len = sizeof(server_address); 70 bind(client_sockfd, (struct sockaddr *)&server_addr, server_len); 71 72 bzero(&recv_message, sizeof(recv_message)); 73 len = recvfrom(client_sockfd, recv_message, MAXLINE, 0, NULL, NULL); 74 printf("收到的应答:%s ",recv_message); 75 return 0; 76 } 77 78 //拼接IP数据报 79 int make_message(char sendbuf[], int send_buf_len, uint32_t src_ip, u_int16_t src_port, uint32_t des_ip, u_int16_t des_port) 80 { 81 char message[1005]; 82 bzero(message, sizeof(message)); 83 strcpy(message,"hello,world!"); 84 printf("message len:%d ",strlen(message)); 85 struct iphdr *ip; 86 ip = (struct iphdr *)sendbuf; 87 ip->ihl = sizeof(struct iphdr) >> 2; //首部长度 88 ip->version = 4; //ip协议版本 89 ip->tos = 0; //服务类型字段 90 ip->tot_len = 0; //总长度 91 ip->id = 1000; // 92 ip->frag_off = 0; 93 ip->ttl = 128; 94 ip->protocol = IPPROTO_UDP; 95 ip->check = 0; //内核会算相应的效验和 96 ip->saddr = src_ip; 97 ip->daddr = des_ip; 98 99 struct udp_front front; 100 front.srcip = src_ip; 101 front.desip = des_ip; 102 front.len = htons(8+strlen(message)); 103 front.protocol = 17; 104 front.zero = 0; 105 106 struct udphdr *udp; 107 udp = (struct udphdr *)(sendbuf + sizeof(struct iphdr)); 108 udp->source = htons(src_port); //源端口 109 udp->dest = htons(des_port); //目的端口 110 udp->check = 0; //效验和,效验整个udp数据报 111 strcpy((sendbuf+20+8), message); 112 udp->len = htons(8+strlen(message)); //udp数据报总长度 113 114 udp->check = udp_check((sendbuf+20), 8+strlen(message), front); 115 116 ip->tot_len = htons(20 + 8 + strlen(message)); //总长度 117 printf("ip->tot_len:%d ",ip->tot_len); 118 ip->check = in_chksum((unsigned short *)sendbuf, 20); 119 120 return ntohs(ip->tot_len); 121 } 122 123 //计算udp效验和 124 unsigned short udp_check(char *sendbuf, int len, const struct udp_front front) 125 { 126 char str[MAXLINE]; 127 bzero(&str, MAXLINE); 128 bcopy(&front, str, sizeof(front)); 129 bcopy(sendbuf, str+sizeof(front), len); 130 struct udp_front *ptr; 131 ptr = (struct udp_front *)str; 132 char *s; 133 s = (str+20); 134 return in_chksum((unsigned short *)str, sizeof(front)+len); 135 } 136 137 //效验和算法 138 uint16_t in_chksum(uint16_t *addr, int len) 139 { 140 int nleft = len; 141 uint32_t sum = 0; 142 uint16_t *w = addr; 143 uint16_t answer = 0; 144 //把ICMP报头二进制数据以2字节为单位累加起来 145 while (nleft > 1) 146 { 147 sum += *w++; 148 nleft -= 2; 149 } 150 if (nleft == 1) 151 { 152 *(unsigned char *)(&answer) = *(unsigned char *)w; 153 sum += answer; 154 } 155 sum = (sum>>16) + (sum&0xffff); 156 sum += (sum>>16); 157 answer = ~sum; 158 return answer; 159 }
server端:
server端是一个简单的收包服务器,监听8686端口,当有udp数据包到来时,打印信息并返回给client一个信息。
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <stdio.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <unistd.h> 7 #include <stdlib.h> 8 #include <string.h> 9 10 #define HOST_IP "192.168.11.105" 11 #define HOST_PORT 8686 12 13 #define MAXLINE 1024*50 14 15 int main() 16 { 17 int server_sockfd; 18 int server_len, client_len; 19 struct sockaddr_in server_address; 20 struct sockaddr_in client_address; 21 22 server_sockfd = socket(AF_INET, SOCK_DGRAM, 0); 23 24 server_address.sin_family = AF_INET; 25 server_address.sin_addr.s_addr = inet_addr(HOST_IP); 26 server_address.sin_port = htons(HOST_PORT); 27 28 server_len = sizeof(server_address); 29 bind(server_sockfd, (struct sockaddr *)&server_address, server_len); 30 31 for( ; ; ) 32 { 33 int len; 34 char recv_mesg[MAXLINE]; 35 char send_mesg[20]; 36 client_len = sizeof(struct sockaddr_in); 37 printf("server2 waiting! "); 38 len = recvfrom(server_sockfd, recv_mesg, MAXLINE, 0, (struct sockaddr *) &client_address, (socklen_t *) &client_len); 39 printf("收到包的长度为:%d ",len); 40 printf("%s ",recv_mesg); 41 strcpy(send_mesg,"yes"); 42 sendto(server_sockfd, send_mesg, strlen(send_mesg), 0, (struct sockaddr *) &client_address, client_len); //将包发出去 43 } 44 return 0; 45 }