网络编程 广播、组播

目录

一、UDP

1. 流程

 2. 相关函数

二、广播

1.概念

2. 流程

三、组播

1. 特点

2. 流程


一、UDP

无连接;

多个客户端可以发送消息给服务器

1. 流程

服务器:

socket() -> bind() -> recvfrom()/sendto() -> close() UDP客户端流程: socket() -> recvfrom()/sendto() -> close()

客户端:

socket() -> recvfrom()/sendto() -> close()

 2. 相关函数

(1)recvfrom

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
	

 功能:接收数据
参数:
        sockfd:socket函数的返回值 数据报套接字
        buf:内存地址
        len:接收数据的大小
        flags:0
        src_addr:发送端的结构体地址
        addrlen:发送端的结构体长度的地址    
返回值:
        成功返回接受的字节数,失败返回-1

(2) sendto()

#include <sys/types.h>
#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

 功能:
参数:
        sockfd:socket函数的返回值 数据报套接字
        buf:内存地址
        len:接收数据的大小
        flags:0
        dest_addr:发送端的结构体地址
        addrlen:发送端的结构体长度的地址
返回值:   
        成功返回接受的字节数,失败返回-1 

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}

	printf("sockfd=%d\n", sockfd);

	struct sockaddr_in serveraddr,clientaddr;
	memset(&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(7777);
	serveraddr.sin_addr.s_addr = inet_addr("0");

	int len = sizeof(serveraddr);

	int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len);
	if(ret == -1)
	{
		perror("bind");
		return -1;
	}

	//收发数据
	char buf[64] = {0};

	int m = sizeof(clientaddr);

	while(1)
	{
		int n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&clientaddr, &m );
		if(n == -1)
		{
			perror("recvfrom");
			return -1;
		}
		
		printf("n=%d\n", n);
	
		printf("client ip:%s client port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

		printf("message:%s\n", buf);

		memset(buf, 0 ,sizeof(buf));
	}

	close(sockfd);

	return 0;
}

客户端: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	if(argc!=2)
	{
		printf("%s ip\n", argv[0]);
		return -1;
	}

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}

	printf("sockfd=%d\n", sockfd);

	struct sockaddr_in serveraddr,clientaddr;
	memset(&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(7777);
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);


	//收发数据
	char buf[64] = {0};

	int len = sizeof(serveraddr);

	while(1)
	{
		gets(buf);

		sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serveraddr, len);

		memset(buf, 0 ,sizeof(buf));
	}

	close(sockfd);

	return 0;
}

设置套接字属性 --- setsockopt 

int setsockopt(int sockfd, int level, int optname,void *optval, socklen_t optlen);

属性设置表:level

SOL_SOCKET --- 广播

------------------------------------------------

参数                                              optname 宏的作用                 对应参数optaval的类型

SO_BROADCAST                     允许发送广播数据                         int

SO_DEBUG                              允许调试                                        int

SO_DONTROUTE                    不查找路由                                    int

SO_ERROR                              获得套接字错误                            int

SO_KEEPALIVE                       保持连接                                       int

SO_LINGER                             延迟关闭连接                                struct linger

SO_OOBINLINE                       带外数据放入正常数据流              int

SO_RCVBUF                           接收缓冲区大小                             int

SO_SNDBUF                           发送缓冲区大小                             int

SO_RCVLOWAT                      接收缓冲区下限                             int

SO_SNDLOWAT                      发送缓冲区下限                             int

SO_RCVTIMEO                       接收超时                                 struct timeval

SO_SNDTIMEO                       发送超时                                 struct timeval

SO_REUSEADDR                   允许重用本地地址和端口               int

SO_TYPE                                 获得套接字类型                             int

SO_BSDCOMPAT 与BSD        系统兼容                                        int ======================================================

IPPROTO_IP --- 组播

------------------------------------------------------

IP_ADD_MEMBERSHIP         加入到组播组中                       struct ip_mreq

IP_MULTICAST_IF                 允许开启组播报文的接口         struct ip_mreq

二、广播

1.概念

同时发给局域网中的所有主机,称为广播;


只有用户数据报(使用UDP协议套接字)才能广播;

广播地址:
    以192.168.1.0 (255.255.255.0)网段为例,最大的主机地址192.168.1.255代表该网段的广播地址发到该地址的数据包被所有的主机接收


255.255.255.255在所有网段中都代表广播地址

2. 流程

发送广播消息:
    1.socket(AF_INET, SOCK_DGRAM, 0);        //创建用户数据报UDP套接字
    2.struct sockaddr_in argv[1]         //向广播地址发送
    3.setsockopt         //设置套接字属性,开广播权限
    4.sendto (如果不开权限,会报错)

 接收广播消息:
    1.socket(AF_INET, SOCK_DGRAM, 0);
    2.struct sockaddr_in serveraddr;
    3.bind         //"0"地址 or 广播地址(只能收广播消息)
    4.recvfrom

 发送端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}

	printf("sockfd=%d\n", sockfd);

	struct sockaddr_in serveraddr,clientaddr;
	memset(&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(7777);//端口号
	serveraddr.sin_addr.s_addr = inet_addr("0");//自动路由

	int len = sizeof(serveraddr);

	int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len);
	if(ret == -1)
	{
		perror("bind");
		return -1;
	}

	//收发数据,没有连接,相对与TCP
	char buf[64] = {0};

	int m = sizeof(clientaddr);

	while(1)//不停的接受数据,客户端关掉,对服务端没有影响,因为没有连接
	{
		int n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&clientaddr, &m );
		if(n == -1)
		{
			perror("recvfrom");
			return -1;
		}
		
		printf("n=%d\n", n);
	
		printf("client ip:%s client port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

		printf("message:%s\n", buf);

		memset(buf, 0 ,sizeof(buf));
	}

	close(sockfd);

	return 0;
}

接收端:

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>

int main(int argc, const char *argv[])
{
	if(argc!=2)
	{
		printf("%s ip\n", argv[0]);
		return -1;
	}

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}

	printf("sockfd=%d\n", sockfd);

	//打开广播权限
	int on = 1;
	int m = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) );//允许发送广播权限
	if(m == -1)
	{
		perror("setsockopt");
		return -1;
	}

	struct sockaddr_in serveraddr,clientaddr;
	memset(&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(7777);
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

	//发数据
	char buf[64] = {0};

	int len = sizeof(serveraddr);

	while(1)
	{
		gets(buf);

		sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serveraddr, len);

		memset(buf, 0 ,sizeof(buf));
	}

	close(sockfd);

	return 0;
}

三、组播

1. 特点

组播(又称为多播)是一种折中的方式,只有加入某个多播组的主机才能收到数据。

既可以发送多个主机, 又避免像广播一样带来过多的负载(过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信);

范围:224.0.0.1~239.255.255(D类网络地址)(1110 0000 -224 1110 1111-239)

2. 流程

发送组播消息:(client)
    1.socket        //创建用户数据报套接字
    2.struct sockaddr_in addr        //向组播地址发送,指定组播地址
    3.sendto();        //发送数据包

 接收组播消息:(server) man 7 ip
    1.socket();        //创建用户数据报套接字
    2.struct sockaddr_in serveraddr;
    3.struct ip_mreqn mreq;         // 组播地址 + "0"地址
    4.setsockopt         //加入多播组
    5.bind();        //绑定ip地址和端口
    6.recvfrom();

相关结构体:
struct ip_mreqn {
    struct in_addr imr_multiaddr; /* IP多播组地址*/
    struct in_addr imr_address; /* 本地接口的IP地址 */
    int imr_ifindex; /* 接口索引 ip_mreq 无该字段 */
};

 接收端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}

	printf("sockfd=%d\n", sockfd);

	struct sockaddr_in serveraddr,clientaddr;
	memset(&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(7777);
	serveraddr.sin_addr.s_addr = inet_addr("0");

	int len = sizeof(serveraddr);

	int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len);
	if(ret == -1)
	{
		perror("bind");
		return -1;
	}

	struct ip_mreqn mrep;
	mrep.imr_multiaddr.s_addr = inet_addr("224.10.10.10");//组播地址
	mrep.imr_address.s_addr = inet_addr("0");//可以用0来表示

	//加入到组播组中
	ret = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mrep, sizeof(mrep) );
	if(ret == -1)
	{
		perror("setsockopt");
		return -1;
	}

	//收发数据
	char buf[64] = {0};

	int m = sizeof(clientaddr);

	while(1)
	{
		int n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&clientaddr, &m );
		if(n == -1)
		{
			perror("recvfrom");
			return -1;
		}
		
		printf("n=%d\n", n);
	
		printf("client ip:%s client port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

		printf("message:%s\n", buf);

		memset(buf, 0 ,sizeof(buf));
	}

	close(sockfd);

	return 0;
}

发送端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	if(argc!=2)
	{
		printf("%s ip\n", argv[0]);
		return -1;
	}

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
		return -1;
	}

	printf("sockfd=%d\n", sockfd);

	struct sockaddr_in serveraddr,clientaddr;
	memset(&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(7777);
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

	//收发数据
	char buf[64] = {0};

	int len = sizeof(serveraddr);

	while(1)
	{
		gets(buf);

		sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serveraddr, len);

		memset(buf, 0 ,sizeof(buf));
	}

	close(sockfd);

	return 0;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值