Linux下使用RAW SOCKET原始套接字构造UDP原始数据帧广播到局域网,在局域网的另一台计算机上显示UDP发送的信息

54 篇文章 0 订阅
36 篇文章 0 订阅
因为使用IEC61850需要直接访问以太网数据链路层,因此需要做一些访问数据链路层的准备工作。计划使用Linux C构造UDP原始帧在局域网内广播消息,并在另一台电脑上使用QT程序接收和显示这个广播消息。

网上关于使用RAW SOCKET构造原始帧的资料很少,在外国的一个网站上找到了一份源码,经过改造后可以再局域网内广播UDP了。 在此之前遇到了很多问题,在此总结:

(1)使用wireshark可以抓取到UDP数据帧,但应用层的QT程序就是不能读到这个UDP中的数据。经测试,这可能是因为UDP段的校验和错误,wireshark并不会报错,但该UDP无法被应用层的程序接收(经过本人测试)。

(2)以为identification(ID)的值对于发送只有一个数据帧的UDP会产生影响。经测试,如果UDP只有一个数据帧,则identification可以取任意值,不会影响UDP消息的正确接收。

(3)构造UDP原始帧的Linux C程序和接收UDP消息的QT程序若放在同一台计算机下测试,则无法接收到广播的UDP消息;若将两个程序分别放在两台不同的计算机下运行,则UDP广播消息可被正确地接收。(本人分析的原因:本机上发送的UDP广播可能不会广播给自身,从而导致了在同一台计算机上不能正确接收)

(4)为何wireshark抓到的UDP原始帧中最后几位为"00",wireshark显示其属于trailer。在以太网中,规定最小的数据包为64字节(和碰撞检测有关),如果数据包不足64字节,则会由网卡填充,所以显示到后面几位就是"00"。

(5)以太网帧的最小长度到底是多少?不是64字节么?为什么看到了42字节的arp包?  是64字节,你用wireshark抓到的包是把最后4个字节的FCS丢掉的结果,在没有达到64字节时,网卡驱动会自动填充到64字节。楼主看到的42字节,可能是wireshark做了处理,去掉了填充部分。

在这个过程中其实遇到了很多问题,就不一一列举了,再好的文字都不如代码来的实在,下面是一些源码:

 

【1】接收UDP消息的QT程序

[cpp]  view plain copy
  1. /** 
  2. *这个程序功能很简单:监听6666端口的UDP消息,如果接收到消息则显示。 
  3. */  
  4.   
  5. #include "mainwindow.h"  
  6. #include "ui_mainwindow.h"  
  7.   
  8. MainWindow::MainWindow(QWidget *parent) :  
  9.     QMainWindow(parent),  
  10.     ui(new Ui::MainWindow)  
  11. {  
  12.     ui->setupUi(this);  
  13.     socket=new QUdpSocket(this);  
  14.     connect(ui->startButton,SIGNAL(clicked()),this,SLOT(startDo()));  
  15.     connect(ui->clearButton,SIGNAL(clicked()),ui->historyTextEdit,SLOT(clear()));  
  16.     connect(ui->quitButton,SIGNAL(clicked()),this,SLOT(quitDo()));  
  17. }  
  18.   
  19. MainWindow::~MainWindow()  
  20. {  
  21.     delete ui;  
  22. }  
  23.   
  24. bool MainWindow::validatePort(QString port)  
  25. {  
  26.     bool ok=false;  
  27.     int tmp_port=port.toInt(&ok,10);  
  28.     if(ok){  
  29.         if((tmp_port>1024)&&(tmp_port<65536)){  
  30.             return true;  
  31.         }else{  
  32.             return false;  
  33.         }  
  34.     }else{  
  35.         return false;  
  36.     }  
  37. }  
  38.   
  39. //初始化程序UDP相关的部分  
  40. void MainWindow::startDo()  
  41. {  
  42.     QString port=ui->portEdit->text();  
  43.     bool bo=validatePort(port);  
  44.     if(bo && (port != "")){  
  45.         bool ok=socket->bind((quint16)port.toInt());  
  46.         if(ok){  
  47.            ui->historyTextEdit->append("bind indicated port!");  
  48.         }else{  
  49.             ui->historyTextEdit->append("bind error!");  
  50.         }  
  51.     }else{  
  52.         bool ok=socket->bind(DEFAULT_PORT);  
  53.         if(ok){  
  54.             ui->historyTextEdit->append("bind default port!");  
  55.         }else{  
  56.             ui->historyTextEdit->append("bind error!");  
  57.         }  
  58.     }  
  59.     connect(socket,SIGNAL(readyRead()),this,SLOT(readData()));  
  60.     ui->startButton->setEnabled(false);  
  61. }  
  62.   
  63. //读取UDP数据报中的信息  
  64. void MainWindow::readData()  
  65. {  
  66.     while(socket->hasPendingDatagrams()){  
  67.         QByteArray arr;  
  68.         arr.resize(socket->pendingDatagramSize());  
  69.         socket->readDatagram(arr.data(),arr.size());  
  70.         ui->historyTextEdit->append(arr);  
  71.     }  
  72. }  
  73.   
  74. void MainWindow::quitDo()  
  75. {  
  76.     exit(0);  
  77. }  


通过了测试的Linux C UDP原始帧构造程序有两个,便于参考。

【2】Linux C UDP构造参考程序一

[cpp]  view plain copy
  1. /** 
  2. *功能:构造UDP 原始帧,并使用Linux原始套接字发送到局域网中。 
  3. *UDP数据帧中包含了一个测试用的字符串“123456789” 
  4. **/  
  5. #include <sys/socket.h>  
  6. #include <netinet/in.h>  
  7. #include <netinet/if_ether.h>  
  8. #include <netinet/ip.h>  
  9. #include <linux/if_packet.h>  
  10. #include <linux/if.h>  
  11. #include <linux/sockios.h>  
  12. #include <stdio.h>  
  13. #include <unistd.h>  
  14.   
  15. int main(void)  
  16. {  
  17.     int fd=0;  
  18.     char buf[256]={0};  
  19.     struct sockaddr_ll dest;  
  20.     int destlen=0;  
  21.     int ret=0;  
  22.     struct ifreq ifstruct;  
  23.     fd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));  
  24.     if(fd<0){  
  25.         printf("ERR:socket was failed.\n");  
  26.         return -1;  
  27.     }  
  28.       
  29.     memset((char *)&dest, 0x00,sizeof(dest));  
  30.     destlen=sizeof(dest);  
  31.     strcpy(ifstruct.ifr_name, "eth0");  
  32.     ioctl(fd, SIOCGIFINDEX, &ifstruct);  
  33.     dest.sll_ifindex=ifstruct.ifr_ifindex;  
  34.       
  35.     dest.sll_family=AF_PACKET;  
  36.         unsigned char my_mac[]={0x08,0x00,0x27,0x93,0x68,0xe5};  
  37.     memcpy(dest.sll_addr,my_mac,6);   
  38.     dest.sll_halen=ETH_ALEN;  
  39.       
  40.     memset(buf,0x00,sizeof(buf));  
  41.     int j=0;  
  42.         buf[0]=0xff;  
  43.     buf[1]=0xff;  
  44.     buf[2]=0xff;  
  45.     buf[3]=0xff;  
  46.     buf[4]=0xff;      
  47.     buf[5]=0xff;  
  48.     buf[6]=0x00;  
  49.     buf[7]=0x11;  
  50.     buf[8]=0x22;  
  51.     buf[9]=0x33;  
  52.     buf[10]=0x00;  
  53.     buf[11]=0x00;  
  54.     buf[12]=0x08;  
  55.     buf[13]=0x00;   //protocal field  
  56.         buf[14]=0x45;  
  57.     buf[15]=0x00;  
  58.     buf[16]=0x00;  
  59.     buf[17]=0x25;  
  60.     buf[18]=0x00;  
  61.     buf[19]=0x00;  
  62.     buf[20]=0x40;  
  63.     buf[21]=0x00;  
  64.     buf[22]=0x40;  
  65.     buf[23]=0x11;  
  66.     buf[24]=0x76;  
  67.     buf[25]=0x4f;  
  68.     buf[26]=0xc0;     
  69.     buf[27]=0xa8;  
  70.     buf[28]=0x03;  
  71.     buf[29]=0xd1;  
  72.     buf[30]=0xff;  
  73.     buf[31]=0xff;  
  74.     buf[32]=0xff;  
  75.     buf[33]=0xff;  
  76.     buf[34]=0x9e;  
  77.     buf[35]=0xae;  
  78.     buf[36]=0x1a;  
  79.         buf[37]=0x0a;  
  80.     buf[38]=0x00;  
  81.     buf[39]=0x11;  
  82.     buf[40]=0x78;  
  83.     buf[41]=0xc5;  
  84.     buf[42]=0x31;  
  85.     buf[43]=0x32;  
  86.     buf[44]=0x33;  
  87.     buf[45]=0x34;  
  88.     buf[46]=0x35;  
  89.     buf[47]=0x36;  
  90.     buf[48]=0x37;  
  91.     buf[49]=0x38;  
  92.     buf[50]=0x39;  
  93.   
  94.     int ii=0;  
  95.     while(1){  
  96.         ret=sendto(fd,buf,51,0,(struct sockaddr *)&dest,destlen);   //将构造的UDP帧发送到数据链路层  
  97.         if(ret<0){  
  98.             perror("error");  
  99.         }  
  100.         printf("%d\n",ii);  
  101.         ii++;  
  102.             sleep(1);  
  103.     }  
  104.         close(fd);  
  105.         printf("finished!\n");  
  106.         return 0;  
  107. }  

运行截图:

UDP发送端

UDP接收端

 


 

【3】Linux C UDP构造参考程序二

[cpp]  view plain copy
  1. /*  Copyright (C) 2011-2013  P.D. Buchan (pdbuchan@yahoo.com) 
  2.  
  3.     This program is free software: you can redistribute it and/or modify 
  4.     it under the terms of the GNU General Public License as published by 
  5.     the Free Software Foundation, either version 3 of the License, or 
  6.     (at your option) any later version. 
  7.  
  8.     This program is distributed in the hope that it will be useful, 
  9.     but WITHOUT ANY WARRANTY; without even the implied warranty of 
  10.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  11.     GNU General Public License for more details. 
  12.  
  13.     You should have received a copy of the GNU General Public License 
  14.     along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  15. */  
  16.   
  17. // Send an IPv4 UDP packet via raw socket at the link layer (ethernet frame).  
  18. // Need to have destination MAC address.  
  19. // Includes some UDP data.  
  20.   
  21. #include <stdio.h>  
  22. #include <stdlib.h>  
  23. #include <unistd.h>           // close()  
  24. #include <string.h>           // strcpy, memset(), and memcpy()  
  25.   
  26. #include <netdb.h>            // struct addrinfo  
  27. #include <sys/types.h>        // needed for socket(), uint8_t, uint16_t, uint32_t  
  28. #include <sys/socket.h>       // needed for socket()  
  29. #include <netinet/in.h>       // IPPROTO_UDP, INET_ADDRSTRLEN  
  30. #include <netinet/ip.h>       // struct ip and IP_MAXPACKET (which is 65535)  
  31. #include <netinet/udp.h>      // struct udphdr  
  32. #include <arpa/inet.h>        // inet_pton() and inet_ntop()  
  33. #include <sys/ioctl.h>        // macro ioctl is defined  
  34. #include <bits/ioctls.h>      // defines values for argument "request" of ioctl.  
  35. #include <net/if.h>           // struct ifreq  
  36. #include <linux/if_ether.h>   // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD  
  37. #include <linux/if_packet.h>  // struct sockaddr_ll (see man 7 packet)  
  38. #include <net/ethernet.h>  
  39.   
  40. #include <errno.h>            // errno, perror()  
  41.   
  42. // Define some constants.  
  43. #define ETH_HDRLEN 14  // Ethernet header length  
  44. #define IP4_HDRLEN 20  // IPv4 header length  
  45. #define UDP_HDRLEN  8  // UDP header length, excludes data  
  46.   
  47. #define SEV_PORT 6666  
  48.   
  49. // Function prototypes  
  50. uint16_t checksum (uint16_t *, int);  
  51. uint16_t udp4_checksum (struct ip, struct udphdr, uint8_t *, int);  
  52. char *allocate_strmem (int);  
  53. uint8_t *allocate_ustrmem (int);  
  54. int *allocate_intmem (int);  
  55.   
  56. int  
  57. main (int argc, char **argv)  
  58. {  
  59. int temp_i=0;  
  60.   
  61. for(temp_i=0;temp_i<40;temp_i++)  
  62. {  
  63.   int i, status, datalen, frame_length, sd, bytes, *ip_flags; char *interface, *target, *src_ip, *dst_ip;  
  64.   struct ip iphdr;  
  65.   struct udphdr udphdr;  
  66.   uint8_t *data, *src_mac, *dst_mac, *ether_frame;  
  67.   struct addrinfo hints, *res;  
  68.   struct sockaddr_in *ipv4;  
  69.   struct sockaddr_ll device;  
  70.   struct ifreq ifr;  
  71.   void *tmp;  
  72.   
  73.   // Allocate memory for various arrays.  
  74.   src_mac = allocate_ustrmem (6);  
  75.   dst_mac = allocate_ustrmem (6);  
  76.   data = allocate_ustrmem (IP_MAXPACKET);  
  77.   ether_frame = allocate_ustrmem (IP_MAXPACKET);  
  78.   interface = allocate_strmem (40);  
  79.   target = allocate_strmem (40);  
  80.   src_ip = allocate_strmem (INET_ADDRSTRLEN);  
  81.   dst_ip = allocate_strmem (INET_ADDRSTRLEN);  
  82.   ip_flags = allocate_intmem (4);  
  83.   
  84.   // Interface to send packet through.  
  85.   strcpy (interface, "eth0");  
  86.   
  87.   // Submit request for a socket descriptor to look up interface.  
  88.   if ((sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) {  
  89.     perror ("socket() failed to get socket descriptor for using ioctl() ");  
  90.     exit (EXIT_FAILURE);  
  91.   }  
  92.   
  93.   // Use ioctl() to look up interface name and get its MAC address.  
  94.   memset (&ifr, 0, sizeof (ifr));  
  95.   snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);  
  96.   if (ioctl (sd, SIOCGIFHWADDR, &ifr) < 0) {  
  97.     perror ("ioctl() failed to get source MAC address ");  
  98.     return (EXIT_FAILURE);  
  99.   }  
  100.   close (sd);  
  101.   
  102.   // Copy source MAC address.  
  103.   memcpy (src_mac, ifr.ifr_hwaddr.sa_data, 6 * sizeof (uint8_t));  
  104.   
  105.   // Report source MAC address to stdout.  
  106.   printf ("MAC address for interface %s is ", interface);  
  107.   for (i=0; i<5; i++) {  
  108.     printf ("%02x:", src_mac[i]);  
  109.   }  
  110.   printf ("%02x\n", src_mac[5]);  
  111.   
  112.   // Find interface index from interface name and store index in  
  113.   // struct sockaddr_ll device, which will be used as an argument of sendto().  
  114.   memset (&device, 0, sizeof (device));  
  115.   if ((device.sll_ifindex = if_nametoindex (interface)) == 0) {  
  116.     perror ("if_nametoindex() failed to obtain interface index ");  
  117.     exit (EXIT_FAILURE);  
  118.   }  
  119.   printf ("Index for interface %s is %i\n", interface, device.sll_ifindex);  
  120.   
  121.   // Set destination MAC address: you need to fill these out  
  122.   dst_mac[0] = 0xff;  
  123.   dst_mac[1] = 0xff;  
  124.   dst_mac[2] = 0xff;  
  125.   dst_mac[3] = 0xff;  
  126.   dst_mac[4] = 0xff;  
  127.   dst_mac[5] = 0xff;  
  128.   
  129.   // Source IPv4 address: you need to fill this out  
  130.   strcpy (src_ip, "192.168.3.209");  
  131.   
  132.   // Destination URL or IPv4 address: you need to fill this out  
  133.   strcpy (target, "255.255.255.255");  
  134.   
  135.   // Fill out hints for getaddrinfo().  
  136.   memset (&hints, 0, sizeof (struct addrinfo));  
  137.   hints.ai_family = AF_INET;  
  138.   hints.ai_socktype = SOCK_STREAM;  
  139.   hints.ai_flags = hints.ai_flags | AI_CANONNAME;  
  140.   
  141.   // Resolve target using getaddrinfo().  
  142.   if ((status = getaddrinfo (target, NULL, &hints, &res)) != 0) {  
  143.     fprintf (stderr, "getaddrinfo() failed: %s\n", gai_strerror (status));  
  144.     exit (EXIT_FAILURE);  
  145.   }  
  146.   ipv4 = (struct sockaddr_in *) res->ai_addr;  
  147.   tmp = &(ipv4->sin_addr);  
  148.   if (inet_ntop (AF_INET, tmp, dst_ip, INET_ADDRSTRLEN) == NULL) {  
  149.     status = errno;  
  150.     fprintf (stderr, "inet_ntop() failed.\nError message: %s", strerror (status));  
  151.     exit (EXIT_FAILURE);  
  152.   }  
  153.   freeaddrinfo (res);  
  154.   
  155.   // Fill out sockaddr_ll.  
  156.   device.sll_family = AF_PACKET;  
  157.   memcpy (device.sll_addr, src_mac, 6 * sizeof (uint8_t));  
  158.   device.sll_halen = htons (6);  
  159.   
  160.   // UDP data  
  161.   datalen = 9;  
  162.   data[0] = '1';  
  163.   data[1] = '2';  
  164.   data[2] = '3';  
  165.   data[3] = '4';  
  166.   data[4] = '5';  
  167.   data[5] = '6';  
  168.   data[6] = '7';  
  169.   data[7] = '8';  
  170.   data[8] = '9';  
  171.   
  172.   // IPv4 header  
  173.   
  174.   // IPv4 header length (4 bits): Number of 32-bit words in header = 5  
  175.   iphdr.ip_hl = IP4_HDRLEN / sizeof (uint32_t);  
  176.   
  177.   // Internet Protocol version (4 bits): IPv4  
  178.   iphdr.ip_v = 4;  
  179.   
  180.   // Type of service (8 bits)  
  181.   iphdr.ip_tos = 0;  
  182.   
  183.   // Total length of datagram (16 bits): IP header + UDP header + datalen  
  184.   iphdr.ip_len = htons (IP4_HDRLEN + UDP_HDRLEN + datalen);  
  185.   
  186.   // ID sequence number (16 bits): unused, since single datagram  
  187.   //iphdr.ip_id = htons (0x2e20+temp_i);  
  188.   iphdr.ip_id = htons(0);   //the ip_id can be constant or variable   
  189.   
  190.   // Flags, and Fragmentation offset (3, 13 bits): 0 since single datagram  
  191.   
  192.   // Zero (1 bit)  
  193.   ip_flags[0] = 0;  
  194.   
  195.   // Do not fragment flag (1 bit)  
  196.   ip_flags[1] = 1;  
  197.   
  198.   // More fragments following flag (1 bit)  
  199.   ip_flags[2] = 0;  
  200.   
  201.   // Fragmentation offset (13 bits)  
  202.   ip_flags[3] = 0;  
  203.   
  204.   iphdr.ip_off = htons ((ip_flags[0] << 15)  
  205.                       + (ip_flags[1] << 14)  
  206.                       + (ip_flags[2] << 13)  
  207.                       +  ip_flags[3]);  
  208.   
  209.   // Time-to-Live (8 bits): default to maximum value  
  210.   iphdr.ip_ttl = 64;  
  211.   
  212.   // Transport layer protocol (8 bits): 17 for UDP  
  213.   iphdr.ip_p = IPPROTO_UDP;  
  214.   
  215.   // Source IPv4 address (32 bits)  
  216.   if ((status = inet_pton (AF_INET, src_ip, &(iphdr.ip_src))) != 1) {  
  217.     fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status));  
  218.     exit (EXIT_FAILURE);  
  219.   }  
  220.   
  221.   // Destination IPv4 address (32 bits)  
  222.   if ((status = inet_pton (AF_INET, dst_ip, &(iphdr.ip_dst))) != 1) {  
  223.     fprintf (stderr, "inet_pton() failed.\nError message: %s", strerror (status));  
  224.     exit (EXIT_FAILURE);  
  225.   }  
  226.   
  227.   // IPv4 header checksum (16 bits): set to 0 when calculating checksum  
  228.   iphdr.ip_sum = 0;  
  229.   iphdr.ip_sum = checksum ((uint16_t *) &iphdr, IP4_HDRLEN);  
  230.   
  231.   // UDP header  
  232.   
  233.   // Source port number (16 bits): pick a number  
  234.   udphdr.source = htons (40622);  
  235.   
  236.   // Destination port number (16 bits): pick a number  
  237.   udphdr.dest = htons (SEV_PORT);  
  238.   
  239.   // Length of UDP datagram (16 bits): UDP header + UDP data  
  240.   udphdr.len = htons (UDP_HDRLEN + datalen);  
  241.   
  242.   // UDP checksum (16 bits)  
  243.   udphdr.check = udp4_checksum (iphdr, udphdr, data, datalen);  
  244.   
  245.   // Fill out ethernet frame header.  
  246.   
  247.   // Ethernet frame length = ethernet header (MAC + MAC + ethernet type) + ethernet data (IP header + UDP header + UDP data)  
  248.   frame_length = 6 + 6 + 2 + IP4_HDRLEN + UDP_HDRLEN + datalen;  
  249.   
  250.   // Destination and Source MAC addresses  
  251.   memcpy (ether_frame, dst_mac, 6 * sizeof (uint8_t));  
  252.   memcpy (ether_frame + 6, src_mac, 6 * sizeof (uint8_t));  
  253.   
  254.   // Next is ethernet type code (ETH_P_IP for IPv4).  
  255.   // http://www.iana.org/assignments/ethernet-numbers  
  256.   ether_frame[12] = ETH_P_IP / 256;  
  257.   ether_frame[13] = ETH_P_IP % 256;  
  258.   
  259.   // Next is ethernet frame data (IPv4 header + UDP header + UDP data).  
  260.   
  261.   // IPv4 header  
  262.   memcpy (ether_frame + ETH_HDRLEN, &iphdr, IP4_HDRLEN * sizeof (uint8_t));  
  263.   
  264.   // UDP header  
  265.   memcpy (ether_frame + ETH_HDRLEN + IP4_HDRLEN, &udphdr, UDP_HDRLEN * sizeof (uint8_t));  
  266.   
  267.   // UDP data  
  268.   memcpy (ether_frame + ETH_HDRLEN + IP4_HDRLEN + UDP_HDRLEN, data, datalen * sizeof (uint8_t));  
  269.   
  270.   // Submit request for a raw socket descriptor.  
  271.   if ((sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) {  
  272.     perror ("socket() failed ");  
  273.     exit (EXIT_FAILURE);  
  274.   }  
  275.   
  276.   
  277.   
  278.       // Send ethernet frame to socket.  
  279.       if ((bytes = sendto (sd, ether_frame, frame_length, 0, (struct sockaddr *) &device, sizeof (device))) <= 0) {  
  280.         perror ("sendto() failed");  
  281.         exit (EXIT_FAILURE);  
  282.       }  
  283.   
  284.   // Close socket descriptor.  
  285.   close (sd);  
  286.   
  287.   // Free allocated memory.  
  288.   free (src_mac);  
  289.   free (dst_mac);  
  290.   free (data);  
  291.   free (ether_frame);  
  292.   free (interface);  
  293.   free (target);  
  294.   free (src_ip);  
  295.   free (dst_ip);  
  296.   free (ip_flags);  
  297.   sleep(1);  
  298.   printf("%d\n",temp_i);  
  299. }  
  300.   
  301.   return (EXIT_SUCCESS);  
  302. }  
  303.   
  304. // Checksum function  
  305. uint16_t  
  306. checksum (uint16_t *addr, int len)  
  307. {  
  308.   int nleft = len;  
  309.   int sum = 0;  
  310.   uint16_t *w = addr;  
  311.   uint16_t answer = 0;  
  312.   
  313.   while (nleft > 1) {  
  314.     sum += *w++;  
  315.     nleft -= sizeof (uint16_t);  
  316.   }  
  317.   
  318.   if (nleft == 1) {  
  319.     *(uint8_t *) (&answer) = *(uint8_t *) w;  
  320.     sum += answer;  
  321.   }  
  322.   
  323.   sum = (sum >> 16) + (sum & 0xFFFF);  
  324.   sum += (sum >> 16);  
  325.   answer = ~sum;  
  326.   return (answer);  
  327. }  
  328.   
  329. // Build IPv4 UDP pseudo-header and call checksum function.  
  330. uint16_t  
  331. udp4_checksum (struct ip iphdr, struct udphdr udphdr, uint8_t *payload, int payloadlen)  
  332. {  
  333.   char buf[IP_MAXPACKET];  
  334.   char *ptr;  
  335.   int chksumlen = 0;  
  336.   int i;  
  337.   
  338.   ptr = &buf[0];  // ptr points to beginning of buffer buf  
  339.   
  340.   // Copy source IP address into buf (32 bits)  
  341.   memcpy (ptr, &iphdr.ip_src.s_addr, sizeof (iphdr.ip_src.s_addr));  
  342.   ptr += sizeof (iphdr.ip_src.s_addr);  
  343.   chksumlen += sizeof (iphdr.ip_src.s_addr);  
  344.   
  345.   // Copy destination IP address into buf (32 bits)  
  346.   memcpy (ptr, &iphdr.ip_dst.s_addr, sizeof (iphdr.ip_dst.s_addr));  
  347.   ptr += sizeof (iphdr.ip_dst.s_addr);  
  348.   chksumlen += sizeof (iphdr.ip_dst.s_addr);  
  349.   
  350.   // Copy zero field to buf (8 bits)  
  351.   *ptr = 0; ptr++;  
  352.   chksumlen += 1;  
  353.   
  354.   // Copy transport layer protocol to buf (8 bits)  
  355.   memcpy (ptr, &iphdr.ip_p, sizeof (iphdr.ip_p));  
  356.   ptr += sizeof (iphdr.ip_p);  
  357.   chksumlen += sizeof (iphdr.ip_p);  
  358.   
  359.   // Copy UDP length to buf (16 bits)  
  360.   memcpy (ptr, &udphdr.len, sizeof (udphdr.len));  
  361.   ptr += sizeof (udphdr.len);  
  362.   chksumlen += sizeof (udphdr.len);  
  363.   
  364.   // Copy UDP source port to buf (16 bits)  
  365.   memcpy (ptr, &udphdr.source, sizeof (udphdr.source));  
  366.   ptr += sizeof (udphdr.source);  
  367.   chksumlen += sizeof (udphdr.source);  
  368.   
  369.   // Copy UDP destination port to buf (16 bits)  
  370.   memcpy (ptr, &udphdr.dest, sizeof (udphdr.dest));  
  371.   ptr += sizeof (udphdr.dest);  
  372.   chksumlen += sizeof (udphdr.dest);  
  373.   
  374.   // Copy UDP length again to buf (16 bits)  
  375.   memcpy (ptr, &udphdr.len, sizeof (udphdr.len));  
  376.   ptr += sizeof (udphdr.len);  
  377.   chksumlen += sizeof (udphdr.len);  
  378.   
  379.   // Copy UDP checksum to buf (16 bits)  
  380.   // Zero, since we don't know it yet  
  381.   *ptr = 0; ptr++;  
  382.   *ptr = 0; ptr++;  
  383.   chksumlen += 2;  
  384.   
  385.   // Copy payload to buf  
  386.   memcpy (ptr, payload, payloadlen);  
  387.   ptr += payloadlen;  
  388.   chksumlen += payloadlen;  
  389.   
  390.   // Pad to the next 16-bit boundary  
  391.   for (i=0; i<payloadlen%2; i++, ptr++) {  
  392.     *ptr = 0;  
  393.     ptr++;  
  394.     chksumlen++;  
  395.   }  
  396.   
  397.   return checksum ((uint16_t *) buf, chksumlen);  
  398. }  
  399.   
  400. // Allocate memory for an array of chars.  
  401. char *  
  402. allocate_strmem (int len)  
  403. {  
  404.   void *tmp;  
  405.   
  406.   if (len <= 0) {  
  407.     fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_strmem().\n", len);  
  408.     exit (EXIT_FAILURE);  
  409.   }  
  410.   
  411.   tmp = (char *) malloc (len * sizeof (char));  
  412.   if (tmp != NULL) {  
  413.     memset (tmp, 0, len * sizeof (char));  
  414.     return (tmp);  
  415.   } else {  
  416.     fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_strmem().\n");  
  417.     exit (EXIT_FAILURE);  
  418.   }  
  419. }  
  420.   
  421. // Allocate memory for an array of unsigned chars.  
  422. uint8_t *  
  423. allocate_ustrmem (int len)  
  424. {  
  425.   void *tmp;  
  426.   
  427.   if (len <= 0) {  
  428.     fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_ustrmem().\n", len);  
  429.     exit (EXIT_FAILURE);  
  430.   }  
  431.   
  432.   tmp = (uint8_t *) malloc (len * sizeof (uint8_t));  
  433.   if (tmp != NULL) {  
  434.     memset (tmp, 0, len * sizeof (uint8_t));  
  435.     return (tmp);  
  436.   } else {  
  437.     fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_ustrmem().\n");  
  438.     exit (EXIT_FAILURE);  
  439.   }  
  440. }  
  441.   
  442. // Allocate memory for an array of ints.  
  443. int *  
  444. allocate_intmem (int len)  
  445. {  
  446.   void *tmp;  
  447.   
  448.   if (len <= 0) {  
  449.     fprintf (stderr, "ERROR: Cannot allocate memory because len = %i in allocate_intmem().\n", len);  
  450.     exit (EXIT_FAILURE);  
  451.   }  
  452.   
  453.   tmp = (int *) malloc (len * sizeof (int));  
  454.   if (tmp != NULL) {  
  455.     memset (tmp, 0, len * sizeof (int));  
  456.     return (tmp);  
  457.   } else {  
  458.     fprintf (stderr, "ERROR: Cannot allocate memory for array allocate_intmem().\n");  
  459.     exit (EXIT_FAILURE);  
  460.   }  
  461. }  


运行效果:

UDP发送端

UDP接收端

所有有参考价值的程序都在上面了,当然还有很多不明白的地方,一时搞不明白,以后遇到了再深入研究吧。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QUdpSocket 是 C++ Qt 框架中的一个类,用于实现 UDP(User Datagram Protocol)套接字通信。UDP 是一种无连接的、不可靠的传输协议,适用于对数据传输延迟要求较低、可容忍丢失或乱序的场景。 QUdpSocket 类提供了发送和接收 UDP 数据报的方法,并支持多主机之间的通信。它可以在客户端和服务器之间进行双向通信,也可以用于广播和多播的应用。 可以使用 QUdpSocket 类的实例来创建一个 UDP 套接字,并通过调用相应的函数来发送和接收数据报。常用的函数包括 bind() 绑定本地地址和端口、readDatagram() 读取数据报、writeDatagram() 发送数据报等。 以下是一个使用 QUdpSocket 类实现简单 UDP 通信的示例代码: ```cpp #include <QtNetwork> int main() { // 创建 QUdpSocket 对象 QUdpSocket udpSocket; // 绑定本地地址和端口 udpSocket.bind(QHostAddress::LocalHost, 12345); // 发送数据报 QByteArray datagram = "Hello, World!"; udpSocket.writeDatagram(datagram, QHostAddress::LocalHost, 6789); // 接收数据报 while (udpSocket.hasPendingDatagrams()) { QByteArray receivedDatagram; receivedDatagram.resize(udpSocket.pendingDatagramSize()); udpSocket.readDatagram(receivedDatagram.data(), receivedDatagram.size()); qDebug() << "Received datagram:" << receivedDatagram; } return 0; } ``` 以上代码创建了一个 UDP 套接字对象 udpSocket,并将其绑定到本地地址和端口 12345。然后使用 writeDatagram() 函数发送数据报给本地地址和端口 6789。接下来使用 readDatagram() 函数接收数据报,并打印在控制台上。 这只是一个简单的示例,QUdpSocket 类还提供了许多其他功能和函数,可以根据需求进行更复杂的 UDP 通信实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值