13.4 Linux_网络编程_套接字属性

news2024/10/22 15:20:42

概述

什么是选项的级别:

socket中可以设置的属性种类很多,比如socke的选项、传输层TCP/UDP的选项、数据链路层的选项。这些选项在不同的层级,这就是选项的级别。常用级别及含义如下:

级别含义
SOL_SOCKET作用于套接字本身
IPPROTO_IP作用于IPv4协议
IPPROTO_TCP作用于流式套接字
IPPROTO_UDP作用于数据报套接字

SOL_SOCKET级别的常用选项:

IPPROTO_IP级别的常用选项:

相关函数

//获取套接字选项
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);

返回值:成功返回0,失败返回-1 

sockfd:套接字的文件描述符

level:选项的级别

optname:要获取/设置哪一个选项

optval:获取/设置的选项存放在哪

optlen:选项存放的空间的大小

相关实验

1、保活连接实验

什么是保活链接:

保活链接指的是在TCP通信中,为了防止长时间不交互而连接被关闭的情况,服务器会向客户端发送一些数据,以确保连接正常。当到达设置的间隔时,会发出第一个保活数据,如果服务器没有收到客户端的回应,之后会继续发出。当到达设置的发送次数依旧没有得到回应,那么服务器就会中断与客户端的连接。

保活连接包括:是否开启、未通讯多久发送第一次保活数据、之后发送的间隔、发送几次未响应中断连接。与之相对应的选项如下:

含义选项级别选项设置值
是否开启保活连接SOL_SOCKETSO_KEEPALIVE1开启0关闭
未通讯多久发送第一次保活数据SOL_TCPTCP_KEEPIDLE

单位s

默认7200s(2h)

之后发送的间隔SOL_TCPTCP_KEEPINTVL

单位s

默认75s

发送几次未响应中断连接SOL_TCPTCP_KEEPCNT默认9次

server.c代码:

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

#define BACKLOG 5 				//最大接入客户端数量
int socket_init(char** argv);
void getKeepAlive(int fd);
void setKeepAlive(int fd);
int main(int argc ,char** argv){
 
	int fd;
	//判断参数有效性
	if(argc != 3){
		printf("param err\n");
		printf("%s<ip><port>\n",argv[0]);
		return -1;
	}
	printf("ip = %s\n",argv[1]);
	printf("port = %s\n",argv[2]);
	//初始化socket:TCP,IPv4,本地回环测试
	fd = socket_init(argv);
	//设置保活链接
	getKeepAlive(fd);
	setKeepAlive(fd);
	getKeepAlive(fd);
	//接受客户端链接
	int newFd;
	struct sockaddr_in newAddr;
	socklen_t newAddrlen;
	char buf[100] = {0};
	fd_set readfds,readfdsTmp;
	int i,nfds;
	//1.清空可读可写集合,并将服务器的fd添加进可读集合
	FD_ZERO(&readfds);
	FD_ZERO(&readfdsTmp);
	FD_SET(fd,&readfds);
	nfds = fd + BACKLOG + 1;
	while(1){
		readfdsTmp = readfds;
		//2.以阻塞方式监听全部文件描述符,如果有文件描述符可以写入,则返回
		if(select(nfds,&readfdsTmp,NULL,NULL,NULL) == -1){
			perror("select");
			exit(-1);
		}
		//3.根据监听到的文件描述符进行相应的操作
		if(FD_ISSET(fd,&readfdsTmp)){//监听到了服务器的fd,代表有新的客户端接入
			if((newFd = accept(fd,(struct sockaddr*)&newAddr,&newAddrlen)) < 0){
				perror("accept");
				return -1;
			}
			printf("[%s,%d]connect\n",inet_ntoa(newAddr.sin_addr),ntohs(newAddr.sin_port));
			printf("newFd = %d\n",newFd);
			FD_SET(newFd,&readfds);//将新的socket加入监听列表
		}
		else{
			//printf("Debug:fd = %d\n",fd);
			for(i=fd+1;i<nfds;i++){//监听到了客户端的fd,处理相应客户端信息,这里的难点是客户端fd的范围如何确定
				//printf("Debug:i=%d\n",i);
				if(FD_ISSET(i,&readfdsTmp)){
					if(read(i,buf,sizeof(buf)) <= 0){
						close(i);
						FD_CLR(i,&readfds);
						printf("fd=%d closed\n",i);
					}else{
						if(getpeername(i,(struct sockaddr*)&newAddr,&newAddrlen) == -1){//与客户端链接存在问题
							perror("getpeername");
						}else{
							printf("[%s,%d]data:%s",inet_ntoa(newAddr.sin_addr),ntohs(newAddr.sin_port),buf);
							write(i,"server\n",strlen("server\n"));
							memset(buf,0,sizeof(buf));
						}
					}
				}
			}	
		}
	}
	close(fd);
	return 0;
}

void setKeepAlive(int fd){
	int optval;
	optval = 1;
	if(setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,&optval,sizeof(int)) == -1){
		perror("SO_KEEPALIVE");
	}
	optval = 5;
	if(setsockopt(fd,SOL_TCP,TCP_KEEPIDLE,&optval,sizeof(socklen_t))){
		perror("TCP_KEEPIDLE");
	}
	optval = 2;
	if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,&optval,sizeof(socklen_t))){
		perror("TCP_KEEPINTVL");
	}
	optval = 3;
	if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT,&optval,sizeof(socklen_t))){
		perror("TCP_KEEPCNT");
	}

}
void getKeepAlive(int fd){
	int optval;
	socklen_t optlen;
	if(getsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,&optval,&optlen) == -1){
		perror("get SO_KEEPALIVE");
	}else{
		printf("是否开启保活链接 = %d\n",optval);
	}
	if(getsockopt(fd,SOL_TCP,TCP_KEEPIDLE,&optval,&optlen) == -1){
		perror("get TCP_KEEPIDLE");
	}else{
		printf("未通讯多久发送第一次保活数据 = %ds\n",optval);
	}
	if(getsockopt(fd,SOL_TCP,TCP_KEEPINTVL,&optval,&optlen) == -1){
		perror("get TCP_KEEPINTVL");
	}else{
		printf("之后发送的间隔 = %d\n",optval);
	}
	if(getsockopt(fd,SOL_TCP,TCP_KEEPCNT,&optval,&optlen) == -1){
		perror("get TCP_KEEPCNT");
	}else{
		printf("发送几次未响应中断连接 = %d\n",optval);
	}
}
int socket_init(char** argv){
	int fd;
	struct sockaddr_in addr;
	//1.创建socket
	if((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议
		perror("socket");
		exit(-1);
	}
	//2.绑定IP、端口号
	addr.sin_family = AF_INET;    				  //IPv4
	addr.sin_port = htons(atoi(argv[2])); 		  //端口号,要转化为大端子节序
	addr.sin_addr.s_addr = inet_addr(argv[1]);    //IP地址:0表示在本网络上的本主机,即:自己
	if(bind(fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) == -1){
		perror("bind");
		exit(-1);
	}
	//3.监听socket
	if(listen(fd,BACKLOG) == -1){ 	//允许最多接入5个客户端
		perror("listen");
		exit(-1);
	}
	return fd;
}

代码执行结果如下:

2、广播实验

什么是广播:

如果数据包发送方式只有一个接收方,这称为单播。如果同时发给网络中的所有主机,这就是广播。只有UDP能够实现广播。

广播地址特点:

一个网络内主机号全为1的IP地址为广播地址。发到广播地址的数据包会被所有的主机接收。

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

Linux下查询广播地址:

ifconfig查询到的广播地址为192.168.129.255,掩码为255.255.255.0

这代表如果向该广播地址发送数据,那么IP为192.168.129.xxx的主机都可以接收到数据。

客户端广播服务器接收实验:

设置允许广播:

//设置可以允许广播
int on = 1;
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

client.c代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
int main(int argc,char** argv){
 
	int fd;
	struct sockaddr_in addr;
	//判断参数有效性
	if(argc != 3){
		printf("param err\n");
		printf("%s<ip><port>\n",argv[0]);
		return -1;
	}
	printf("ip = %s\n",argv[1]);
	printf("port = %s\n",argv[2]);
	//1.创建socket
	if((fd=socket(AF_INET,SOCK_DGRAM,0))<0){//IPv4,UDP协议
		perror("socket");
		return -1;
	}
	//设置可以允许广播
	int on = 1;
	setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
	//2.设置要发送到的服务器信息
	addr.sin_family = AF_INET;    		  		//IPv4
	addr.sin_port = htons(atoi(argv[2])); 		//服务器端口号,要转化为大端子节序
	addr.sin_addr.s_addr = inet_addr(argv[1]);  //服务器IP地址:在本网络上的本主机,即:自己
	//3.数据交互
	while(1){
		sendto(fd,"cilent",strlen("cilent"),0,(struct sockaddr*)&addr,sizeof(addr));
		sleep(1);
	}
	close(fd);
 
	return 0;
}

代码运行结果如下: 

3、组播实验

组播是什么:

组播是向多个网络的不同主机同时发送消息。组播的IP是D类地址(多播地址),每一个IP代表一个多播组。多播地址只能用于 "目的地址" ,不能用作 "源地址" 。

组播地址范围:224.0.0.0 ~ 239.255.255.255,一般选择239的网络号

组播如何通讯:

接收方(目的地址)先加入指定的多播组,发送方(源地址)向多播组发送数据,则加入该组的全部主机都可以收到数据。

服务器加入多播组客户端发数据实验:

注意事项:

  • 进行该实验时需要保证本机已经联网,因为组播的数据是在网络上传输的。
  • 在设置服务器IP时,不能设置127.0.0.1,因为这是本地回环地址,是不涉及真正的网络传输的。应该设置为0,这代表本网络上的本主机的IP地址,这样才能真正的在网络中通信。

加入多播组:

//设置多播组的结构体
struct ip_mreq {
    struct in_addr imr_multiaddr;   /*IP 组播组地址*/
    struct in_addr imr_interface;   /*本地接口的IP地址*/
};

//绑定组播IP
struct ip_mreq mreq;
memset(&mreq,0,sizeof(mreq));
if(!inet_aton("239.2.2.100",&mreq.imr_multiaddr)){//组播IP
    perror("inet_aton");
    return -1;
}
printf("组播ip = %s\n",inet_ntoa(mreq.imr_multiaddr));//setsockopt设置套接字属性,加入多播组
if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0){
    perror("setsockopt");
    return -1;
}

server.c代码: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
int main(int argc,char** argv){
	int fd;
	struct sockaddr_in addr;
	//判断参数有效性
	if(argc != 3){
		printf("param err\n");
		printf("%s<ip><port>\n",argv[0]);
		return -1;
	}
	printf("ip = %s\n",argv[1]);
	printf("port = %s\n",argv[2]);
	//1.创建socket
	if((fd=socket(AF_INET,SOCK_DGRAM,0))<0){//IPv4,UDP协议
		perror("socket");
		return -1;
	}
	
	//绑定组播IP
	struct ip_mreq mreq;
	memset(&mreq,0,sizeof(mreq));
	//if(!inet_aton("0.0.0.0",&mreq.imr_interface)){//本地IP
	//	perror("inet_aton");
	//	return -1;
	//}
	if(!inet_aton("239.2.2.100",&mreq.imr_multiaddr)){//组播IP
		perror("inet_aton");
		return -1;
	}
	printf("组播ip = %s\n",inet_ntoa(mreq.imr_multiaddr));
	if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0){
    	perror("setsockopt");
    	return -1;
	}
	
	//2.绑定IP、端口号
	addr.sin_family = AF_INET;    				  //IPv4
	addr.sin_port = htons(atoi(argv[2])); 		  //端口号,要转化为大端子节序
	addr.sin_addr.s_addr = inet_addr(argv[1]);    //IP地址:0表示在本网络上的本主机,即:自己
	if(bind(fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) == -1){
		perror("bind");
		return -1;
	}
	//3.数据交互
	char buf[100] = {0};
	struct sockaddr_in src_addr;
	socklen_t src_addrlen;
	while(1){
		memset(buf,0,sizeof(buf));
		if(recvfrom(fd,buf,sizeof(buf)-1,0,(struct sockaddr*)&src_addr,&src_addrlen) > 0){
			printf("client port = %d\n",ntohs(src_addr.sin_port));
			printf("client ip = %s\n",inet_ntoa(src_addr.sin_addr));
			printf("read:%s\n",buf);
		}	
	}
	close(fd);
	return 0;
}

 代码运行结果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2220881.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL中的优先规则

在图片的例子中&#xff0c;有两个条件&#xff1a; 第一个条件是job_id是AD_PRES并且薪水高于15,000。 第二个条件是job_id是SA_REP。 在图片中的例子有两个条件&#xff1a; 第一个条件是job_id是AD_PRES或者SA_REP。 第二个条件是薪水高于$15,000。

React Componet类组件详解(老项目)

React类组件是通过创建class继承React.Component来创建的&#xff0c;是React中用于构建用户界面的重要部分。以下是对React类组件的详细解释&#xff1a; 一、定义与基本结构 类组件使用ES6的class语法定义&#xff0c;并继承自React.Component。它们具有更复杂的功能&#…

Vscode连接WSL2(Ubuntu20.04)

一.安装WSL插件 在扩展里面搜索WSL,选择安装即可 二.连接到wsl 安装完毕后在左下角看到一个按钮&#xff08;一个>和一个<组成&#xff09;&#xff0c;点击在中间选择"连接到wsl",然后Vscode会弹出一个新窗口&#xff0c;左下角显示WSL子系统名称&#xff0…

vue中如何检测数组变化(vue基础,面试,源码级讲解)

大家有什么不明白的地方可以分享在评论区&#xff0c;大家一起探讨哦~~ &#xff08;如果对数据劫持还有所不明白的小伙伴&#xff0c;可以去看看上一篇文章哦&#xff09; 在vue2中&#xff0c;是如何对数组进行劫持的呢&#xff1f; 简单代码实现&#xff1a; 在vue2中&…

学习中,师傅b站泷羽sec——xss挖掘过程

某职业技术学院网站xss挖掘&#xff1a; 资产归纳 例如&#xff1a;先把功能点都看一遍&#xff0c;大部分都是文章 根据信息搜集第一课学习到一般主站的防御力是比较强的&#xff0c;出现漏洞的点不是对新手不友好。 在资产验证过程中还是把主站看了一遍 没有发现有攻击的机会…

G1 GAN生成MNIST手写数字图像

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 G1 GAN生成MNIST手写数字图像 1. 生成对抗网络 (GAN) 简介 生成对抗网络 (GAN) 是一种通过“对抗性”学习生成数据的深度学习模型&#xff0c;通常用于生成…

如何调试浏览器中的内存泄漏?

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介⭐ 如何调试浏览器中的内存泄漏&#xff1f;1. 什么是内存泄漏&#xff1f;2. 调试内存泄漏的工具3. 如何使用 Memory 面板进行内存调试3.1 获取内存快照&#xff08;Heap Snapshot&#xff09;获取内存快照的步骤&#xff1a;快照…

即时通讯增加Redis渠道

情况说明 在本地和服务器分别启动im服务&#xff0c;当本地发送消息时&#xff0c;会发现服务器上并没有收到消息 初版im只支持单机版&#xff0c;不支持分布式的情况。此次针对该情况对项目进行优化,文档中贴出的代码非完整代码&#xff0c;可自行查看参考资料[2] 代码结构调…

C Primer Plus 第9章——第一篇

你该逆袭了 文章目录 一、复习函数1、定义带形式参数的函数2、声明带形式参数函数的原型3、使用 return 从函数中返回值&#xff08;1&#xff09;、返回值不仅可以赋给变量&#xff0c;也可以被用作表达式的一部分。&#xff08;2&#xff09;、返回值不一定是变量的值&#x…

springboot redisTemplate hash 序列化探讨

前提提要&#xff1a;这个是个人小白总结&#xff0c;写完博客后开始厌蠢。 redisTemplate 有两种插入hash的方式 redisTemplate.opsForHash().putAll(key, map);redisTemplate.opsForHash().put(key, field, value);在使用的过程中&#xff0c;难免会疑问为什么 key field v…

Windows下部署autMan

一、安装autMan 下载autMan压缩包 https://github.com/hdbjlizhe/fanli/releases 解压安装包 二、运行&#xff08;注意&#xff0c;无论是交互运行还是静默运行&#xff0c;终端均不可关闭&#xff09; 基本运行 双击autMan.exe运行。 高级运行 在autMan文件夹&#xff0…

Sigrity Power SI Model Extraction模式如何提取电源网络的S参数和阻抗操作指导(一)

Sigrity Power SI Model Extraction模式如何提取电源网络的S参数和阻抗操作指导(一) Sigrity PowerSI是频域电磁场仿真工具,以下图为例介绍如果用它观测电源的网络的S参数以及阻抗的频域曲线. 观测IC端电源网络的自阻抗 1. 用powerSi.exe打开该SPD文件

工业相机详解及选型

工业相机相对于传统的民用相机而言&#xff0c;具有搞图像稳定性,传输能力和高抗干扰能力等&#xff0c;目前市面上的工业相机大多数是基于CCD&#xff08;Charge Coupled Device)或CMOS(Complementary Metal Oxide Semiconductor)芯片的相机。 一&#xff0c;工业相机的分类 …

sentinel原理源码分析系列(六)-统计指标

调用链和统计节点构建完成&#xff0c;进入统计指标插槽&#xff0c;统计指标在最后执行的&#xff0c;等后面的插槽执行完&#xff0c;资源调用完成了&#xff0c;根据资源调用情况累计。指标统计是最重要的插槽&#xff0c;所有的功能都依靠指标数据&#xff0c;指标的正确与…

你知道什么叫数控加工中心吗?

加工中心是一种高度机电一体化的数控机床&#xff0c;具有刀库&#xff0c;自动换刀功能&#xff0c;对工件一次装夹后进行多工序加工的数控机床。通过计算的控制系统和稳定的机械结构&#xff0c;加工中心能够实现高精度的加工&#xff0c;确保工件的尺寸精度和表面质量。通过…

实用好助手

在现代职场中&#xff0c;拥有高效且适用的工具能够显著提升我们的工作效率与质量。除了常见的办公软件&#xff0c;还有许多小众但非常实用的工具可以大幅度优化工作流程。以下是另外五个推荐的工作软件&#xff0c;它们各自具备独特的功能与优势&#xff0c;值得一试。 1 …

【Docker系列】在 Docker 容器中打印和配置环境变量

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

双十一有哪些值得买的东西?2024年最全双十一好物推荐榜单来了!

双十一能够入手的好东西那肯定是非常多的&#xff0c;不过要想买到性价比高、实用性强的好物&#xff0c;就必须得做些功课了。作为一个智能家居和数码领域的博主&#xff0c;自然知道每年双十一买什么是最划算的。如果有朋友正在为双十一不知道买什么而发愁&#xff0c;那就快…

python+大数据+基于热门视频的数据分析研究【内含源码+文档+部署教程】

博主介绍&#xff1a;✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业毕业设计项目实战6年之久&#xff0c;选择我们就是选择放心、选择安心毕业✌ &#x1f345;由于篇幅限制&#xff0c;想要获取完整文章或者源码&#xff0c;或者代做&am…

登录后端笔记(一):注册、登录;基于MD5加密

一、注册 一、参数&#xff1a;lombok pom.xml里引入依赖&#xff1b; 二、响应数据&#xff1a;Result 原视频 两个注解对应有参无参生成构造方法&#xff1b; data类型是泛型T&#xff0c;即data在使用时可对应object可对应string字符串可对应bean对象可对应map等&#x…