目录
题目
解析
- 由于该题需要实现组播通信,所以我们需要将套接字文件句柄设置为组播属性,并将需要通信的用户端IP地址,加入组中。
- 由于组播通信需要实现一对多发送消息,所以还需要将套接字文件句柄的广播属性一并开启。
- 由于该题实现过程使用到了线程相关函数接口,所以编译时需要带上 “-pthread“ 选项
代码实现
- /********************************************************************************
- *
- * file name: udp_group.c
- * author : ProgramMonkey_Fly@126.com
- * date : 2024/06/04
- * function : 该案例是掌握UDP协议的组播通信方式
- * note :
- * 想要实现组间通信,只需要对本地地址进行修改后,可实现组间通信,即一个用户
- * 发送的消息,组间所有用户都能收到
- * version :
- *
- * CopyRight (c) 2023-2024 ProgramMonkey_Fly@126.com All Right Reseverd
- *
- * ******************************************************************************/
- /****************************头文件*********************************************/
- #include <sys/socket.h>
- #include <stdio.h>
- #include <errno.h>
- #include <netinet/ip.h>
- #include
- #include <netinet/in.h>
- #include <netinet/udp.h>
- #include <stdlib.h>
- #include <string.h>
- #include <pthread.h>
- #include <time.h>
- /****************************全局变量*********************************************/
- int udp_socket; //用于存储套接字文件的句柄
- /****************************宏*********************************************/
- #define PORT 60000 //用户端运行进程的端口号
- #define IP_HOST "192.168.64.94" //用户端本地IP地址
- #define IP_GROUP "226.66.66.66" //组播通信IP地址
- /********************************************************************************
- *
- * name : recv_time
- * function : 实现获取当前系统时间,并对获取值进行固定格式的处理,获得更好的显示效果
- * param :
- * none
- *
- * retval : 调用成功返回处理后的当前系统时间
- * author : ProgramMonkey_Fly@126.com
- * date : 2024/06/04
- * note :
- * 输出时间的格式固定为:"X年 X月 X日 星期X XX:XX:XX",可根据需要进行修改
- * version :
- *
- * *****************************************************************************/
- char *recv_time()
- {
- //定义一个字符指针,用于存储获取的当前系统时间值
- char *buf =(char *)calloc(1,128);
- //定义一个字符数组,用于优化后的星期值
- char wekday[10];
- //利用time()获取当前系统时间,并将返回值存储起来
- time_t systime = time(NULL);
- //利用localtime()对获取值进行处理,并将处理后的数据写入目标文件中
- struct tm *systimep = localtime(&systime);
- systimep->tm_year += 1900;
- systimep->tm_mon += 1;
- //对获取星期数值进行判断,并进行美观优化
- if(systimep->tm_wday == 1)
- {
- strcpy(wekday, "一");
- }
- else if(systimep->tm_wday == 2)
- {
- strcpy(wekday, "二");
- }
- else if(systimep->tm_wday == 3)
- {
- strcpy(wekday, "三");
- }
- else if(systimep->tm_wday == 4)
- {
- strcpy(wekday, "四");
- }
- else if(systimep->tm_wday == 5)
- {
- strcpy(wekday, "五");
- }
- else if(systimep->tm_wday == 6)
- {
- strcpy(wekday, "六");
- }
- else if(systimep->tm_wday == 7)
- {
- strcpy(wekday, "日");
- }
- //将获取值按固定格式写入缓冲区中
- sprintf(buf, "%d年 %d月 %d日 星期%s %d:%d:%d", systimep->tm_year,
- systimep->tm_mon,
- systimep->tm_mday,
- wekday,
- systimep->tm_hour,
- systimep->tm_min,
- systimep->tm_sec);
- return buf;
- }
- /********************************************************************************
- *
- * name : recv_msg
- * function : 接收信息线程的任务函数
- * param :
- * none
- *
- * retval : none
- * author : ProgramMonkey_Fly@126.com
- * date : 2024/06/04
- * note :
- * 对应填写的IP地址,可以通过程序开头的宏定义进行修改
- * version :
- *
- * *****************************************************************************/
- void *recv_msg(void *arg)
- {
- //将该线程的属性设置为分离属性,防止线程结束后变为僵尸线程
- pthread_detach(pthread_self());
- //绑定服务器的端口和地址
- struct sockaddr_in host_addr;
- host_addr.sin_family = AF_INET; //协议族,UDP协议固定填写该宏定义
- host_addr.sin_port = htons(PORT); //想要接收信息进程的端口号
- host_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址,填写该宏定义代表任意网口都可以接收值
- bind(udp_socket,(struct sockaddr *)&host_addr, sizeof(host_addr));
- //调用recvfrom等待接收数据,并且接收客户端的网络信息
- char buf[128] = {0};
- struct sockaddr_in client;
- socklen_t client_len = sizeof(client);
- //定义一个字符数组用于存储系统时间
- char *recvtime = (char *)calloc(128,1);
- while(1)
- {
- recvfrom(udp_socket,buf,sizeof(buf), 0 ,(struct sockaddr *)&client,&client_len); //函数标识定义为默认属性,即会阻塞
- recvtime = recv_time();
- if(client.sin_addr.s_addr == inet_addr(IP_HOST))
- {
- bzero(buf,sizeof(buf));
- continue;
-
- }
- printf("[%s] (%s): %s\n",inet_ntoa(client.sin_addr),recvtime,buf);
- bzero(buf,sizeof(buf));
- }
- }
- /********************************************************************************
- *
- * name : send_msg
- * function : 发送信息线程的任务函数
- * param :
- * none
- *
- * retval : none
- * author : ProgramMonkey_Fly@126.com
- * date : 2024/06/04
- * note :
- * 对应填写的IP地址,可以通过程序开头的宏定义进行修改
- * version :
- *
- * *****************************************************************************/
- void *send_msg(void *arg)
- {
- //将该线程的属性设置为分离属性,防止线程结束后变为僵尸线程
- pthread_detach(pthread_self());
-
- //向目标主机发送消息,需要设置目标端口和目标地址
- char buf[128] = {0};
-
- struct sockaddr_in dest_addr;
- dest_addr.sin_family = AF_INET; //协议族,是固定的
- dest_addr.sin_port = htons(PORT); //服务器运行进程的端口号,必须转换为网络字节序
- dest_addr.sin_addr.s_addr = inet_addr(IP_GROUP); //要发送对象的IP地址,必须转换为点分十进制字符串形式
- while(1)
- {
- //阻塞等待获取用户从键盘输入的信息
- scanf("%s", buf);
- //发送获取到的用户输入信息
- sendto(udp_socket,buf,strlen(buf),0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
- }
-
- }
- int main(int argc,char *argv[])
- {
-
- //创建UDP套接字,并存储套接字文件的句柄
- udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
- if (udp_socket == -1)
- {
- fprintf(stderr, "udp socket error,errno:%d,%s\n",errno,strerror(errno));
- exit(1);
- }
- //将套接字文件的广播属性设置为开启
- int flag = 1;
- socklen_t flag_len = sizeof(flag);
- setsockopt(udp_socket, SOL_SOCKET, SO_BROADCAST, (void *)&flag, flag_len);
- //将套接字文件的组播属性设置为开启
- struct ip_mreqn ip_grop;
- ip_grop.imr_multiaddr.s_addr = inet_addr(IP_GROUP);//设置组播地址
- ip_grop.imr_address.s_addr = inet_addr(IP_HOST); //设置要加入组的本地地址
- ip_grop.imr_ifindex = 0; //设置为默认属性,由系统分配端口
- socklen_t ip_len = sizeof(ip_grop);
- setsockopt(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&ip_grop, ip_len);
-
- //创建两个线程,分别用于发送数据和接受数据
- pthread_t send;
- pthread_t recv;
- pthread_create(&send, NULL, send_msg, NULL);
- pthread_create(&recv, NULL, recv_msg, NULL);
- //关闭主线程
- pthread_exit(NULL);
- return 0;
- }
复制代码 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |