钿稳铆 发表于 2025-6-8 12:24:03

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

目录

[*]题目
[*]解析
[*]代码实现

题目


解析


[*]由于该题需要实现组播通信,所以我们需要将套接字文件句柄设置为组播属性,并将需要通信的用户端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;
        //利用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_inhost_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 = {0};
        struct sockaddr_inclient;
        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 = {0};
       
        struct sockaddr_indest_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;

}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 网络编程练习题---利用UDP协议实现组播通信