Linux网络编程笔记(六)广播介绍

文章目录

  • 参考
  • 1 套接字选项
    • 1.1 参数说明
    • 1.2 SO_BROADCAST选项
    • 1.3 SO_SNDBUF和SO_RCVBUF选项
  • 2 广播地址
  • 3 示例
    • 3.1 广播接收端
    • 3.2 广播发送端
    • 3.3 运行结果

参考

本文所有知识均参考网课:
https://study.163.com/course/courseLearn.htm?courseId=1002913013&share=1&shareId=1145943119#/learn/video?lessonId=1003301249&courseId=1002913013

1 套接字选项

套接字选项用于修饰套接字以及其底层通讯协议的各种行为。函数setsockopt和getsockopt可以查看和设置套接字的各种选项。
以下两个函数返回值:若成功,返回0;若出错,返回-1

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

1.1 参数说明

  • level

其中参数level标识了选项应用的协议。如果选项是通用的套接字层次选项,则level设置成SOL_SOCKET。否则,level设置成控制这个选项的协议编号。对于TCP选项,level是IPPROTO_TCP,对于IP,level是IPPROTO_IP。

  • optname
    下面总结了通用套接字层次选项,也就是参数optname。
选项 参数optval的类型 描述
SO_ACCEPTCONN int 返回信息指示该套接字是否能被监听(仅getsockopt)
SO_BROADCAST int 如果*optval非0,广播数据段
SO_DEBUG int 如果*optval非0,启用网络驱动调试功能
SO_DONTROUTE int 如果*optval非0,绕过通常路由
SO_ERROR int 返回挂起的套接字错误并清除(仅getsockopt)
SO_KEEPALIVE int 如果*optval非0,启用周期性keep-alive报文
SO_LINGER struct linger 当还有未发报文而套接字已关闭时,延迟时间
SO_OOBINLINE int 如果*optval非0,将带外数据放在普通数据中
SO_RCVBUF int 接收缓冲区的字节长度
SO_RCVLOWAT int 接收调用中返回的最小数据字节数
SO_RCVTIMEO struct timeval 套接字接收调用的超时值
SO_REUSEADDR int 如果*optval非0,重用bind中的地址
SO_SNDBUF int 发送缓冲区的字节长度
SO_SNDLOWAT int 发送调用中传送的最小数据字节数
SO_SNDTIMEO struct timeval 套接字发送调用的超时值
SO_TYPE int 标识套接字类型(仅getsockopt)
  • optval
    参数optval根据选项的不同指向一个数据结构或者一个整数。一些选项是on/off开关。如果整数非0,则启用选项。如果整数为0,则禁止选项。
  • optlen
    参数optlen指定了optal指向的对象的大小。

1.2 SO_BROADCAST选项

SO_BROADCAST选项控制着UDP套接字是否能够发送广播数据报,选项的类型为int,非零意味这"是",注意,只有UDP套接字可以使用这个选项,TCP是不能使用广播的。

int opt = 1;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) < 0){
	//错误处理
}
if(setsockopt(sockfd,SOL_SOCKET,
					SO_BROADCAST,&opt,sizeof(opt)) < 0){
	//错误处理
}

1.3 SO_SNDBUF和SO_RCVBUF选项

每一个套接字有一个发送缓冲区和接收缓冲区,这两个缓冲区由底层协议使用,接收缓冲区存放由协议接收的数据直到被应用程序读走,发送缓冲区存放应用写出的数据直到被协议发送出去。SO_SNDBUF和SO_RCVBUF选项分别控制发送和接收缓存区的大小,他们的类型均为int,以字节为单位。

if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0){
	//错误处理
}

//获得发送缓冲区的大小存放在下面程序中的opt参数中
if(getsockopt(sockfd,SOL_SOCKET,
					SO_SNDBUF,&opt,sizeof(opt)) < 0){
	//错误处理
}
opt += 2048;
if(setsockopt(sockfd,SOL_SOCKET,
				SO_SNDBUF,&opt,sizeof(opt)) < 0){
	//错误处理
}

2 广播地址

  • 如果用{net ID,subnet ID,host ID} 表示IPV4地址,那么有四类的广播地址,我们用-1表示所有比特都为1的字段。
  • 子网广播地址:{net ID,subnet ID,-1}。这类地址编排指定子网上的所有接口。例如,如果我们对B类地址192.168采用8位子网ID,那么192.168.2.255将是192.168.2子网上所有接口的子网广播地址。路由器通常不转发这类广播。

3 示例

3.1 广播接收端

#include
#include
#include
#include
#include
#include
#include
#include
#include

int sockfd;

void sig_handler(int signo){
	if(signo == SIGINT){
		printf("receiver will exited\n");
		close(sockfd);
		exit(1);
	}
}

int main(int argc,char *argv[]){
	if(argc < 2){
		fprintf(stderr,"usage:%s port\n",argv[0]);
		exit(1);
	}

	if(signal(SIGINT,sig_handler) == SIG_ERR){
		perror("signal sigint error");
		exit(1);
	}

	sockfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd < 0){
		perror("socket error");
		exit(1);
	}

	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(atoi(argv[1]));
	serveraddr.sin_addr.s_addr = INADDR_ANY;
	if(bind(sockfd,(struct sockaddr*)&serveraddr,
				sizeof(serveraddr)) < 0){
		perror("bind error");
		exit(1);
	}

	char buffer[1024];
	struct sockaddr_in clientaddr;
	socklen_t len = sizeof(clientaddr);
	while(1){
		memset(buffer,0,sizeof(buffer));
		memset(&clientaddr,0,sizeof(clientaddr));
		if(recvfrom(sockfd,buffer,sizeof(buffer),0,
			(struct sockaddr*)&clientaddr,&len) < 0){
			perror("recvfrom error");
			exit(1);
		}
		else{
			char ip[16];
			inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,
					ip,sizeof(ip));
			int port = ntohs(clientaddr.sin_port);
			printf("%s(%d):%s\n",ip,port,buffer);
		}
	}
	return 0;
}

3.2 广播发送端

#include
#include
#include
#include
#include
#include

int main(int argc,char *argv[]){
	if(argc < 3){
		fprintf(stderr,"usage: %s ip port\n",argv[0]);
		exit(1);
	}
	
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd < 0){
		perror("socket error");
		exit(1);
	}
	int opt = 1;
	//采用广播方式发送
	setsockopt(sockfd,SOL_SOCKET,
			SO_BROADCAST,&opt,sizeof(opt));

	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(atoi(argv[2]));
	inet_pton(AF_INET,argv[1],
			&serveraddr.sin_addr.s_addr);
	printf("I will broadcast...\n");
	char *info = "hello llc";
	size_t size = strlen(info) * sizeof(char);
	if(sendto(sockfd,info,size,0,
			(struct sockaddr*)&serveraddr,
				sizeof(serveraddr)) < 0){
		perror("sento error");
		exit(1);
	}
	else{
		printf("broadcast success\n");
	}

	return 0;
}

3.3 运行结果

以下都在本机测试:
1 启动接收端接收客户端:
在这里插入图片描述
2 启动发送端连接,同时接收端接收到客户端的连接:
在这里插入图片描述
在这里插入图片描述

返回信息指示该套接字是否能被监听(仅getsockopt)

你可能感兴趣的:(linux网络编程)