lv8 嵌入式开发-网络编程开发 17 套接字属性设置

news2024/10/6 2:26:26

1 基本概念

  • 设置套接字的选项对套接字进行控制
  • 除了设置选项外,还可以获取选项
  • 选项的概念相当于属性,所以套接字选项也可说是套接字属性
  • 有些选项(属性)只可获取,不可设置;
  • 有些选项既可设置也可获取

2 选项的级别

一些选项都是针对一种特定的协议
一些选项适用于所有类型的套接字
选项级别(level)的概念

2.1 常用的级别

SOL_SOCKET该级别的选项只作用于套接字本身
SOL_LRLMP该级别的选项作用于IrDA协议
IPPROTO_IP该级别的选项作用于IPv4协议
IPPROTO_IPV6该级别的选项作用于IPv6协议
IPPROTO_RM该级别的选项作用于可靠的多播传输
IPPROTO_TCP该级别的选项适用于流式套接字
IPPROTO_UDP该级别的选项适用于数据报套接字

2.2 SOL_SOCKET的常用选项

选项名称说明获取/设置
SO_ACCEPTCONN套接字是否处于监听状态获取
SO_BROADCAST允许发送广播数据两者都可
SO_DEBUG允许调试两者都可
SO_DONTROUTE不查找路由两者都可
SO_ERROR获得套接字错误获取
SO_KEEPALIVE保活连接两者都可
SO_LINGER延迟关闭连接两者都可
SO_OOBINLINE带外数据放入正常数据流两者都可
SO_RCVBUF接收缓冲区大小两者都可
SO_SNDBUF发送缓冲区大小两者都可
SO_REUSERADDR允许重用本地地址和端口两者都可
SO_TYPE获得套接字类型获取

2.3 IPPROTO_IP级别的常用选项

选项名称说明获取/设置
IP_OPTIONS获取或设置IP头部内的选项两者都可
IP_HDRINCL是否将IP头部与数据一起提交给Winsock函数两者都可
IP_TTLIP TTL相关两者都可

3获取套接字选项

3.1 getsockopt函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

参数
sockfd:套接字描述符
level:表示选项的级别
optname:表示要获取的选项名称
optval:指向存放接收到的选项内容的缓冲区
optlen:指向optval所指缓冲区的大小
函数返回值:
执行成功返回0,否则返回‒1errno来获取错误码
常见的错误码:
EBADF:参数sockfd不是有效的文件描述符
EFAULT:参数optlen太小或optval所指缓冲区非法
EINVAL:参数level未知或非法
ENOPROTOOPT:选项未知或不被指定的协议族所支持
ENOTSOCK:描述符不是一个套接字描述符

3.2 示例:获取流套接字和数据报套接字接收和发送的(内核)缓冲区大小

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
 
int main()
{
	int err,s = socket(AF_INET, SOCK_STREAM, 0);//创建流套接字
	if (s == -1) {
		printf("Error at socket()\n");
		return -1;
	}
	int su = socket(AF_INET, SOCK_DGRAM, 0); //创建数据报套接字
	if (s == -1) {
		printf("Error at socket()\n");
		return -1;
	}

	int optVal;
	int optLen = sizeof(optVal);
	//获取流套接字接收缓冲区大小
	if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&optVal,
                   (socklen_t *)&optLen) == -1)
		printf("getsockopt failed:%d", errno);
	else
		printf("Size of stream socket receive buffer: %ld bytes\n", optVal);
	//获取流套接字发送缓冲区大小
	if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&optVal,
                   (socklen_t *)&optLen) == -1)
		printf("getsockopt failed:%d", errno);
	else 
		printf("Size of streaming socket send buffer: %ld bytes\n", optVal);

	//获取数据报套接字接收缓冲区大小
	if (getsockopt(su, SOL_SOCKET, SO_RCVBUF, (char*)&optVal,
                   (socklen_t *)&optLen) == -1)
		printf("getsockopt failed:%d", errno);
	else
		printf("Size of datagram socket receive buffer: %ld bytes\n", optVal);
	//获取数据报套接字发送缓冲区大小
	if (getsockopt(su, SOL_SOCKET, SO_SNDBUF, (char*)&optVal,
                   (socklen_t *)&optLen) == -1)
		printf("getsockopt failed:%d", errno);
	else
		printf("Size of datagram socket send buffer:%ld bytes\n", optVal);

	getchar();
	return 0;
}

3.3 示例:获取当前套接字类型

#include <stdio.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>

int main()
{
	int err;
	int s = socket(AF_INET, SOCK_STREAM, 0); //创建流套接字
	if (s == -1) {
		printf("Error at socket()\n");
		return -1;
	}
	int su = socket(AF_INET, SOCK_DGRAM, 0); //创建数据报套接字
	if (s == -1) {
		printf("Error at socket()\n");
		return -1;
	}

	int optVal;
	int optLen = sizeof(optVal);
	//获取套接字s的类型
	if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&optVal, (socklen_t *)&optLen) == -1)
		printf("getsockopt failed:%d", errno);
	else
	{
		if (SOCK_STREAM == optVal) // SOCK_STREAM宏定义值为1
			printf("The current socket is a stream socket.\n"); //当前套接字是流套接字
		else if (SOCK_DGRAM == optVal) // SOCK_ DGRAM宏定义值为2
			printf("The current socket is a datagram socket.\n");//当前套接字是数据报套接字
	}
	//获取套接字su的类型
	if (getsockopt(su, SOL_SOCKET, SO_TYPE, (char*)&optVal, (socklen_t *)&optLen) == -1)
		printf("getsockopt failed:%d", errno);
	else
	{
		if (SOCK_STREAM == optVal)  // SOCK_STREAM宏定义值为1
			printf("The current socket is a stream socket.\n");
		else if (SOCK_DGRAM == optVal) // SOCK_ DGRAM宏定义值为2
			printf("The current socket is a datagram socket.\n");
	}
	getchar();
	return 0;
}

3.4 示例:判断套接字是否处于监听状态

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
 
typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;

#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
        Addr_in service;
        if(argc < 3)
        {
                printf("%s[ADDR][PORT]\n", argv[0]);
                exit(0);
        }
        int s = socket(AF_INET, SOCK_STREAM, 0); //创建一个流套接字
        if (s == -1) 
                ErrExit("socket");
        //允许地址的立即重用
        char on = 1;
        setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

        service.sin_family = AF_INET;
        service.sin_addr.s_addr = inet_addr(argv[1]);
        service.sin_port = htons( atoi(argv[2]) );
        if (bind(s, (Addr*)&service, sizeof(service)) == -1) //绑定套接字
                ErrExit("bind");
        int optVal;
        int optLen = sizeof(optVal);
        //获取选项SO_ACCEPTCONN的值
        if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, (char*)&optVal, (socklen_t*)&optLen) == -1)
                printf("getsockopt failed:%d",errno);
        else printf("Before listening, The value of SO_ACCEPTCONN:%d, The socket is not listening\n", optVal);

        // 开始侦听
        if (listen(s, 100) == -1)
                ErrExit("listen");
        //获取选项SO_ACCEPTCONN的值
        if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, (char*)&optVal, (socklen_t*)&optLen) == -1)
                ErrExit("getsockopt");
        else printf("After listening,The value of SO_ACCEPTCONN:%d, The socket is listening\n", optVal);
        return 0;
}

4 设置套接字选项

4.1 setsockopt函数

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

参数
sockfd:套接字描述符
level:表示选项的级别
optname:表示要获取的选项名称
optval:指向存放接收到的选项内容的缓冲区
optlen:指向optval所指缓冲区的大小
函数返回值:
执行成功返回0,否则返回‒1errno来获取错误码

4.2 示例:启用套接字的保活机制**

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
 
typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;

#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
        Addr_in service;
        if(argc < 3)
        {
                printf("%s[ADDR][PORT]\n", argv[0]);
                exit(0);
        }

        int s = socket(AF_INET, SOCK_STREAM, 0); //创建一个流套接字
        if( s < 0)
                ErrExit("socket");

        char on = 1;
        setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

        service.sin_family = AF_INET;
        service.sin_addr.s_addr = inet_addr(argv[1]);
        service.sin_port = htons(atoi(argv[2]));
        if (bind(s, (Addr *) &service, sizeof(service)) == -1) //绑定套接字
                ErrExit("bind");

        int optVal = 1;//一定要初始化
        int optLen = sizeof(int);

        //获取选项SO_KEEPALIVE的值
        if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)&optVal, (socklen_t *)&optLen) == -1)
                ErrExit("getsockopt");
        else printf("After listening,the value of SO_ACCEPTCONN:%d\n", optVal);

        optVal = 1;
        if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)&optVal, optLen) != -1)
                printf("Successful activation of keep alive mechanism.\n");//启用保活机制成功

        if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)&optVal, (socklen_t *)&optLen) == -1)
                ErrExit("getsockopt");
        else printf("After setting,the value of SO_KEEPALIVE:%d\n", optVal);

        return 0;
}

5 综合示例

server.c

#include "net.h"
#include <sys/select.h>
#define MAX_SOCK_FD 1024

void setKeepAlive (int sockfd, int attr_on, socklen_t idle_time, socklen_t interval, socklen_t cnt)
{
	setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char *) &attr_on, sizeof (attr_on));
	setsockopt (sockfd, SOL_TCP, TCP_KEEPIDLE, (const char *) &idle_time, sizeof (idle_time));
	setsockopt (sockfd, SOL_TCP, TCP_KEEPINTVL, (const char *) &interval, sizeof (interval));
	setsockopt (sockfd, SOL_TCP, TCP_KEEPCNT, (const char *) &cnt, sizeof (cnt));
}

int main(int argc, char *argv[])
{
	int i, ret, fd, newfd;
	fd_set set, tmpset;
	Addr_in clientaddr;
	socklen_t clientlen = sizeof(Addr_in);
	/*检查参数,小于3个 直接退出进程*/
	Argment(argc, argv);
	/*创建已设置监听模式的套接字*/
	fd = CreateSocket(argv);

	FD_ZERO(&set);
	FD_ZERO(&tmpset);
	FD_SET(fd, &set);
	while(1){
		tmpset = set;
		if( (ret = select(MAX_SOCK_FD, &tmpset, NULL, NULL, NULL)) < 0){
			perror("select");
			getchar();
		}
		if(FD_ISSET(fd, &tmpset) ){
			/*接收客户端连接,并生成新的文件描述符*/
			if( (newfd = accept(fd, (Addr *)&clientaddr, &clientlen) ) < 0){
				perror("accept");
				getchar();
			}
#if 1
			int keepAlive = 1;			//设定KeepAlive
			int keepIdle = 5;			//开始首次KeepAlive探测前的TCP空闭时间
			int keepInterval = 5;		//两次KeepAlive探测间的时间间隔
			int keepCount = 3;			//判定断开前的KeepAlive探测次数
			setKeepAlive (newfd, keepAlive, keepIdle, keepInterval, keepCount);
#endif

			printf("[%s:%d]已建立连接\n", 
					inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
			FD_SET(newfd, &set);
		}else{ //处理客户端数据
			for(i = fd + 1; i < MAX_SOCK_FD; i++){
				if(FD_ISSET(i, &tmpset)){
					if( DataHandle(i) <= 0){
						if( getpeername(i, (Addr *)&clientaddr, &clientlen) )
							perror("getpeername");
						printf("[%s:%d]断开连接\n", 
								inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
						FD_CLR(i, &set);
						close(i);
					}
				}
			}
		}
	}
	close(fd);
	return 0;
}

socket.c

#include "net.h"

void Argment(int argc, char *argv[]){
	if(argc < 3){
		fprintf(stderr, "%s<addr><port>\n", argv[0]);
		exit(0);
	}
}
int CreateSocket(char *argv[]){
	/*创建套接字*/
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd < 0)
		ErrExit("socket");
	/*允许地址快速重用*/
	int flag = 1;
	if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag) ) )
		perror("setsockopt");
	/*设置通信结构体*/
	Addr_in addr;
	bzero(&addr, sizeof(addr) );
	addr.sin_family = AF_INET;
	addr.sin_port = htons( atoi(argv[2]) );
	/*绑定通信结构体*/
	if( bind(fd, (Addr *)&addr, sizeof(Addr_in) ) )
		ErrExit("bind");
	/*设置套接字为监听模式*/
	if( listen(fd, BACKLOG) )
		ErrExit("listen");
	return fd;
}
int DataHandle(int fd){
	char buf[BUFSIZ] = {};
	Addr_in peeraddr;
	socklen_t peerlen = sizeof(Addr_in);
	if( getpeername(fd, (Addr *)&peeraddr, &peerlen) )
		perror("getpeername");
	int ret = recv(fd, buf, BUFSIZ, 0);
	if(ret < 0)
		perror("recv");
	if(ret > 0){
		printf("[%s:%d]data: %s\n", 
				inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf);
	}
	return ret;
}

net.h

`#ifndef _NET_H_
#define _NET_H_

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>

typedef struct sockaddr Addr;
typedef struct sockaddr_in Addr_in;
#define BACKLOG 5
#define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)

void Argment(int argc, char *argv[]);
int CreateSocket(char *argv[]);
int DataHandle(int fd);

#endif

通过wireshark查看设置keepalive的包
在这里插入图片描述

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

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

相关文章

Android组件通信——消息机制(二十六)

1. 消息机制 1.1 知识点 &#xff08;1&#xff09;掌握Message、Handler、Looper类的使用以及消息的传递&#xff1b; &#xff08;2&#xff09;可以通过消息机制动态取得信息&#xff1b; 1.2 具体内容 对于android的消息机制&#xff0c;我们主要要使用Java中线程的一…

医生访问学者出国进修必备面试技巧

医生访问学者出国进修&#xff0c;一直以来都是医学领域内追求更高学术水平和国际化视野的重要途径之一。然而&#xff0c;要成功进入国外院校或研究机构进行进修&#xff0c;首先需要通过面试&#xff0c;因此&#xff0c;面试技巧显得尤为关键。本文知识人网小编将为您介绍一…

LeetCode 739 每日温度(单调栈的初步了解)

1、重新学习了栈的操作&#xff0c;isEmpty()、peek()以及pop()、push()操作 但是值得注意的点是push()必须要有输入 2、单调栈用在这里非常巧妙&#xff0c;通过暴力搜索的方法无法通过最后一个用例 并且通过使用单调栈可以使得时间复杂度从O()降到了O() 3、Deque<Inte…

竞赛 深度学习+opencv+python实现车道线检测 - 自动驾驶

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &am…

回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测(多指标,多图)

回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09;…

关于信息安全软考的记录6

1、入侵检测相关概念 及 入侵检测模型 入侵&#xff1a;违背访问目标的安全策略的行为 判断入侵的依据是&#xff1a;对目标的操作是否超出了目标的安全策略范围 入侵检测&#xff1a;通过收集操作系统、系统程序、应用程序、网络包等信息&#xff0c;发现系统中违背安全策略…

【unity】【VR】白马VR课堂系列-VR开发核心基础04-主体设置-XR Rig的引入和设置

接下来我们开始引入并构建XR Rig。 你可以将XR Rig理解为玩家在VR世界中的替身。 我们先删除Main Camera&#xff0c;在Hierarchy右键点击删除。 然后再在场景层右键选择XR下的XR Origin。这时一个XR Origin对象就被添加到了Hierarchy。 重设XR Origin的Position和Rotation…

C++中将十六进制数转化为字符串数据

C中将十六进制数转化为字符串数据 1、十六进制转字符串2、string转char[]3、调用4、调试结果 1、十六进制转字符串 std::string Number2HexStr( uint32_t mData ) {std::stringstream ss;ss << std::hex << std::setw(2) << std::setfill(0) << (int)…

快速排序 O(nlgn)

大家好&#xff0c;我是蓝胖子&#xff0c;我一直相信编程是一门实践性的技术&#xff0c;其中算法也不例外&#xff0c;初学者可能往往对它可望而不可及&#xff0c;觉得很难&#xff0c;学了又忘&#xff0c;忘其实是由于没有真正搞懂算法的应用场景&#xff0c;所以我准备出…

el-dialog两个弹框里面套弹框受外层弹框影响

el-dialog嵌套的影响及解决方法 解决方法如下&#xff1a; 在里层弹框里添加 append-to-body <el-dialogtitle"图片预览":visible.sync"dialogVisible"class"imgDialog":modal"false"append-to-body><img width"100%&q…

分享一个查询OpenAI Chatgpt key余额查询的工具网站

OpenAI Key 余额查询工具 欢迎使用 OpenAI Key 余额查询工具网站&#xff01;这个工具可以帮助您轻松地验证您的 OpenAI API 密钥&#xff0c;并查看您的余额。 http://tools.lbbit.top/check_key/ 什么是 OpenAI Key 余额查询工具&#xff1f; OpenAI Key 余额查询工具是一…

要想成为黑客,离不开这些资料

目录 一、想入门学黑客&#xff0c;去哪里找详细的教程&#xff1f; 二、适合新人入门的书籍 三、相关网站推荐 四、在线靶场 五、Web安全学习路线 六、Web安全入门基础学习 小白在学习黑客的过程中一般会遇到这样一些问题&#xff1a;感觉自己工具、原理都会了但是遇到真…

ue5打包失败与优化项目

打包报错&#xff1a; PackagingResults: Error: Multiple game targets found for project. Specify the desired target using the -Target... argument. 解决方案&#xff1a; 关闭项目后&#xff0c;删除项目目录下的 Intermediate 文件 再重新启动项目打包即可 参考&…

小学英语教学计划模板范文 英语优秀教案模板

小学英语课教学计划模板&#xff1a; 课程时长&#xff1a;40分钟/节 课程目标&#xff1a;本课程的目标是让学生达到一定的英语水平&#xff0c;包括词汇、语法、听说读写能力等。 授课内容&#xff1a; 主题 1&#xff1a;Unit 1 Greetings 内容&#xff1a;学习如何用英…

[Python小项目] 从桌面壁纸到AI绘画

从桌面壁纸到AI绘画 一、前言 1.1 确认问题 由于生活和工作需要&#xff0c;小编要长时间的使用电脑&#xff0c;小编又懒&#xff0c;一个主题用半年的那种&#xff0c;所以桌面壁纸也是处于常年不更换的状态。即时改变主题也是在微软自带的壁纸中选择&#xff0c;而这些自…

机器学习-有监督学习-神经网络

目录 线性模型分类与回归感知机模型激活函数维度诅咒过拟合和欠拟合正则数据增强数值稳定性神经网络大家族CNNRNNGNN&#xff08;图神经网络&#xff09;GAN 线性模型 向量版本 y ⟨ w , x ⟩ b y \langle w, x \rangle b y⟨w,x⟩b 分类与回归 懂得两者区别激活函数&a…

项目成本超支的主要原因以及解决方法

成本超支&#xff0c;是每个项目经理在其职业生涯中都会遇到的一个问题。当项目的实际成本超过估计或预算成本时&#xff0c;就会发生成本超支。这在建筑、制造和软件开发项目中尤其常见&#xff0c;并影响着项目的盈利能力、利益相关者满意度和竞争优势。 成本超支的原因 由…

LINUX定时解压缩方案

需求背景 对接客户中某个上游为外包系统&#xff0c;外包系统每日推送压缩文件至指定文件夹下&#xff0c;文件格式为YYYYMMDD_RegReport.zip。由于每日采集文件&#xff0c;无法对接压缩包内文件&#xff0c;需要将推送的压缩文件每日解压为文件夹 需求分析 与客户沟通后&a…

苹果电脑其他内存怎么清理?

苹果电脑中的应用程序大部分是可以通过将其拖拽至废纸篓并倾倒来卸载的。但是部分程序在卸载后仍有残留文件&#xff0c;比如support文件和pref设置等文件的。小编今天介绍下苹果电脑清理内存怎么清理卸载残留以及好用的清理技巧分享。 一、苹果电脑清理内存怎么清理 苹果电脑…

分享38个AI绘画网站

本文是参考AI沉思录「1000AI」栏目的第十二期&#xff0c;「1000AI」栏目专注研究有哪些AI产品&#xff0c;目标研究1000AI产品(进度:532/1000)。 AI沉思录 ​aichensilu.com/ 1、Midjourney 网址&#xff1a;https://www.midjourney.com/ 基于diffusion的AI艺术生成器。生成…