linux网络编程--广播与组播
广播
前面介绍的数据包发送方式只有一个接收方,称为单播
如果发送给局域网中的所有主机,称为广播
只有用户数据报(使用UDP协议)套接字才能广播
广播地址:
以192.168.1.0(255.255.255.0)网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
发送该地址的数据包被所有主机接收
255.255.255.255在所有网段中都代表广播地址
广播发送
创建用户数据报套接字 (服务器可绑定也可以不绑定,但是接受端必须绑定)
缺省创建的套接字不允许广播数据包,需要设置属性--setsockopt可以设置套接字属性
接收方指定为广播地址
指定端口信息
发送数据包
广播接收:
创建用户数据报套接字
绑定ip地址(广播ip或0.0.0.0/INADDR_ANY,服务器所有网口)和端口(必须绑定,不然无法找到客户端端口)
绑定的端口必须和发送方的指定的端口相同
等待接收数据
实例如下:
send.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define err_log(errlog) do{perror(errlog); exit(1);}while(0) #define N 128 // ./send 192.168.0.255 10000 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in broadcastaddr; struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); char buf[N] = {}; if(argc < 3) { fprintf(stderr, "usage:%s serverip port.\n", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { err_log("fail to socket"); } broadcastaddr.sin_family = AF_INET; broadcastaddr.sin_addr.s_addr = inet_addr(argv[1]); broadcastaddr.sin_port = htons(atoi(argv[2])); int on = 1; if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { err_log("fail to setsockopt"); } while(1) { printf("Input > "); fgets(buf, N, stdin); buf[strlen(buf)-1] = '\0'; if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&broadcastaddr, addrlen) < 0) { err_log("fail to sendto"); } if(strncmp(buf, "quit", 4) == 0) { break; } printf("%s\n", buf); } close(sockfd); return 0; }
recv.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define err_log(errlog) do{perror(errlog); exit(1);}while(0) #define N 128 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in broadcastaddr; struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); char buf[N] = {}; if(argc < 3) { fprintf(stderr, "usage:%s serverip port.\n", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { err_log("fail to socket"); } broadcastaddr.sin_family = AF_INET; broadcastaddr.sin_addr.s_addr = inet_addr(argv[1]);//(IN_ADDRANY/0.0.0.0)都可以 broadcastaddr.sin_port = htons(atoi(argv[2])); if(bind(sockfd, (struct sockaddr*)&broadcastaddr, sizeof(broadcastaddr)) < 0) { err_log("fail to bind"); } while(1) { if(recvfrom(sockfd, buf, N, 0, (struct sockaddr*)&clientaddr, &addrlen) < 0) { err_log("fail to recvfrom"); } printf("From clientaddr:%s\n", buf); strcat(buf, " from server..."); } close(sockfd); return 0; }
编译运行测试
./send 192.168.1.255 10000
分别在两台局域网内的机器上测试
./recv 192.168.1.255 10000 收到
./recv 192.168.1.255 10000 收到
(2)组播(多播)
单播方式只能发送给一个接收方
广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常通信
组播(多播)是一种折中的方式只有加入某个多播组的主机才能收到数据
多播方式既可以发送给多个主机,又能避免广播那样带来带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
ip ad
查看网卡编号
if_nametoindex
根据网卡名字获取编号
组播发送:
创建用户数据报套接字
接收方地址指定为组播地址
指定端口信息
发送数据包
组播接收:
创建用户数据报套接字
加入多播组
绑定ip地址(加入组的组ip或0.0.0.0/INADDR_ANY)和端口---绑定的端口必须和发送方指定的端口相同(必须绑定)
等待接收数据
加入多播组
struct ip_mreq
{
struct in_addr imr_multiaddr;////多播组的IP地址
struct in_addr imr_interface;; //加入的客服端主机IP地址
}
struct ip_mreq req;
bzero(&req,sizeof(req))
req.imr_multiaddr.s_addr = inet_addr(argv[1]);
req.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(sockfd, IPPROTO_IP,IP_ADD_MEMBERSHIP, &req, sizeof(req)) < 0)
{
err_log("fail to setsockopt");
}
send.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define err_log(errlog) do{perror(errlog); exit(1);}while(0) #define N 128 // ./send 192.168.0.255 10000 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in groupcastaddr; struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); char buf[N] = {}; if(argc < 3) { fprintf(stderr, "usage:%s serverip port.\n", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { err_log("fail to socket"); } groupcastaddr.sin_family = AF_INET; groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]); groupcastaddr.sin_port = htons(atoi(argv[2])); while(1) { printf("Input > "); fgets(buf, N, stdin); buf[strlen(buf)-1] = '\0'; if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&groupcastaddr, addrlen) < 0) { err_log("fail to sendto"); } if(strncmp(buf, "quit", 4) == 0) { break; } printf("%s\n", buf); } close(sockfd); return 0; }
进一步正常处理
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <net/if.h> #define SERVER_PORT 8000 #define CLIENT_PORT 9000 #define MAXLINE 1500 #define GROUP "239.0.0.2" int main(void) { int sockfd, i ; struct sockaddr_in serveraddr, clientaddr; char buf[MAXLINE] = "itcast\n"; char ipstr[INET_ADDRSTRLEN]; /* 16 Bytes */ socklen_t clientlen; ssize_t len; struct ip_mreqn group; /* 构造用于UDP通信的套接字*/ sockfd = socket(AF_INET, SOCK_DGRAM, 0); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; /* IPv4 */ serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* 本地任意IP INADDR_ANY = 0 */ serveraddr.sin_port = htons(SERVER_PORT); bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); /*设置组地址*/ inet_pton(AF_INET, GROUP, &group.imr_multiaddr); /*本地任意IP*/ inet_pton(AF_INET, "0.0.0.0", &group.imr_address); /* eth0 --> 编号命令:ip ad */ group.imr_ifindex = if_nametoindex("eth0"); setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group, sizeof(group));//设置组播权限 /*构造client 地址IP+端口*/ bzero(&clientaddr, sizeof(clientaddr)); clientaddr.sin_family = AF_INET; /* IPv4 */ inet_pton(AF_INET, GROUP, &clientaddr.sin_addr.s_addr); clientaddr.sin_port = htons(CLIENT_PORT); while (1) { // fgets(buf, sizeof(buf), stdin); sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&clientaddr, sizeof(clientaddr)); sleep(1); } close(sockfd); return 0; }
recv.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define err_log(errlog) do{perror(errlog); exit(1);}while(0) #define N 128 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in groupcastaddr; struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); char buf[N] = {}; if(argc < 3) { fprintf(stderr, "usage:%s serverip port.\n", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { err_log("fail to socket"); } groupcastaddr.sin_family = AF_INET; groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]); groupcastaddr.sin_port = htons(atoi(argv[2])); if(bind(sockfd, (struct sockaddr*)&groupcastaddr, sizeof(groupcastaddr)) < 0) { err_log("fail to bind"); } struct ip_mreq req; req.imr_multiaddr.s_addr = inet_addr(argv[1]); // req.imr_interface.s_addr = inet_addr("192.168.0.196"); req.imr_interface.s_addr = htonl(INADDR_ANY); if(setsockopt(sockfd, IPPROTO_IP,IP_ADD_MEMBERSHIP, &req, sizeof(req)) < 0) { err_log("fail to setsockopt"); } while(1) { if(recvfrom(sockfd, buf, N, 0, (struct sockaddr*)&clientaddr, &addrlen) < 0) { err_log("fail to recvfrom"); } printf("From clientaddr:%s\n", buf); } close(sockfd); return 0; }
更进一步:
#include <netinet/in.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <net/if.h> #define SERVER_PORT 8000 #define MAXLINE 4096 #define CLIENT_PORT 9000 #define GROUP "239.0.0.2" int main(int argc, char *argv[]) { struct sockaddr_in serveraddr, localaddr; int confd; ssize_t len; char buf[MAXLINE]; /* 组播结构体*/ struct ip_mreqn group; //1.创建一个socket confd = socket(AF_INET, SOCK_DGRAM, 0); //2.初始化本地端地址 bzero(&localaddr, sizeof(localaddr)); localaddr.sin_family = AF_INET; inet_pton(AF_INET, "0.0.0.0" , &localaddr.sin_addr.s_addr); localaddr.sin_port = htons(CLIENT_PORT); bind(confd, (struct sockaddr *)&localaddr, sizeof(localaddr)); /*设置组地址*/ inet_pton(AF_INET, GROUP, &group.imr_multiaddr); /*本地任意IP*/ inet_pton(AF_INET, "0.0.0.0", &group.imr_address); /* eth0 --> 编号命令:ip ad */ group.imr_ifindex = if_nametoindex("eth0"); /*设置client 加入多播组*/ setsockopt(confd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)); while (1) { len = recvfrom(confd, buf, sizeof(buf), 0, NULL, 0); write(STDOUT_FILENO, buf, len); } close(confd); return 0; }
https://cloud.tencent.com/developer/article/2323226
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· Qt个人项目总结 —— MySQL数据库查询与断言