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

cnblogs 2024-06-10 12:45:00 阅读 71

利用UDP的面向无连接特性,实现一个组之间的通信

目录

  • 题目
  • 解析
  • 代码实现

题目

image

解析

  1. 由于该题需要实现组播通信,所以我们需要将套接字文件句柄设置为组播属性,并将需要通信的用户端IP地址,加入组中。
  2. 由于组播通信需要实现一对多发送消息,所以还需要将套接字文件句柄的广播属性一并开启。
  3. 由于该题实现过程使用到了线程相关函数接口,所以编译时需要带上 “-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 <arpa/inet.h>

#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地址

#defineIP_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;

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。