找回密码
 立即注册
首页 业界区 安全 网络编程练习题---利用UDP协议实现组播通信 ...

网络编程练习题---利用UDP协议实现组播通信

钿稳铆 2025-6-8 12:24:03
目录

  • 题目
  • 解析
  • 代码实现

题目

1.png

解析


  • 由于该题需要实现组播通信,所以我们需要将套接字文件句柄设置为组播属性,并将需要通信的用户端IP地址,加入组中。
  • 由于组播通信需要实现一对多发送消息,所以还需要将套接字文件句柄的广播属性一并开启。
  • 由于该题实现过程使用到了线程相关函数接口,所以编译时需要带上 “-pthread“ 选项
代码实现
  1. /********************************************************************************
  2. *
  3. *        file name:        udp_group.c
  4. *        author         :  ProgramMonkey_Fly@126.com
  5. *        date         :  2024/06/04
  6. *        function :  该案例是掌握UDP协议的组播通信方式
  7. *         note         :  
  8. *                                想要实现组间通信,只需要对本地地址进行修改后,可实现组间通信,即一个用户
  9. *                                发送的消息,组间所有用户都能收到
  10. *   version  :
  11. *
  12. *        CopyRight (c)  2023-2024   ProgramMonkey_Fly@126.com   All Right Reseverd
  13. *
  14. * ******************************************************************************/
  15. /****************************头文件*********************************************/
  16. #include <sys/socket.h>
  17. #include <stdio.h>
  18. #include <errno.h>
  19. #include <netinet/ip.h>
  20. #include
  21. #include <netinet/in.h>
  22. #include <netinet/udp.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <pthread.h>
  26. #include <time.h>
  27. /****************************全局变量*********************************************/
  28. int udp_socket;                                                                //用于存储套接字文件的句柄
  29. /****************************宏*********************************************/
  30. #define PORT           60000                                         //用户端运行进程的端口号
  31. #define IP_HOST   "192.168.64.94"                        //用户端本地IP地址
  32. #define        IP_GROUP  "226.66.66.66"                        //组播通信IP地址
  33. /********************************************************************************
  34. *
  35. *        name         :        recv_time
  36. *        function :  实现获取当前系统时间,并对获取值进行固定格式的处理,获得更好的显示效果
  37. *        param :  
  38. *                                none
  39. *                               
  40. *        retval         :  调用成功返回处理后的当前系统时间
  41. *        author         :  ProgramMonkey_Fly@126.com
  42. *        date         :  2024/06/04
  43. *         note         :
  44. *                                输出时间的格式固定为:"X年 X月 X日 星期X XX:XX:XX",可根据需要进行修改
  45. *   version  :
  46. *        
  47. * *****************************************************************************/
  48. char *recv_time()
  49. {
  50.         //定义一个字符指针,用于存储获取的当前系统时间值
  51.         char *buf =(char *)calloc(1,128);
  52.         //定义一个字符数组,用于优化后的星期值
  53.         char wekday[10];
  54.         //利用time()获取当前系统时间,并将返回值存储起来
  55.         time_t systime = time(NULL);
  56.         //利用localtime()对获取值进行处理,并将处理后的数据写入目标文件中
  57.         struct tm *systimep = localtime(&systime);
  58.         systimep->tm_year += 1900;
  59.         systimep->tm_mon += 1;
  60.         //对获取星期数值进行判断,并进行美观优化
  61.         if(systimep->tm_wday == 1)
  62.                 {
  63.                         strcpy(wekday, "一");
  64.                 }
  65.                 else if(systimep->tm_wday == 2)
  66.                 {
  67.                         strcpy(wekday, "二");
  68.                 }
  69.                 else if(systimep->tm_wday == 3)
  70.                 {
  71.                         strcpy(wekday, "三");
  72.                 }
  73.                 else if(systimep->tm_wday == 4)
  74.                 {
  75.                         strcpy(wekday, "四");
  76.                 }
  77.                 else if(systimep->tm_wday == 5)
  78.                 {
  79.                         strcpy(wekday, "五");
  80.                 }
  81.                 else if(systimep->tm_wday == 6)
  82.                 {
  83.                         strcpy(wekday, "六");
  84.                 }
  85.                 else if(systimep->tm_wday == 7)
  86.                 {
  87.                         strcpy(wekday, "日");
  88.                 }
  89.                 //将获取值按固定格式写入缓冲区中
  90.                 sprintf(buf, "%d年 %d月 %d日 星期%s %d:%d:%d", systimep->tm_year,
  91.                                 systimep->tm_mon,
  92.                                 systimep->tm_mday,
  93.                                 wekday,
  94.                                 systimep->tm_hour,
  95.                                 systimep->tm_min,
  96.                                 systimep->tm_sec);
  97.                 return buf;
  98. }
  99. /********************************************************************************
  100. *
  101. *        name         :        recv_msg
  102. *        function :  接收信息线程的任务函数
  103. *        param :  
  104. *                                none
  105. *                               
  106. *        retval         :  none
  107. *        author         :  ProgramMonkey_Fly@126.com
  108. *        date         :  2024/06/04
  109. *         note         :  
  110. *                                对应填写的IP地址,可以通过程序开头的宏定义进行修改
  111. *   version  :
  112. *        
  113. * *****************************************************************************/
  114. void *recv_msg(void *arg)
  115. {
  116.         //将该线程的属性设置为分离属性,防止线程结束后变为僵尸线程
  117.         pthread_detach(pthread_self());
  118.         //绑定服务器的端口和地址
  119.         struct sockaddr_in  host_addr;
  120.         host_addr.sin_family                 = AF_INET;                                                 //协议族,UDP协议固定填写该宏定义
  121.         host_addr.sin_port                   = htons(PORT);                                        //想要接收信息进程的端口号
  122.         host_addr.sin_addr.s_addr   = htonl(INADDR_ANY);                        //本地地址,填写该宏定义代表任意网口都可以接收值
  123.         bind(udp_socket,(struct sockaddr *)&host_addr, sizeof(host_addr));
  124.         //调用recvfrom等待接收数据,并且接收客户端的网络信息
  125.         char buf[128] = {0};
  126.         struct sockaddr_in  client;
  127.         socklen_t client_len = sizeof(client);
  128.         //定义一个字符数组用于存储系统时间
  129.         char *recvtime = (char *)calloc(128,1);
  130.         while(1)
  131.         {
  132.                 recvfrom(udp_socket,buf,sizeof(buf), 0 ,(struct sockaddr *)&client,&client_len); //函数标识定义为默认属性,即会阻塞
  133.                 recvtime = recv_time();
  134.                 if(client.sin_addr.s_addr == inet_addr(IP_HOST))
  135.                 {
  136.                         bzero(buf,sizeof(buf));
  137.                         continue;
  138.                        
  139.                 }
  140.                         printf("[%s] (%s): %s\n",inet_ntoa(client.sin_addr),recvtime,buf);
  141.                 bzero(buf,sizeof(buf));
  142.         }
  143. }
  144. /********************************************************************************
  145. *
  146. *        name         :        send_msg
  147. *        function :  发送信息线程的任务函数
  148. *        param :  
  149. *                                none
  150. *                               
  151. *        retval         :  none
  152. *        author         :  ProgramMonkey_Fly@126.com
  153. *        date         :  2024/06/04
  154. *         note         :  
  155. *                                对应填写的IP地址,可以通过程序开头的宏定义进行修改
  156. *   version  :
  157. *        
  158. * *****************************************************************************/
  159. void *send_msg(void *arg)
  160. {
  161.         //将该线程的属性设置为分离属性,防止线程结束后变为僵尸线程
  162.         pthread_detach(pthread_self());
  163.        
  164.         //向目标主机发送消息,需要设置目标端口和目标地址
  165.         char buf[128] = {0};
  166.        
  167.         struct sockaddr_in  dest_addr;
  168.         dest_addr.sin_family                 = AF_INET;                                                 //协议族,是固定的
  169.         dest_addr.sin_port                   = htons(PORT);                                        //服务器运行进程的端口号,必须转换为网络字节序
  170.         dest_addr.sin_addr.s_addr   = inet_addr(IP_GROUP);                        //要发送对象的IP地址,必须转换为点分十进制字符串形式
  171.         while(1)
  172.         {
  173.                 //阻塞等待获取用户从键盘输入的信息
  174.                 scanf("%s", buf);
  175.                 //发送获取到的用户输入信息
  176.                 sendto(udp_socket,buf,strlen(buf),0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
  177.         }
  178.        
  179. }
  180. int main(int argc,char *argv[])
  181. {
  182.        
  183.         //创建UDP套接字,并存储套接字文件的句柄            
  184.         udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
  185.         if (udp_socket == -1)
  186.         {
  187.                 fprintf(stderr, "udp socket error,errno:%d,%s\n",errno,strerror(errno));
  188.                 exit(1);
  189.         }
  190.         //将套接字文件的广播属性设置为开启
  191.         int flag = 1;
  192.         socklen_t flag_len = sizeof(flag);
  193.         setsockopt(udp_socket, SOL_SOCKET, SO_BROADCAST, (void *)&flag, flag_len);
  194.         //将套接字文件的组播属性设置为开启
  195.         struct ip_mreqn ip_grop;
  196.         ip_grop.imr_multiaddr.s_addr = inet_addr(IP_GROUP);//设置组播地址
  197.         ip_grop.imr_address.s_addr = inet_addr(IP_HOST); //设置要加入组的本地地址
  198.         ip_grop.imr_ifindex = 0;                                                                 //设置为默认属性,由系统分配端口
  199.         socklen_t ip_len = sizeof(ip_grop);
  200.         setsockopt(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&ip_grop, ip_len);
  201.        
  202.         //创建两个线程,分别用于发送数据和接受数据
  203.         pthread_t send;
  204.         pthread_t recv;
  205.         pthread_create(&send, NULL, send_msg, NULL);
  206.         pthread_create(&recv, NULL, recv_msg, NULL);
  207.         //关闭主线程
  208.         pthread_exit(NULL);
  209.         return 0;
  210. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册