Linux嵌入式网络 _ 广播和组播、UNIX域套接字

一、广播和组播

1、广播的概念:

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

       Linux嵌入式网络 _ 广播和组播、UNIX域套接字_第1张图片

  • 要点:
  1. 发送 方要通过 setsockopt设置套接字广播属性;
  2. 发送方的发送地址 为广播地址:XXX.XXX.XXX.255
  3. 接收方和发送方的端口号相同;

2、广播发送示例

  • 缺省创建 的 套接字文件是不允许 广播
  • 客户端 改成 sender方;
int b_br = 1;
setsockopt(fd , SOL_SOCKET , SO_BROADCAST,&b_br , sizeof(int));
  • 传入的IP地址 以.255的广播地址;
#include "net_exp.h"

void usage(const char* s){
	printf("\n %s serv_ip ser_port \n",s);
	printf("\n\t serv_ip:server ip address");
	printf("\r\n serv_port: server port(>5000)\n");
}
int main(int argc, const char *argv[])
{
	int fd = -1;
	int port = 0;
	struct sockaddr_in sin;
	
	if((fd = socket(AF_INET , SOCK_DGRAM,0)) < 0 ){
		perror("socket");
		exit(1);
	}
	if(argc != 3 ){
		usage(argv[0]);
		exit(1);
	}
	port = atoi(argv[2]);
	if (port < 5000){
		usage(argv[0]);
		exit(1);
	}
	int b_br = -1;
	setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&b_br,sizeof(int));  //允许广播
	/*2、填充*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
	if( inet_pton(AF_INET,argv[1],(void*)&sin.sin_addr) != 1){
		perror("inet_ntop");
		exit(1);
	}
	printf("broadcast starting ....... \n");
	char buff[BUFSIZ];
	while(1){
		bzero(buff,BUFSIZ);
		if(fgets(buff,BUFSIZ-1,stdin) == NULL){
			perror("fgets");
			continue;
		}
		sendto(fd,buff,strlen(buff),0,(struct sockaddr*)&sin,sizeof(sin));
		if(!strncasecmp(buff , "quit\n",sizeof("quit\n"))){
			printf("Client is exiting !!! \n");
			break;
		}
	}
	close(fd);
	return 0;
}

3、组播 

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

          Linux嵌入式网络 _ 广播和组播、UNIX域套接字_第2张图片

  • 加入组播
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));
#include "net_udp.h"

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

	/*1、创建socket fd*/
	if((fd = socket(AF_INET , SOCK_DGRAM , 0)) < 0){
		perror("socket");
		exit(1);
	}
	/*优化1 : 允许客户端快速重连*/
	int b_reuse = -1;
	char server_ip[16];
	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(“235.10.10.3”); //
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);

	setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
	/*2 、绑定*/
	/*2.1 填充*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
	
	sin.sin_addr.s_addr = htonl(INADDR_ANY);// INADDR_ANY :为0.0.0.0  泛指本机的意思
	/*2.2 绑定*/
	if(bind(fd , (struct sockaddr*)&sin, sizeof(sin))){
		perror("bind");
		exit(1);
	}
	if(!inet_ntop(AF_INET , (void*)&sin.sin_addr,server_ip,sizeof(sin))){
		perror("inet_ntop");
		exit(1);
	}
	printf("Server %s  starting......\n",server_ip);
	/*3、*/
	char buff[BUFSIZ];
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	while(1){
		bzero(buff, sizeof(buff));
		if(recvfrom(fd , buff , BUFSIZ-1 , 0 ,(struct sockaddr*)&cin ,&addrlen) < 0){
			perror("recvform");
			continue;
		}
		char ipv4_addr[16];
		if(!inet_ntop(AF_INET , (void*)&cin.sin_addr,ipv4_addr,sizeof(cin))){
			perror("inet_ntop");
			exit(1);
		}
		printf("Client (%s:%d) Send : %s \n",ipv4_addr,htons(sin.sin_port) ,buff);
		if(!strncasecmp(buff,"quit\n",sizeof("quit\n"))){
			printf("Client (%s : %d) is exiting \n",ipv4_addr , htons(sin.sin_port));
		}
	}
	close(fd);
	return 0;
}

4、网络地址

  • A类地址

      第1字节为网络地址,其他3个字节为主机地址。第1字节的最高位固定为0
       1.0.0.1 – 126.255.255.255

  • B类地址

       第1字节和第2字节是网络地址,其他2个字节是主机地址。第1字节的前两位固定为10
         128.0.0.1 – 191.255.255.255

  • C类地址

       前3个字节是网络地址,最后1个字节是主机地址。第1字节的前3位固定为110
          192.0.0.1 – 223.255.255.255

  • D类地址(组播地址)

       不分网络地址和主机地址,第1字节的前4位固定为1110
         224.0.0.1 – 239.255.255.255
          组播地址:224.0.0.1 – 239.255.255.255(除掉广播地址)

 

二、UNIX域套接字

1、UNIX域套接字基本概念

  • socket套接字的 简化版,在进程(本地)间进行通信;
  • socket也可以 在进程(本地)通信中使用,但没有UNIX域套接字效率高;
  • 创建套接字时使用本地协议PF_UNIX(或PF_LOCAL).
  1. socket(AF_LOCAL,SOCK_STREAM);
  2. socket(AF_LOCAL,SOCK_DGRAM);
  • 分为流式套接字和用户数据报套接字;
  • 和其他进程间通信方式相比使用方便、效率更高;
  • 常用于前后台进程通信;

2、地址结构

struct  sockaddr_un   //
{
sa_family_t  sun_family;
char  sun_path[108];
}
  • 填充地址结构
struct sockaddr_un  myaddr;
bzero(&myaddr , sizeof(myaddr));
myaddr .sun_family = AF_UNIX;
strcpy(myaddr .sun_path ,  “/tmp/mysocket”);

3、UNIX域 流式套接字

  • 客户端

                                             Linux嵌入式网络 _ 广播和组播、UNIX域套接字_第3张图片

  • 服务器

Linux嵌入式网络 _ 广播和组播、UNIX域套接字_第4张图片

                                          Linux嵌入式网络 _ 广播和组播、UNIX域套接字_第5张图片

—  绑定的地址 要是同一个 路径的 UNIX域套接字文件;

4、UNIX域 用户数据报套接字

  • 客户端

                                   Linux嵌入式网络 _ 广播和组播、UNIX域套接字_第6张图片

  • 服务器

                                        Linux嵌入式网络 _ 广播和组播、UNIX域套接字_第7张图片

5、总结

  • 编程时:先bind() 在socket_unix结构体变量;
  • 要点:
  1. unix域套接字 的文件路径名(在内存中的文件)
    1. 必须事先不存在;
    2. 一般给绝对路径;
  • 总结:进程间通信

Linux嵌入式网络 _ 广播和组播、UNIX域套接字_第8张图片

—  进程间的数据共享:

  1. 管道、消息队列、共享内存、unix域套接字

         易用性:消息队列 > unix域套接字 > 管道 > 共享内存(经常要和信号量一起用)

           效率  : 共享内存 > unix域套接字 > 管道 > 消息队列

            常用 : 共享内存 、 unix域套接字

—  异步通信

  1. 信号

— 同步和互斥(做资源保护)

  1. 信号量

6、示例 — 将 多进程TCP网络通信 改为 多进程UNIX域 流式套接字通信

  • 服务器端

 

  • 客户端

 

 

你可能感兴趣的:(Linux,_,嵌入式网络,Linux嵌入式网络)