一.概述
同上一篇tcp一样,udp也是封装在ip报文里面。创建UDP的原始套接字如下:
(sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
同样,如果要构造udp的ip首部,要开启IP_HDRINCL选项!
udp首部格式:
udp的不可靠性,比tcp报文简单很多。上面的16位UDP长度是UDP首部+普通数据的总长度,这点跟ip首部的16位总长度一样!
udp结构定义在netinet/udp.h
struct udphdr { __extension__ union { struct { u_int16_t uh_sport; /* source port */ u_int16_t uh_dport; /* destination port */ u_int16_t uh_ulen; /* udp length */ u_int16_t uh_sum; /* udp checksum */ }; struct { u_int16_t source; u_int16_t dest; u_int16_t len; u_int16_t check; }; }; };
二.构造IP_UDP报文发送
/** * @file ip_udp_send.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <netinet/udp.h> /* ip首部长度 */ #define IP_HEADER_LEN sizeof(struct ip) /* udp首部长度 */ #define UDP_HEADER_LEN sizeof(struct udphdr) /* ip首部 + udp首部长度 */ #define IP_UDP_HEADER_LEN IP_HEADER_LEN + UDP_HEADER_LEN void err_exit(const char *err_msg) { perror(err_msg); exit(); } /* 填充ip首部 */ struct ip *fill_ip_header(const char *src_ip, const char *dst_ip, int ip_packet_len) { struct ip *ip_header; ip_header = (struct ip *)malloc(IP_HEADER_LEN); ip_header->ip_v = IPVERSION; ip_header->ip_hl = IP_HEADER_LEN / ; ip_header->ip_tos = ; ip_header->ip_len = htons(ip_packet_len); ip_header->ip_id = ; ip_header->ip_off = ; ip_header->ip_ttl = MAXTTL; ip_header->ip_p = IPPROTO_UDP; /* 这里是UDP */ ip_header->ip_sum = ; ip_header->ip_src.s_addr = inet_addr(src_ip); ip_header->ip_dst.s_addr = inet_addr(dst_ip); return ip_header; } /* 填充udp首部 */ struct udphdr *fill_udp_header(int src_port, int dst_port, int udp_packet_len) { struct udphdr *udp_header; udp_header = (struct udphdr *)malloc(UDP_HEADER_LEN); udp_header->source = htons(src_port); udp_header->dest = htons(dst_port); /* 这里的长度是整个UDP报文 */ udp_header->len = htons(udp_packet_len); udp_header->check = ; return udp_header; } /* 发送ip_udp报文 */ void ip_udp_send(const char *src_ip, int src_port, const char *dst_ip, int dst_port, const char *data) { struct ip *ip_header; struct udphdr *udp_header; struct sockaddr_in dst_addr; socklen_t sock_addrlen = sizeof(struct sockaddr_in); int data_len = strlen(data); int ip_packet_len = IP_UDP_HEADER_LEN + data_len; int udp_packet_len = UDP_HEADER_LEN + data_len; char buf[ip_packet_len]; ; bzero(&dst_addr, sock_addrlen); dst_addr.sin_family = PF_INET; dst_addr.sin_addr.s_addr = inet_addr(dst_ip); dst_addr.sin_port = htons(dst_port); /* 创建udp原始套接字 */ ) err_exit("socket()"); /* 开启IP_HDRINCL,自定义IP首部 */ ) err_exit("setsockopt()"); /* ip首部 */ ip_header = fill_ip_header(src_ip, dst_ip, ip_packet_len); /* udp首部 */ udp_header = fill_udp_header(src_port, dst_port, udp_packet_len); bzero(buf, ip_packet_len); memcpy(buf, ip_header, IP_HEADER_LEN); memcpy(buf + IP_HEADER_LEN, udp_header, UDP_HEADER_LEN); memcpy(buf + IP_UDP_HEADER_LEN, data, data_len); /* 发送报文 */ ret_len = sendto(sockfd, buf, ip_packet_len, , (struct sockaddr *)&dst_addr, sock_addrlen); ) printf("sendto() ok!!!\n"); else printf("sendto() failed\n"); close(sockfd); free(ip_header); free(udp_header); } int main(int argc, const char *argv[]) { ) { printf(]); exit(); } /* 发送ip_udp报文 */ ip_udp_send(argv[], atoi(argv[]), argv[], atoi(argv[]), argv[]); ; }
上面的大部分代码都跟上一篇差不多。不同的是这次是填充udp首部,创建原始套接字的类型是UDP。