【计算机网络】网络编程接口 Socket API 解读(8)

         Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。

        本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。


socket(7)

send()           遵循 POSIX.1 - 2008

MSG_CONFIRM 是 Linux 扩展

1.库

标准 c 库,libc, -lc

2.头文件

<sys/socket.h>

3.接口定义

       sockfd = socket(int socket_family, int socket_type, int protocol);

4.接口描述

       本文主要描述 Linux 网络套接字层的用户编程接口。 BSD 兼容的套接字是用户进程和内核网络协议栈的统一接口。各协议模块被分配不同的协议家族(AF_INET、AF_IPX、AF_PACKET 等)以及不同的套接字类型(如 SOCK_STREAM、SOCK_DGRAM),参考 socket(2) 获取更多关于协议家族和类型的信息。

套接字层函数

        这些套接字层函数是用户进程用来发送和接收数据包以及其他套接字操作。

        socket(2) 创建一个套接字,connect(2) 连接一个套接字到一个远程套接字地址,bind(2) 将一个套接字绑定到一个本地套接字地址上,listen(2) 告知套接字有新连接需要被接受,accept(2) 用来获取新来连接的新的套接字,socketpair(2) 返回两个连接的匿名套接字(只有类似 AF_UNIX 的本地套接字才有这个实现)。

        send(2)、sendto() 和 sendmsg(2) 在套接字上发送数据,recv(2)、recvfrom、recvmsg(2) 从套接字上接收数据。poll(2) 和 select(2) 等待数据来临或者是否发送数据就绪。此外,标准的 I/O 操作类似 write(2)/writev(2)/sendfile(2)/read(2)/readv(2) 可以用来读写套接字上的数据。

        getsockname(2) 返回本地套接字地址,getpeername(2) 返回远程套接字地址。getsockopt(2) 和 setsockopt(2) 用来设置/获取套接字层或者协议层选项。ioctl(2) 可以用来设置或者读取一些其他选项。

        close(2) 用来关闭一个套接字。shutdown(2) 关闭全双工套接字双方。

        Seeking 或者 pread(2)、pwrite(2) 这种从非 0 位置读写操作套接字是不支持的。

        可以通过 fcntl(2) 来设置一个套接字文件描述符的非阻塞标记,实现套接字的非阻塞 I/O 操作。一旦设置,套接字上所有可能导致阻塞状态通常都会返回 EAGAIN(表示操作稍后需要重试),connect(2) 会返回 EINPROGRESS 错误。用户可以通过 poll(2) 或者 select(2) 来等待各种事件。

I/O 事件
事件轮询标记发生时机
POLLIN新数据到达
POLLIN一个连接配置已完成(对于面向连接的套接字)
POLLHUP对端发起了断开连接请求
POLLHUP连接断开了(对于面向连接的套接字),当写套接字时,会发送 SIGPIPE 信号
POLLOUT套接字具有足够的空间来写入新数据
读写POLLIN/POLLOUT向外连接的 connect(2) 完成
读写POLLERR发生了异步错误
读写POLLHUP对端关闭了一个方向的连接
异常POLLPRI紧急数据到达,随后会发送 SIGURG。

         另一个代替 poll(2)/select(2) 的方式是内核通过 SIGIO 信号通知应用程序,对于这种方式,必须通过 fcntl(2) 来设置套接字文件描述符的 O_ASYNC 标记,然后通过 sigaction(2) 安装 SIGIO 的信号处理函数,可以参考后面关于信号的讨论。

套接字地址结构

        每个套接字域(domain)都有自己的套接字地址格式。每个结构以一个整型的“家族”字段(sa_family_t) 来指示地址结构的类型,各种系统调用,比如 connect(2)、bind(2)、accept(2)、getsockname(2)、getpeername(2) 是各个套接字域通用的,可以通过家族和类型来区分不同域的特定套接字地址。

        为了允许任何类型的套接字地址都可以传递到各个套接字 API,我们定义了 struct sockaddr,目的是将各域特定的地址类型转换为通用的类型,避免调用套接字 API 时编译器报告类型不匹配警告。

        此外,套接字 API 也提供了 struct sockaddr_storage 数据类型。这个类型足以装下所有域特定的套接字地址结构,并且处理了对齐问题。(尤其是它已经能够装下 IPv6 套接字地址。)数据结构包含下面的字段,这个字段可以用来识别结构中实际存储的套接字地址类型:

    sa_family_t ss_family;

        sockaddr_storage 结构在以通用方式处理套接字地址时非常有用(也就是程序同时处理 IPv4 和 IPv6 套接字地址)。

 套接字选项

        下面列出的套接字选项可以通过 setsockopt(2) 来设置,也可以通过 getsockopt(2) 设置套接字级别参数为 SOL_SOCKET 来读取这些选项。除非特别说明,否则 optval 是一个指向整型数据的指针。

        SO_ACCEPTCONN

        返回值指示套接字是否被标记为可以通过 listen(2) 接收连接。返回 0 表示非可监听套接字,返回 1 表示是一个监听套接字。这个选项是只读的。

        SO_ATTACH_FILTER(Linux 2.2 后),SO_ATTACH_BPF(Linux 3.19 后)

        挂载一个经典的 BPF(SO_ATTACH_FILTER)或者扩展 BPF 程序到套接字上,来过滤进来的数据包。如果程序返回 0,那么数据包会被丢弃,如果返回值比数据包长度小,那么数据包会被截断。如果返回值大于等于数据包长度,那么数据包可以被原封不动的处理。

        SO_ATTACH_FILTER 定义在 <linux/filter.h> 中,是一个 sock_fprog 类型的结构体:

                  struct sock_fprog {unsigned short      len;struct sock_filter *filter;};

         SO_ATTACH_BPF 的参数是一个通过 bpf(2) 系统调用返回的文件描述符,必须指向一个 BPF_PROG_TYPE_SOCKET_FILTER 类型的程序。

        对于指定套接字,这些选项可以设置多次,新的设置会覆盖之前的设置。经典和扩展版本可以在同一个套接字上使用,但是之前的过滤器总是会被新的过滤器代替,也就是说一个套接字上同一时刻只能定义一个过滤器。

        经典和扩展 BPF 在 Linux 内核源码文件 /Documentation/networking/filter.txt 中有解释。

SO_ATTACH_REUSEPORT_CBPF,SO_ATTACH_REUSESETPORT_EBPF

        在使用 SO_REUSEPORT 选项时,用户可以用这些选项来设置经典 BPF(SO_ATTACH_REUSEPORT_CBPF) 或者扩展 BPF(SO_ATTACH_REUSEPORT_EBPF)程序,这些程序定义了reuseport 端口组中的套接字的数据包如何过滤(也就是所有设置了 SO_REUSEPORT 并使用相同本地地址接收数据包的套接字)。

        BPF 程序必须返回一个 0 到 N-1 的索引值表示哪个套接字应该接收数据包(N 是套接字组中套接字的数量)。如果 BPF 程序返回非法索引值,套接字选择会回退到没设置这些选项时的 SO_REUSEPORT 机制。

        为了将套接字加入到组中,每个套接字都按照加入的顺序编号(即,UDP 套接字按照 bind(2) 调用的顺序, TCP 套接字按照 listen(2) 调用的顺序)。新加入 reuse 组的套接字会继承 BPF 程序,移除时,最后一个套接字会移动到该套接字位置。

        这些选项可以在组内任何套接字上设置多次,来更新组内所有套接字使用的 BPF 程序。

        SO_ATTACH_REUSEPORT_CBPF 和 SO_ATTACH_FILTER 携带相同的参数类型,SO_ATTACH_REUSEPORT_EBPF 和 SO_ATTACH_BPF 携带相同的参数类型。

        UDP 从 Linux 4.5 后支持这个特性,TCP 是从 Linux 4.6 后支持的。

SO_BINDTODEVICE

        将一个套接字绑定到特定的诸如 "eth0" 这样的设备上,在传递的接口名称中指定。如果名字是一个空字符串或者选项长度是 0,那么套接字绑定会被移除。传进来的选项是一个变长、‘\0’ 结尾的接口名称字符串,最大长度为 IFNAMESIZ。如果套接字被绑定到特定接口,那么套接字只会处理该接口进来的数据包。值得注意的是,这个只对特定套接字类型有用,尤其是 AF_INET 套接字。分组(packet)套接字不支持这个特性(使用普通的 bind(2))。

        在 Linux 3.8 之前,这个套接字选项可以设置但是不能通过 getsockopt(2) 获取,Linux 3.8 后就可以读了。optlen 参数包含用于接收设备名字的缓冲区大小,建议设置为 IFNAMSIZ 字节,真实的设备名字长度会在 optlen 参数报告出来。

SO_BROADCAST

        设置/获取广播标记。开启后,数据报套接字可以向广播地址发送数据包,这个选项对于流套接字无效。

SO_BSDCOMPAT

        开启 BSP 错误兼容。这个只在 Linux 2.0 和 2.2 的 UDP 协议模块中使用。如果使能,UDP 套接字的 ICMP 错误不会被传递给用户程序,后面的内核版本中逐步淘汰这个选项。Linux 2.4 悄悄的忽略这个设置,Linux 2.6 会在用户设置这个选项时生成内核警告(printk())。Linux 2.0 对于原始套接字默认开启了这个选项,但是很快就在 Linux 2.2 中就移除了这个设置。

SO_DEBUG

        开启套接字调试。只允许具有 CAP_NET_ADMIN 能力、或者有效用户 ID 为 0 的进程设置开启该选项。

SO_DETACH_FILTER(Linux 2.2 后),SO_DETACH_BPF(Linux 3.19 后)

        这两个选项意思相同,可以用来移除套接字上使用 SO_ATTACH_FILTER 或者 SO_ATTACH_BPF 绑定的经典/扩展 BDF 程序,选项值会被忽略。

SO_DOMAIN(Linux 2.6.32 后)

        获取套接字的域(整数值),返回类似 AF_INET6 这样的值,参考 socket(2) 更多详细信息,这个套接字选项是只读的。

SO_ERROR

        获取/清除套接字上的错误。这个套接字选项也是只读的,返回一个整型数值。

SO_DONTROUTE

        不要通过网关发送,直接发送到连接的主机。这个和 send(2) 时设置 MSG_DONTROUTE 标记效果相同。期待返回整型布尔标记。

SO_INCOMING_CPU(Linux 3.19 后可读取,Linux 4.4 后可设置)

        获取或者设置套接字的 CPU 亲和性,是一个整型标记:

                  int cpu = 1;setsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu,sizeof(cpu));

         因为一个单独流上的所有数据包都是在一个特定 CPU 上的 RX 队列中到达,这个选项的典型应用是每个 RX 队列使用一个监听进程,然后将监听进程和 RX 队列处理进程处于同一个 CPU 上。这个是 NUMA 上的最佳用法,能够保持 CPU 缓存热度。

SO_INCOMING_NAPI_ID(Linux 4.12 后可读取)

        返回一个系统级的唯一 ID,即 NAPI ID,这个 ID 是套接字上最后收到数据包所在 RX 队列的标识。

        应用程序可以用这个来对不同 RX 队列上的数据流使用不同的工作线程来进行分流,这就允许每个工作线程和一个 NIC 硬件接收队列关联,为这个上面 RX 队列上接收到的所有连接服务。硬件 NIC 队列到应用线程上的映射使得 NIC 到应用的数据流处理更加高效。

SO_KEEPALIVE

        在面向连接的套接字上开启保活消息发送,需要的是一个整型布尔型标记。

SO_LINGER

        设置/获取 SO_LINGER 选项,参数是一个 struct linger 结构体:

                  struct linger {int l_onoff;    /* linger active */int l_linger;   /* how many seconds to linger for */};

         开启后,close(2) 和 shutdown(2) 操作会在所有消息都被发送后才返回,或者达到了存留超时值。否则调用会立即返回,而关闭操作会在后台进行。exit(2) 中关闭套接字时,它总是在后台留存(linger)。

SO_LOCK_FILTER

        设置这个选项会阻止修改套接字的过滤器,这些过滤器包括通过 SO_ATTACH_FILTER、SO_ATTACH_BPF、SO_ATTACH_REUSEPORT_CBPF、SO_ATTACH_REUSEPORT_EBPF 设置的。

        典型的应用场景是特权进程设置一个原始套接字(一个需要 CAP_NET_RAW 权限的操作)、应用一个限制过滤器、设置 SO_LOCK_FILTER 选项、然后或者放弃特权或者通过 UNIX 域套接字将套接字文件描述符传递给非特权进程。

        一旦 SO_LOCK_FILTER 选项开启了,尝试修改或者移除套接字上的过滤器或者禁止 SO_LOCK_FILTER 选项会报错 EPERM。

SO_MARK(Linux 2.6.25 后)

        设置由该套接字发送的数据包的标记(mark)(和 netfilter MARK 类似,只是基于套接字的)。修改标记可以用于没有 netfilter 或者分组过滤器的基于标记的路由。设置这个选项要求 CAP_NET_ADMIN 能力。

SO_OOBINLINE

        如果这个选项开启,带外数据会直接放入接收数据流中。否则,带外数据只有在 MSG_OOB 标记设置时才会接收时才会这样做。

SO_PASSCRED

        使能/禁能接收 SCM_CREDENTIALS 控制消息。更多信息,参考 unix(7)。

SO_PASSSEC

        使能/禁能接收 SCM_SECURITY 控制消息。更多信息,参考 unix(7)。        

更多选项,阅读下一篇 【计算机网络】网络编程接口 Socket API 解读(9)

5.注意

        Linux 假定发送/接受缓冲区的一半用于内部内核结构,因此对应的 /proc 文件大小是线上可观测大小的两倍。

        Linux 只有在之前调用 bind(2) 程序和新程序都设置了 SO_REUSEADDR 时才会允许端口重用。这个和其他实现(如 FreeBSD) 只要求新程序设置就好了是不一样的,对于服务器程序总是设置这个选项的情况,这个区别是不可见的。

6.示例代码

        下面是一个 getsockopt 函数的使用代码:

int rc;
int s;
int option_value;
int option_len;
struct linger l;
int getsockopt(int s, int level, int option_name,
char *option_value,int *option_len);⋮
/* Is out-of-band data in the normal input queue? */
option_len = sizeof(int);
rc = getsockopt(s, SOL_SOCKET, SO_OOBINLINE, (
char *) &option_value, &option_len);
if (rc == 0)
{if (option_len == sizeof(int)){if (option_value)/* yes it is in the normal queue */else/* no it is not*/}
}⋮
/* Do I linger on close? */
option_len = sizeof(l);
rc = getsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &l, &option_len);
if (rc == 0)
{if (option_len == sizeof(l)){if (l.l_onoff)/* yes I linger */else/* no I do not  */}
}

     

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

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

相关文章

IDEA 2021.2.2设置自动热部署

1.导入包坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency> 2.pom.xml添加piugins插…

车载网络诊断应如何测试?

文章目录 一、前言二、测试内容三、测试设备和台架方案四、测试脚本及工程五、其他一、前言 目前车上主流的网络有CAN、LIN、ETH(以太网)。 按照测试环境可以划分为单件测试,系统测试,整车测试。 我们来看下CAN和以太网的分层图: CAN的分层图: 以太网的分层图: 最好的…

【C++】STL详解(十二)—— 用哈希表封装出unordered_map和unordered_set

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C学习 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【C】STL…

基于Dockerfile搭建LNMP

目录 一、基础环境准备 1、环境前期准备 二、部署nginx&#xff08;容器IP 为 172.18.0.10&#xff09; 1、配置Dockerfile文件 2、配置nginx.conf文件 3、构建镜像、启动镜像 三、部署mysql 1、配置Dockerfile文件 2、配置my.conf文件 3、构建镜像、启动镜像 5、验…

经典算法----迷宫问题(找出所有路径)

目录 前言 问题描述 算法思路 定义方向 回溯算法 代码实现 前言 前面我发布了一篇关于迷宫问题的解决方法&#xff0c;是通过栈的方式来解决这个问题的&#xff08;链接&#xff1a;经典算法-----迷宫问题&#xff08;栈的应用&#xff09;-CSDN博客&#xff09;&#xff…

One Thread One Loop主从Reactor模型⾼并发服务器

One Thread One Loop主从Reactor模型⾼并发服务器 文章目录 One Thread One Loop主从Reactor模型⾼并发服务器一些补充HTTP服务器Reactor 模型eventfd通用类Any 目标功能模块划分&#xff1a;SERVER模块Buffer模块&#xff1a;编写思路&#xff1a;接口设计&#xff1a;具体实现…

详谈Spring

作者&#xff1a;爱塔居 专栏&#xff1a;JavaEE 目录 一、Spring是什么&#xff1f; 1.1 Spring框架的一些核心特点&#xff1a; 二、IoC&#xff08;控制反转&#xff09;是什么&#xff1f; 2.1 实现手段 2.2 依赖注入&#xff08;DI&#xff09;的实现原理 2.3 优点 三、AO…

Springboot学习笔记——2

Springboot学习笔记——2 一、打包与运行1.1、程序打包与运行&#xff08;windows版&#xff09;1.2、打包插件1.3、Boot工程快速启动&#xff08;Linux版&#xff09; 二、配置高级2.1、临时属性设置2.2、配置程序四级分类2.3、自定义配置文件 三、多环境开发3.1、多环境开发&…

深圳市重点实验室申报条件-华夏泰科

深圳市重点实验室是一个致力于科学研究和技术创新的重要机构。作为中国科技创新的重要一环&#xff0c;深圳市重点实验室在多个领域展开前沿研究&#xff0c;并为科学家、工程师和创新者提供了宝贵的资源和支持。、在接下来的内容中&#xff0c;华夏泰科将为您说明深圳市重点实…

强化学习------Sarsa算法

简介 SARSA&#xff08;State-Action-Reward-State-Action&#xff09;是一个学习马尔可夫决策过程策略的算法&#xff0c;通常应用于机器学习和强化学习学习领域中。它由Rummery 和 Niranjan在技术论文“Modified Connectionist Q-Learning&#xff08;MCQL&#xff09;” 中…

SpringTask ----定时任务框架 ----苍穹外卖day10

目录 SpringTask 需求分析 快速入门 使用步骤 ​编辑业务开发 SpringTask 定时任务场景特化的框架 需求分析 快速入门 使用cron表达式来使用该框架 使用步骤 添加注解 自定义定时任务类 重点在于以下cron表达式的书写,精确表达触发的间隔 业务开发 主task方法 time使用(-…

ESP32设备驱动-Nokia5110显示屏驱动

Nokia5110显示屏驱动 文章目录 Nokia5110显示屏驱动1、Nokia5110介绍2、硬件准备3、软件准备4、代码实现4.1 显示简单文本4.2 显示不同文本4.3 显示基本图形4.4 显示位图在本文中,我们将介绍如何使用ESP32驱动Nokia5110图形 LCD 显示器。 我们将介绍如何在Nokia5110 LCD 显示屏…

数据结构:二叉树(超详解析)

目录​​​​​​​ 1.树概念及结构 1.1树的概念 1.2树的相关概念 1.3树的表示 1.3.1孩子兄弟表示法&#xff1a; 1.3.2双亲表示法&#xff1a;只存储双亲的下标或指针 两节点不在同一树上&#xff1a; 2.二叉树概念及结构 2.1.概念 2.2.特殊的二叉树&#xff1a; 2…

机器学习(四十八):粒子群优化(PSO)-提升机器学习模型准确率的秘密武器

文章目录 PSO算法简介为什么使用PSO优化机器学习参数?PSO与其他启发式算法的比较如何使用PSO优化机器学习模型?模块安装和测试例子PSO优化决策树总结PSO算法简介 粒子群优化算法(Particle Swarm Optimization,PSO)是一种模拟鸟群觅食行为的启发式算法。在PSO算法中,每个…

【C++设计模式之组合模式:结构型】分析及示例

简介 组合模式是一种结构型设计模式&#xff0c;它能够将对象组合成树形结构以表示“整体-部分”的层次结构&#xff0c;并且能够使用相同的方式处理单个对象和组合对象。组合模式使得客户端可以一致地处理单个对象和组合对象&#xff0c;无需关心具体的对象类型。 组合模式将对…

企业想过等保,其中2FA双因素认证手段必不可少

随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显。等保2.0时代的到来&#xff0c;意味着企业和组织需要更加严格地保护自身的信息安全。而在这个过程中&#xff0c;双因素认证的重要性逐渐得到广泛认可。本文将探讨 2FA 双因素认证的重要性。 在了解 2FA 双因素认证的…

FastAPI学习-24.自定义异常处理器 exception_handler

前言 添加自定义处理器&#xff0c;要使用 Starlette 的异常工具。 安装自定义异常处理器 假设要触发的自定义异常叫作 UnicornException。 且需要 FastAPI 实现全局处理该异常。 此时&#xff0c;可以用 app.exception_handler() 添加自定义异常控制器&#xff1a; from f…

自动化机器学习AutoML之flaml:利用flaml框架自动寻找最优算法及其对应最佳参数python

AutoML 一、自动化机器学习包简介1、H2O (Python,R,Java,Scala)2、auto-sklearn(Linux,Python)3、FLAML(Python)4、AutoGlueon(安装比较啰嗦,略过)二、FLAML1、安装2、方法.fit()常用参数介绍3、代码(1) 解决分类问题(2)解决回归问题一、自动化机器学习包简介 机…

【GIT版本控制】--高级分支策略

一、分支合并策略 在Git中&#xff0c;高级分支策略是为了有效地管理和整合分支而设计的。其中一个关键方面是分支合并策略&#xff0c;它定义了如何将一个分支的更改合并到另一个分支。以下是几种常见的分支合并策略&#xff1a; 合并提交策略&#xff08;Merge Commit Stra…

2023-IDEA插件推荐

CamelCase 链接 https://plugins.jetbrains.com/plugin/7160-camelcase https://github.com/netnexus/camelcaseplugin 介绍 提供下划线、驼峰等代码风格的切换。快捷键是⇧ ⌥ U / Shift Alt U GsonFormatPlus 链接 https://plugins.jetbrains.com/plugin/14949-gs…