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

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);elseprintf("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);elseprintf("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);elseprintf("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宏定义值为1printf("The current socket is a stream socket.\n"); //当前套接字是流套接字else if (SOCK_DGRAM == optVal) // SOCK_ DGRAM宏定义值为2printf("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宏定义值为1printf("The current socket is a stream socket.\n");else if (SOCK_DGRAM == optVal) // SOCK_ DGRAM宏定义值为2printf("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 1024void 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 1int keepAlive = 1;			//设定KeepAliveint keepIdle = 5;			//开始首次KeepAlive探测前的TCP空闭时间int keepInterval = 5;		//两次KeepAlive探测间的时间间隔int keepCount = 3;			//判定断开前的KeepAlive探测次数setKeepAlive (newfd, keepAlive, keepIdle, keepInterval, keepCount);
#endifprintf("[%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.rhkb.cn/news/157632.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ansible概述和模块解释

一、Ansible概述 Ansible介绍 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可以实现。 Ansible能做什么 Ansi…

呼吁社区共同维护Sui品牌和商标

Sui社区成员可以通过举报Sui商标和品牌资产的不当使用来帮助保护网络的信誉。Sui商标政策解释了logo和名称的可接受和不可接受的使用方式。这些展示代表Sui面向公众&#xff0c;而善意行为者的正确使用有助于维护Sui的声誉。 Sui网络在公众中享有良好声誉&#xff0c;Sui社区都…

GEO生信数据挖掘(七)差异基因分析

上节&#xff0c;我们使用结核病基因数据&#xff0c;做了一个数据预处理的实操案例。例子中结核类型&#xff0c;包括结核&#xff0c;潜隐进展&#xff0c;对照和潜隐&#xff0c;四个类别。本节延续上个数据&#xff0c;进行了差异分析。 差异分析 计算差异指标step12 加载…

API攻防-接口安全SOAPOpenAPIRESTful分类特征导入项目联动检测

文章目录 概述什么是接口&#xff1f; 1、API分类特征SOAP - WSDLWeb services 三种基本元素&#xff1a; OpenApi - Swagger UISpringboot Actuator 2、API检测流程Method&#xff1a;请求方法URL&#xff1a;唯一资源定位符Params&#xff1a;请求参数Authorization&#xff…

阿里云r7服务器内存型CPU采用

阿里云服务器ECS内存型r7实例是第七代内存型实例规格族&#xff0c;CPU采用第三代Intel Xeon可扩展处理器&#xff08;Ice Lake&#xff09;&#xff0c;基频2.7 GHz&#xff0c;全核睿频3.5 GHz&#xff0c;计算性能稳定&#xff0c;CPU内存比1:8&#xff0c;2核16G起步&#…

NSIDC定义的海冰相关概念

文章目录 相关概念Matlab绘图结果展示 相关概念 NSIDC 表示 “National Snow and Ice Data Center”&#xff0c;即美国国家雪和冰数据中心。NSIDC 是一个位于美国科罗拉多大学波尔得分校的研究中心&#xff0c;致力于收集、管理和分发全球雪和冰的科学数据。 Matlab绘图 cl…

2015架构真题(五十)

供应链中信息流覆盖了供应商、制造商和分销商&#xff0c;信息流分为需求信息流和供应信息流&#xff0c;&#xff08;&#xff09;属于需求信息流&#xff0c;&#xff08;&#xff09;属于供应信息流。 库存记录生产计划商品入库单提货发运单 客户订单采购合同完工报告单销售…

迅为RK3568开发板Scharr滤波器算子边缘检测

本小节代码在配套资料“iTOP-3568 开发板\03_【iTOP-RK3568 开发板】指南教程\04_OpenCV 开发配套资料\33”目录下&#xff0c;如下图所示&#xff1a; 在 Sobel 算子算法函数中&#xff0c;如果设置 ksize-1 就会使用 3x3 的 Scharr 滤波器。Scharr 算子是 Soble 算子在 ksize…

leetcode oj

150. 逆波兰表达式求值 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;定义一个名为 Solution 的类&#xff0c;并在其中定义了一个名为 evalRPN 的公共函数。这个函数接受一个由字符串组成的向量 tokens 作为输入&#xff0c;并返回一个整数。 在代码中&#xff0…

【C++初阶】关键字命名空间缺省函数函数重载入门必看!!!超详解!!!

主页点击直达&#xff1a;个人主页 我的小仓库&#xff1a;代码仓库 C语言偷着笑&#xff1a;C语言专栏 数据结构挨打小记&#xff1a;初阶数据结构专栏 Linux被操作记&#xff1a;Linux专栏 LeetCode刷题掉发记&#xff1a;LeetCode刷题 算法&#xff1a;算法专栏 C头…

嵌入式系统开发【深入浅出】 EXTI 与 NVIC

目录 CPU 感知外部事件变化的三种方式 中断分三个级别 中断控制器 STM32 的中断和异常 NVIC 中断控制器 NVIC 结构体成员 抢占优先级和响应优先级 简单配置NVIC中断控制器 EXTI 外部中断【中断源级】​ STM32系列微控制器实际上最多有23根外部中断线&#xff08;EXT…

【管理运筹学】第 10 章 | 排队论(1,排队论的基本概念)

文章目录 引言一、基本概念1.1 排队过程1.2 排队系统的组成和特征1.3 排队模型的分类1.4 系统指标1.5 系统状态 引言 开一点排队论的内容吧&#xff0c;方便做题。 排队论&#xff08;Queuing Theory&#xff09;也称随机服务系统理论&#xff0c;是为解决一系列排队问题&…

docker入门加实战—从部署MySQL入门docker

docker入门加实战—从部署MySQL入门docker docker部署MySQL 输入如下命令&#xff1a; docker run -d \--name mysql \-p 3306:3306 \-e TZAsia/Shanghai \-e MYSQL_ROOT_PASSWORD123 \mysql部署成功截图如下&#xff1a; 当执行命令后&#xff0c;Docker做的第一件事情&…

Grafana 10 新特性解读:体验与协作全面提升

作者&#xff1a;徽泠(苏墨馨) 为了庆祝 Grafana 的 10 年里程碑&#xff0c;Grafana Labs 推出了 Grafana 10&#xff0c;这个具有纪念意义的版本强调增强用户体验&#xff0c;使各种开发人员更容易使用。Grafana v10.0.x 为开发者与企业展示卓越的新功能、可视化与协作能力&…

java中的接口interface

一、面向对象基本概念 Java是一种面向对象的语言&#xff0c;其中「对象」就相当于是现实世界中的一个个具体的例子&#xff0c;而「类」就相当于是一个抽象的模板&#xff0c;将抽象的概念模板转化为具体的例子的过程就叫做「实例化」。 比如说人这个概念就是一个抽象化的「…

VSCode 快速移动光标至行尾

最近在用vscode进行C编程&#xff0c;经常需要把光标跳到行尾去添加符号。 手动到行尾太麻烦了。 一种快捷方式是&#xff1a;用键盘上的“END”快捷键。 但是用这个键也不是很方便&#xff0c;因为“end”键离主键盘区太远。 另一种便捷的方式是&#xff1a;给vscode设置自定义…

TypeScript React(上)

目录 扩展学习资料 TypeScript设计原则 TypeScript基础 语法基础 变量声明 JavaScript声明变量 TypeScript声明变量 示例 接口 (标准类型-Interface) 类型别名-Type 接口 VS 类型别名 类型断言:欺骗TS&#xff0c;肯定数据符合结构 泛型、<大写字母> 扩展学习…

小谈设计模式(24)—命令模式

小谈设计模式&#xff08;24&#xff09;—命令模式 专栏介绍专栏地址专栏介绍 命令模式角色分析命令&#xff08;Command&#xff09;具体命令&#xff08;ConcreteCommand&#xff09;接收者&#xff08;Receiver&#xff09;调用者&#xff08;Invoker&#xff09;客户端&am…

Linux 网络编程 tcp server 笔记

一、TCP 服务器的创建 在 Linux 上创建一个简单的 tcp 服务器步骤如下&#xff1a; ①创建套接字 ②将套接字绑定到 IP 地址和端口号 ③监听来自客户端的连接 ④接受连接并创建新的套接字用于与客户端通信 ⑤通过新建的套接字发送和接收数据 ⑥关闭套接字 流程框图如下&#xf…

Spring是什么?为什么要使用Spring?

目录 前言 一、Spring是什么&#xff1f; 1.1 轻量级 1.2 JavaEE的解决方案 二、为什么要使用Spring 2.1 传统方式完成业务逻辑 2.2 使用Spring模式完成业务逻辑 三、为什么使用Spring&#xff1f; 前言 本文主要介绍Spring是什么&#xff0c;并且解释为何要去使用Spring&…