嵌入式linux网络编程,广播,组播

1,广播

  1. 前面介绍的数据包发送方式只有一个接受方,称为单播
  2. 如果同时发给局域网中的所有主机,称为广播
  3. 只有用户数据报(使用UDP协议)套接字才能广播
  4. 广播地址
    ·以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
    ·发到该地址的数据包被所有的主机接收
    ·255.255.255.255在所有网段中都代表广播地址

1.1,广播收发

广播发送 广播接受
创建用户数据报套接字fd = socket(AF_INET,SOCK_DGRAM,0)
缺省创建的套接字不允许广播数据包,需要设置属性
---setsockopt可以设置套接字属性
接收方地址指定为广播地址
指定端口信息
发送数据包
创建用户数据报套接字
绑定本机IP地址和端口
---绑定的端口必须和发送方指定的端口相同
等待接收数据

1.2,广播收发—示例

1.2.1,net.h

#ifndef __NET_H__
#define __NET_H__

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

#define SERV_PORT 5005
#define QUIT_STR "quite"
#endif

1.2.2,sender.c

/* boardcast demo */
/*./sender receiver_ip receiver_port */
#include "net.h"

void usage(char *s)
{
	printf("this is a boardcast demo!\n");
	printf("Usage:\n\t%s <receiver_ip> <receiver_port>\n",s);
	printf("\treceiver_ip: boardcast_receiver ip address\n");
	printf("\treceiver_port: boardcast_receiver port(>5000)\n ");
}
int main(int argc, const char *argv[])
{
	int fd;
	short port;
	struct sockaddr_in sin;
	if(argc != 3)
	{
		usage((char *)argv[0]);
		exit(1);
	}
	if((port = atoi(argv[2])) < 5000)
	{
		usage((char *)argv[0]);
		exit(1);
	}
	/* 1 创建socket fd(创建用户数据报套接字) */
	if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程
	{
		perror("socket");
		exit(-1);
	}

	/*设置允许广播*/
	int b_br =1;
	setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&b_br,sizeof(int));
	
	/* 2 连接服务器 */
	/* 2.1 填充struct sockaddr_in结构体变量*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);//转为网络字节序端口号
#if 0
#else
	if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0)
	{
		perror("inet_pton");
		goto _error1;
	}
#endif
	printf("boardcast sender starting ...... OK!\n");
	/* 3 读写*/
	char buf[BUFSIZ];
	while(1)
	{
		fprintf(stderr,"Please input the string to boardcast receiver:");
		bzero(buf,BUFSIZ);
		if(fgets(buf,BUFSIZ-1,stdin) == NULL)
		{
			perror("fgets");
			continue;
		}
		sendto(fd,buf,strlen(buf),0,(struct sockaddr *)&sin,sizeof(sin));
		if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
		{
			printf("boardcast sender is existed!\n");
			break;
		}
	}

_error1:
	close(fd);
	return 0;
}

1.2.3,receiver.c

#include "net.h"

int main(int argc, const char *argv[])
{
	int fd;		
	struct sockaddr_in sin;

	/* 1 创建socket fd */
	if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程,套接字类型为SOCK_DGRAM
	{
		perror("socket");
		exit(-1);
	}

	/* 2 允许绑定地址快速重用 */ 
	int b_reuse = 1;
	setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)); 
	
	/* 3 绑定 */
	/* 3.1 填充struct sockaddr_in 结构体变量*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
#if 1
	/* 让服务器可以绑定在任意的IP上*/
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
	if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0)
	{
		perror("inet_pton");
		goto _error1;
	}
#endif
	/* 3.2 绑定*/
	if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)))
	{
		perror("bind");
		goto _error1;
	}
	printf("boardcast receiver starting .... OK!\n");
	
	char buf[BUFSIZ];
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
	while(1)
	{
		/* 让系统自动获取客户端的信息*/
		bzero(buf,BUFSIZ);
		if(recvfrom(fd,buf,BUFSIZ-1,0,(struct sockaddr *)&cin,&addrlen) < 0)
		{
			perror("recvfrom");
			continue ;
		}
		char cin_ipv4_addr[16];
		if(inet_ntop(AF_INET,&cin.sin_addr.s_addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0)
		{
			perror("inet_ntop");
			goto _error1;
		}

		printf("Receive broadcast Data: %s",buf);
		
		if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
		{
			printf("boardcast sender (%s:%d) is existing!\n",cin_ipv4_addr,ntohs(cin.sin_port));
		}
	}
_error1:
	close(fd);
	return 0;
}

1.2.4,运行结果

接收端

linux@linux:~/test/network/network_broadcast$ ./receiver 
boardcast receiver starting .... OK!
Receive broadcast Data: 1
Receive broadcast Data: 2
Receive broadcast Data: 3
Receive broadcast Data: 4
Receive broadcast Data: quite
boardcast sender (192.168.1.100:34256) is existing!

发送端

linux@linux:~/test/network/network_broadcast$ ./sender 192.168.1.255 5005
boardcast sender starting ...... OK!
Please input the string to boardcast receiver:1
Please input the string to boardcast receiver:2
Please input the string to boardcast receiver:3
Please input the string to boardcast receiver:4
Please input the string to boardcast receiver:quite
boardcast sender is existed!

2,组播(多播)

  1. 单播方式只能发给一个接收方。
  2. 广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
  3. 组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
  4. 多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)

2.1,网络地址

  1. A类地址
    第1字节为网络地址,其他3个字节为主机地址。第1字节的最高位固定为0
    1.0.0.1 – 126.255.255.255
  2. B类地址
    第1字节和第2字节是网络地址,其他2个字节是主机地址。第1字节的前两位固定为10
    128.0.0.1 – 191.255.255.255
  3. C类地址
    前3个字节是网络地址,最后1个字节是主机地址。第1字节的前3位固定为110
    192.0.0.1 – 223.255.255.255
  4. D类地址(组播地址)
    不分网络地址和主机地址,第1字节的前4位固定为1110
    224.0.0.1 – 239.255.255.255
    组播地址:224.0.0.1 – 239.255.255.255(中间除掉广播地址)

2.2,组播收发

组播发送 组播接受
创建用户数据报套接字fd = socket(AF_INET,SOCK_DGRAM,0)
接收方地址指定为组播地址
指定端口信息
发送数据包
创建用户数据报套接字
加入多播组
绑定本机IP地址和端口
---绑定的端口必须和发送方指定的端口相同
等待接收数据

2.2.1,加入多播组

struct ip_mreq
{
     struct  in_addr  imr_multiaddr;
     struct  in_addr  imr_interface;
};
struct  ip_mreq  mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(235.10.10.3);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);

setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

2.3,组播收发 — 示例

2.3.1,net.h

#ifndef __NET_H__
#define __NET_H__

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

#define MULTICAST_IP "235.10.10.3"
#define SERV_PORT 5003
//#define BACKLOG 5
#define QUIT_STR "quite"

#endif

2.3.2,sender.c

/* multicast demo */
/*./sender receiver_ip receiver_port */
#include "net.h"

void usage(char *s)
{
	printf("this is a multicast demo!\n");
	printf("Usage:\n\t%s <receiver_ip> <receiver_port>\n",s);
	printf("\treceiver_ip: multicast_receiver ip address(between 224~239 segment)\n");
	printf("\treceiver_port: multicast_receiver port(>5000)\n ");
}
int main(int argc, const char *argv[])
{
	int fd;
	short port;
	struct sockaddr_in sin;
	if(argc != 3)
	{
		usage((char *)argv[0]);
		exit(1);
	}
	if((port = atoi(argv[2])) < 5000)
	{
		usage((char *)argv[0]);
		exit(1);
	}
	/* 1 创建socket fd(创建用户数据报套接字) */
	if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程
	{
		perror("socket");
		exit(-1);
	}

	
	/* 2 连接服务器 */
	/* 2.1 填充struct sockaddr_in结构体变量*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);//转为网络字节序端口号
#if 0
#else
	if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0)
	{
		perror("inet_pton");
		goto _error1;
	}
#endif
	printf("multicast sender starting ...... OK!\n");
	/* 3 读写*/
	char buf[BUFSIZ];
	while(1)
	{
		fprintf(stderr,"Please input the string to boardcast receiver:");
		bzero(buf,BUFSIZ);
		if(fgets(buf,BUFSIZ-1,stdin) == NULL)
		{
			perror("fgets");
			continue;
		}
		sendto(fd,buf,strlen(buf),0,(struct sockaddr *)&sin,sizeof(sin));
		if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
		{
			printf("multicast sender is existed!\n");
			break;
		}
	}

_error1:
	close(fd);
	return 0;
}

2.3.3,recviver.c

#include "net.h"

int main(int argc, const char *argv[])
{
	int fd;		
	struct sockaddr_in sin;

	/* 1 创建socket fd */
	if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程,套接字类型为SOCK_DGRAM
	{
		perror("socket");
		exit(-1);
	}

	/* 2 允许绑定地址快速重用 */ 
	int b_reuse = 1;
	setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)); 

	/* 加入多播组 */
	struct  ip_mreq  mreq;
	bzero(&mreq, sizeof(mreq));
	mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP);
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);

	setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

	/* 3 绑定 */
	/* 3.1 填充struct sockaddr_in 结构体变量*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
#if 1
	/* 让服务器可以绑定在任意的IP上*/
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
	if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0)
	{
		perror("inet_pton");
		goto _error1;
	}
#endif
	/* 3.2 绑定*/
	if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)))
	{
		perror("bind");
		goto _error1;
	}
	printf("multicast receiver starting .... OK!\n");
	
	char buf[BUFSIZ];
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
	while(1)
	{
		/* 让系统自动获取客户端的信息*/
		bzero(buf,BUFSIZ);
		if(recvfrom(fd,buf,BUFSIZ-1,0,(struct sockaddr *)&cin,&addrlen) < 0)
		{
			perror("recvfrom");
			continue ;
		}
		char cin_ipv4_addr[16];
		if(inet_ntop(AF_INET,&cin.sin_addr.s_addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0)
		{
			perror("inet_ntop");
			goto _error1;
		}

		printf("Receive multicast Data: %s",buf);
		
		if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
		{
			printf("multicast sender (%s:%d) is existing!\n",cin_ipv4_addr,ntohs(cin.sin_port));
		}
	}
_error1:
	close(fd);
	return 0;
}

2.3.4,运行结果

接收端

linux@linux:~/test/network/network_multicast$ ./receiver 
multicast receiver starting .... OK!
Receive multicast Data: 1
Receive multicast Data: 2
Receive multicast Data: 3
Receive multicast Data: 4
Receive multicast Data: 5
Receive multicast Data: 6
Receive multicast Data: quite
multicast sender (192.168.1.100:48837) is existing!

发送端

linux@linux:~/test/network/network_multicast$ ./sender 235.10.10.3 5003
multicast sender starting ...... OK!
Please input the string to boardcast receiver:1
Please input the string to boardcast receiver:2
Please input the string to boardcast receiver:3
Please input the string to boardcast receiver:4
Please input the string to boardcast receiver:5
Please input the string to boardcast receiver:6
Please input the string to boardcast receiver:quite
multicast sender is existed!

猜你喜欢

转载自blog.csdn.net/m0_37542524/article/details/84658588