zoukankan      html  css  js  c++  java
  • 004.UDP--拼接UDP数据包,构造ip头和udp头通信(使用原始套接字)

    一.大致流程:

        建立一个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 }
    server.c
  • 相关阅读:
    js伪数组转数组
    前端解决跨域几种方式
    mac 下node,yarn安装及版本切换
    如何给一个数组对象去重
    cookie、session、sessionStorage 、localStorage 区别
    moment.js 时间戳转换
    tp框架,addAll方法报错,返回false
    js获得 json对象的个数(长度)
    php 魔术方法和魔术常量
    js 对象的创建方式和对象的区别
  • 原文地址:https://www.cnblogs.com/ruo-yu/p/4979020.html
Copyright © 2011-2022 走看看