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

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

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

一.socket() 

遵循 POSIX.1 - 2001、POSIX.1-2008、4.4BSD

1.库

标准 c 库,libc, -lc

2.头文件

<sys/socket.h>

3.接口定义

int socket(int domain, int type, int protocol);

4.接口描述

        socket() 创建一个通信端点并返回一个指向该端点的文件描述符。返回的文件描述符号是当前进程没有打开的号最小的文件描述符。

5.参数

  • domain

        domain 参数指定了一个通信域,它选择了用于通信的协议家族,这些协议家族在<sys/socket.h> 中定义,当前 Linux 内核能够认识的格式包括:

名称目的
AF_UNIX本地通信
AF_LOCAL和 AF_UNIX 同意
AF_INETIPv4 网络协议
AF_AX25业余无线电 AX.25 协议
AF_IPXNovell 分组交换协议
AF_APPLETALKAppletalk 协议
AF_X25X25 分组交换网络
AF_INET6IPv6 网络协议
AF_DECnetDECnet 协议 socket
AF_KEY密钥管理协议 
AF_NETLINK内核用户接口设备
AF_PACKET底层 packet 接口
AF_RDS可靠的数据报套接字协议
AF_PPPOX通用 PPP 传输层,用于设置 L2 层隧道(L2TP、PPPoE)
AF_LLC逻辑链路控制协议(IEEE 802.2 LLC)
AF_IBInfiniBand 本地访问
AF_MPLS多协议标记切换
AF_CAN控制器局域网汽车总线协议
AF_TIPC集群域内套接字
AF_BLUETOOTH蓝牙底层套接字协议
AF_ALG内核密码学 API 接口
AF_VSOCKVSOCK 原来用于 VMWARE VSockets,hypervisor 和 guest 之间的通信协议
AF_KCM内核连接多路复用器接口
AF_XDP快速数据路径接口

        更多关于地址家族的信息可以从 address_families(7) 中查看。

        socket 有一个指定的 type 类型,定义了双方通信语义,目前定义的类型有:

        SOCK_STREAM

        提供有序、可靠、双向、面向连接的字节流,可以支持带歪数据传输机制。

        SOCK_DGRAM

        支持数据报文(无连接、不可靠定长消息)。 

        SOCK_SEQPACKET

        提供了有序、可靠、双向、面向连接的数据传输,传输的内容不是字节流,而是固定长度的数据报文。数据报消费者每次通过 read 系统调用读取整个数据报文。

        SOCK_RAW

        提供原始网络协议访问。

        SOCK_RDM

        提供可靠的数据报层,但是并不保证有序。

        SOCK_PACKET

        已经过时了,新应用不应该使用,参考 packet(7)。

       一些协议类型并不是被所有协议家族支持的。

        Linux 2.6.27 后,type 类型具有另外一个目的:除了指定 socket 类型,还包含了下面数值的位或值,来控制 socket 的行为:

        SOCK_NONBLOCK

        在打开新文件描述符指向的文件时设置文件状态标记 O_NONBLOCK,这就不需要额外使用 fcntl() 来进行设置。

        SOCK_CLOEXEC

        设置新文件描述符的 FD_CLOEXEC 标记,可以参考 open() 来看为什么需要设置整个参数

        protocol 指定了 socket 使用的具体协议,通常一个指定的协议家族、协议类型中只有一种协议,这时 protocol 可以指定为 0。然而,也可能存在多个协议,这种情况下就必须指定协议,而协议号是根据实际的通信域的不同而不同的。参考 protocol(5)。参考 getprotoent(3) 来查看如何将协议号映射到协议名字符串上。

        SOCK_STREAM  socket 是全双工字节流,它并没有保留记录边界。流套接字必须处于连接状态来进行数据的发送和接收。连接到其他套接字是通过 connect(2) 系统调用实现的。一旦连接上了,数据就可以通过 read(2) 和 write(2) 调用来进行传输,或者使用 send(2) 和 recv(2) 变体调用。会话结束后,应该使用 close(2) 来关闭。带外数据可以根据 send(2) 和 recv(2) 的描述来进行收发。

        实现 SOCK_STREAM 的通信协议需要保证数据不能丢失或者重复。对于缓存到底层协议中在规定时间内无法传输完成的数据来讲,该连接会被视为死掉了。当套接字协议开启了 SO_KEEPALIVE 保活机制,协议会使用协议自己定义的方式来检查对端是否还活着。当我们在一个破损了的 pipe 上发送接收数据,那么就会收到 SIGPIPE信号,这会导致没有处理这个信号的本地进程直接退出。

        SOCK_QEQPACKET 套接字使用同样的系统调用 SOCK_STREAM,唯一的不同 read(2) 调用返回指定请求数量的数据,接收数据包中剩余的数据将会被丢弃。同时,发过来的数据报文的所有边界都保留着。

        SOCK_DGRAM 和 SOCK_RAW 套接字允许使用 sendto(2) 来发送数据报文给对端。数据报文通常使用 recvfrom(2) 来接收,这个接口会返回下一个数据报文以及发送者的地址。

        SOCK_PACKET 是一个过时的直接从对端接收原始数据报文的套接字类型,应该使用 packet(7) 来替代。

        我们可以使用 fcntl(2) 的 F_SETOWN 操作来指定进程或者进程组来接收带外数据到达信号 SIGURG 和连接异常中断信号 SIGPIPE。这个操作也可以用来接收 SIGIO 异步 I/O 通知事件。使用 F_SETOWN 等效于 ioctl() 调用的 FIOSETOWN 或者 SIOCSPGRP。

        当网络给协议模块发送了错误指示信号时(比如 IP 层的 ICMP 消息),那么错误标记将会设置到套接字上,在套接字的下一次操作发生时,会将挂起的错误以错误码的形式返回。对于一些协议而言,也可以通过开启套接字特定的错误队列来获得关于错误的详细信息,可以参考 ip(7) 中的 IP_RECVERR。

        对于套接字的操作是由套接字层面的选项来控制的,这些选项定义在 <sys/socket.h> 中。函数 setsockopt(2) 和 getsockopt(2) 用来设置和获取对于的选项。

带外数据传输指的是 TCP 在紧急情况下通过调整报文在发送/接收缓冲区的位置以及数据包中添加紧急标记的逻辑。 

6.返回值

        发生错误时返回 -1,设置 errno 指示错误码,否则返回一个新创建的整型文件描述符。

        可能的错误码包括:

错误码含义
EACCES没有权限创建对应的 socket
EAFNOSUPPORT实现不支持指定的 AF_ 地址家族
EINVAL未知的协议或者地址家族不可用
EINVALtype 参数不合法
EMFILE进程文件描述符到达最大限制
ENFILE系统文件描述符到达上限
ENOBUFS or ENOMEM内存不足
EPROTONOSUPPORTdomain 不支持指定的协议类型

二、bind

遵循 POSIX.1-2008

1.库

标准 c 库,libc, -lc

2.头文件

<sys/socket.h>

3.接口定义

 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

4.接口描述

        通过 socket() 接口创建 socket 后,socket 只存在于名字空间中,并没有实际的地址分配给它。bind 接口将 addr 指定的 IP 地址分配给由文件描述符 sockfd 指定的 socket。addrlen 指定了 addr 指针指向的地址结构的字节长度。以前我们将这个操作给 socket 分配名字。

        通常在 TCP_STREAM socket 接收连接前需要将一个本地地址通过 bind 分配给 socket。

        名字绑定规则随着地址家族的不同而不同。

         addr 的数据结构也是随着地址家族的变化而变化的。sockaddr 结构的定义类似:

 struct sockaddr {sa_family_t sa_family;char        sa_data[14];}

         这个结构定义主要是为了防止编译器报错,主要是将各种地址结构做一个强制转换。

5. 返回值

        发生错误时返回 -1,设置 errno 指示错误码,否则返回一个新创建的整型文件描述符。

        可能的错误码包括:

错误码含义
EACCES地址是保护地址,并且用户不是超级用户
EADDRINUSE指定的地址已经使用
EADDRINUSE

对于 domain socket,端口号在地址结构体中

指定为 0,但在尝试 bind 到临时端口时,临时端口没有空闲的了

EBADFsockfd 不是可用的文件描述符
EINVALsocket 已经绑定到了一个地址
EINVALaddrlen 错误,或者 addr 不是一个可用的 domain 地址
ENOTSOCK文件描述符没有指向任何 socket
UNIX domain(AF_UNIX) 特定的错误码
EACCESS在路径前缀下无搜索权限
EADDRNOTAVAIL请求的接口不存在或者不是本地的接口
EFAULTaddr 指向了用户无法访问的地址空间
ELOOP解析地址时遇到了太多的符号链接
ENAMETOOLONG地址太长
ENOENT指定路径不存在
ENOMEM内核内存不足
ENOTDIR路径前缀不是一个目录
EROFSsocket inode 位于只读文件系统中

6.示例代码

       #include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <sys/un.h>#include <unistd.h>#define MY_SOCK_PATH "/somepath"#define LISTEN_BACKLOG 50#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)intmain(void){int                 sfd, cfd;socklen_t           peer_addr_size;struct sockaddr_un  my_addr, peer_addr;sfd = socket(AF_UNIX, SOCK_STREAM, 0);if (sfd == -1)handle_error("socket");memset(&my_addr, 0, sizeof(my_addr));my_addr.sun_family = AF_UNIX;strncpy(my_addr.sun_path, MY_SOCK_PATH,sizeof(my_addr.sun_path) - 1);if (bind(sfd, (struct sockaddr *) &my_addr,sizeof(my_addr)) == -1)handle_error("bind");if (listen(sfd, LISTEN_BACKLOG) == -1)handle_error("listen");/* Now we can accept incoming connections oneat a time using accept(2). */peer_addr_size = sizeof(peer_addr);cfd = accept(sfd, (struct sockaddr *) &peer_addr,&peer_addr_size);if (cfd == -1)handle_error("accept");/* Code to deal with incoming connection(s)... */if (close(sfd) == -1)handle_error("close");if (unlink(MY_SOCK_PATH) == -1)handle_error("unlink");}

 三、accept

1.库

标准 c 库,libc, -lc

2.头文件

<sys/socket.h>

3.接口定义

int accept(int sockfd, struct sockaddr *_Nullable restrict addr,socklen_t *_Nullable restrict addrlen);#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <sys/socket.h>int accept4(int sockfd, struct sockaddr *_Nullable restrict addr,socklen_t *_Nullable restrict addrlen, int flags);

4.接口描述

        accept 系统调用用于面向连接的 socket (SOCK_STREAM、SOCK_SEQPACKET),它会从监听的 socket(sockfd)的等待连接队列里拿到第一个连接请求,创建一个新的连接的 socket,并返回一个新的文件描述指向这个新的 socket。新创建的 socket 并没有处于监听状态,原来的 socket(sockfd)并不会受到任何影响。

        sockfd 是由 sockect() 创建的,并通过 bind 绑定到了本地地址上,并通过 listen 监听连接。

        addr 是一个指向 sockaddr 结构的指针,这个地址由对端 socket 的地址填充。而返回的 addr 的结构类型根据 socket 地址家族的不同而不同。当 addr 是 NULL 时,底层并不会对其填充,这种情况下 addrlen 也没有用,也应该是 NULL。

        addrlen 参数是一个输入输出参数,调用者必须使用 addr 指向的结构体的大小来初始化它,而在返回时,则会使用对端地址的实际大小来填充。

        如果提供的 buffer 太小,则返回的地址将会被截断,这种情况下,addrlen 会返回一个比提供值大的值。

        如果当前等待连接队列中没有待连接请求,并且 socket 没有被设置成非阻塞,那么 accept() 将会一直阻塞。如果 socket 设置为非阻塞,那么 accept() 将报错为 EAGAIN 或者 EWOULDBLOCK。

        为了获取 socket 上有连接请求过来,我们需要使用 select、poll、epoll。当一个新连接来临时,会产生一个可读事件,我们可以使用 accept 来继续从连接上获取一个 socket。

        我们也可以设置 socket 上有连接时发送 SIGIO 信号。

        如果 flag 是 0,那么 accept4() 就等同于 accept()。flag 可以是下面配置的或起来的值,来实现不同的行为:

  • SOCK_NONBLOCK

                 设置新文件描述符的 O_NONBLOCK 属性

  • SOCK_CLOEXEC

                 设置新文件描述符的 FD_CLEXEC 属性。

5.返回值

        成功时,返回一个新接收的 socket 的文件描述符(非负值)。

        出错时,返回 -1,设置 errno 为错误码,addrlen 不会被修改。

  • 错误处理

        Linux 的 accept() 会将既存的网络错误也会给返回值,这个行为和其他 BSD socket 实现的行为不太一样。为了可靠性,我们应该处理 accept 返回网络错误,这些错误是和协议相关的。比如 EAGAIN 表示重传,在TCP/IP 的场景下,还有 ENETDOWN、 EPROTO、 ENOPROTOOPT, EHOSTDOWN、 ENONET、 EHOSTUNREACH、 EOPNOTSUPP、 ENETUNREACH等需要处理。

       可能的错误码包括:

错误码含义
EAGAIN或EWOULDBLOCKsocket 设置为非阻塞,目前没有可用连接。POSIX.1-2001 和 POSIX.1-2008 允许范围任何一个错误,同时并没有他们有相同的值,所以为了实现移植性,需要分别判断。
ECONNABORTED连接已中断
EFAULT

addr 不是用户地址空间可写的地址

EBADFsockfd 不是打开的文件描述符
EINVALsocket 没有在监听连接或者addrlen不合法
EINVALaccept4,flags 值不合法
ENOTSOCK文件描述符没有指向任何 socket
EINTR在连接到达前,系统调用被信号打断
EMFILE进程描述符数达到上限
EFAULTaddr 指向了用户无法访问的地址空间
ENFILE系统文件描述符达到上限
ENAMETOOLONG地址太长
ENOENT指定路径不存在
ENOMEM或ENOBUFS内核内存不足
EOPNOTSUPPsocket 不 SOCK_STREAM 类型
EPERM防火墙禁止连接
EPROTO协议错误

Linux 上,新创建的 socket 并不会从监听 socket 上继承 O_NONBLOCK 和 O_AYSNC 属性,这点和 canonical BSD socket 实现的行为不同。所以,实现可移植的程序不应该依赖这些行为。

值得注意的是,有时在我们收到 SIGIO 或者通过select、poll、epoll 获得到一个刻度的事件时,并不一定就会有一个连接等待连接,这是因为连接很可能会被异步网络错误或者其他线程通过 accept() 拿走了。在这种情况下,就会导致 accept 阻塞,直到下一个连接到达。为了保证 accept 永远不会阻塞,传来的 socketfd 需要有 O_NONBLOCK 属性。

在最初的 BSD socket 实现中,accept 的第三个参数是 int *,在 POSIX.1g 草稿版标准想把它改成 size_t *C,后来 POSIX 标准和glibc 2.x 定为 socket_t *。

遵循:

accept()   POSIX.1-2008

accept4    Linux 

下一篇 【计算机网络】网络编程接口 Socket API 解读(2)​​​​​​​ 

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

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

相关文章

学习视觉SLAM需要会些什么?

前言 SLAM是现阶段很多研究生的研究方向&#xff0c;我也是作为一个即将步入视觉SLAM的研究生&#xff0c;网上对于SLAM的介绍很多&#xff0c;但很少有人完整系统的告诉你学习视觉SLAM该有那些基础&#xff0c;那么此贴将告诉你学习SLAM你要有那些方面的基础。 文章目录 前言…

【AI】机器学习——线性模型(线性回归)

线性模型既能体现出重要的基本思想&#xff0c;又能构造出功能更加强大的非线性模型 参考&#xff1a;唐宇迪机器学习课程 文章目录 3.1 线性模型3.1.1 数据3.1.2 目标/应用 3.2 线性回归3.2.1 回归模型历史3.2.2 回归分析研究内容回归分析步骤 3.2.3 回归分析分类3.2.4 回归模…

【Golang入门】Golang第一天心得

生活所迫&#xff0c;入门一下Go 很奇葩的第一点&#xff0c;接口 package mainimport ("fmt" )// 定义一个接口 type Shape interface {Area() float64 }// 定义一个矩形类型 type Rectangle struct {Width float64Height float64 }// 矩形类型实现Shape接口的Ar…

Codeforces Round 895 (Div. 3) A ~ F

Dashboard - Codeforces Round 895 (Div. 3) - Codeforces A 问多少次能使a 和 b相等&#xff0c;就是abs(a - b) / 2除c向上取整&#xff0c;也就是abs(a - b)除2c向上取整。 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #de…

学习网络编程No.6【将服务器日志和守护进程化】

引言&#xff1a; 北京时间&#xff1a;2023/9/1/21:15&#xff0c;下午刚更新完博客&#xff0c;同理再接再厉&#xff0c;这样整天不需要干什么&#xff0c;除了玩手机的日子不多了&#xff0c;马上就要开学&#xff0c;每天需要签到签退的日子就要来临&#xff0c;烦躁&…

浅谈STL|STL函数对象篇

一.函数对象概念 概念: 重载函数调用操作符的类&#xff0c;其对象常称为函数对象 函数对象使用重载的()时&#xff0c;行为类似函数调用&#xff0c;也叫仿函数 本质: 函数对象(仿函数)是一个类&#xff0c;不是一个函数 特点 函数对象在使用时&#xff0c;可以像普通函数那…

Linux提权

shell分本地shell 和 webshell 有些提权方式只能本地shell使用 常见内核漏洞查找脚本以及利用 环境变量提权 suid https://www.cnblogs.com/banglook/archive/2022/03/17/16019354.html linux特殊命令https://www.secrss.com/articles/28493 什么是suid SUID (Set UID)是Li…

「中秋来袭」没想到,用OpenCV竟能画出这么漂亮的月饼「附源码」

一、前言 中秋佳节即将来临&#xff0c;作为传统的中国节日之一&#xff0c;人们除了品尝美味的月饼、赏月外&#xff0c;还喜欢通过绘画来表达对这个节日的喜悦和祝福。而如今&#xff0c;随着科技的不断发展&#xff0c;竟然可以借助计算机视觉库OpenCV来绘制精美的月饼和可…

Redis的数据持久化方案

目录 前言 RDB方式 概述&#xff1a; 1.RDB手动 &#xff12;.RDB自动 RDB优缺点 AOF方式 概述 AOF写数据的三种策略 AOF相关配置 AOF重写 AOF重写方式 手动重写 bgrewriteaof 自动重写 总结 前言 Redis是一个内存型数据库&#xff0c;也就是说如果不将内存中的…

被删除并且被回收站清空的文件如何找回

文件的意外删除和回收站清空是许多用户面临的普遍问题。这种情况下&#xff0c;很多人会感到无助和焦虑&#xff0c;担心自己的重要文件永远丢失。然而&#xff0c;幸运的是&#xff0c;依然存在一些有效的方法能够帮助我们找回被删除并且被回收站清空的文件。 ▌被删除文件在…

uniapp——实现聊天室功能——技能提升

这里写目录标题 效果图聊天室功能代码——html部分代码——js部分代码——其他部分 首先声明一点&#xff1a;下面的内容是从一个uniapp的程序中摘录的&#xff0c;并非本人所写&#xff0c;先做记录&#xff0c;以免后续遇到相似需求抓耳挠腮。 效果图 聊天室功能 发送图片 …

进制转换问题

进制 二进制 &#xff08;Binary&#xff09;&#xff1a;0、1。简写为B 八进制&#xff08;Octonary&#xff09;&#xff1a;0、1、2、3、4、5、6、7。简写为O 十进制&#xff08;decimalism&#xff09;&#xff1a;0、1、2、3、4、5、6、7、8、9 简写为D 十六进制&#xff…

代码随想录算法训练营完结篇 ★★★

代码随想录 ★★★ 文档讲解 &#xff1a; 代码随想录 状态&#xff1a;★★★&#xff1a;需要多次重新回顾&#xff01; 不知不觉60天刷过去了&#xff0c;曾经也想着放弃&#xff0c;也觉得没啥意义&#xff0c;但是最后还是坚持下来了。完结撒花&#xff01;接下来就是继续…

socket编程

网络编程 网络编程的步骤常用APITCP中的accept和connect和listen的关系UDP中的connect广播和组播过程服务端大量TIMEWAIT或CLOSEWAIT状态复位报文段RST优雅关闭和半关闭解决TCP粘包select可以判断网络断开吗send和read的阻塞和非阻塞情况网络字节序和主机序IP地址分类及转换sel…

MySQL 子查询

文章目录 1.简介2.优势3.分类3.1 标量子查询3.2 行子查询3.3 列子查询IN 操作符ALL 操作符ANY/SOME 操作符 3.4 表子查询 4.关联子查询5.EXISTS 和 NOT EXISTS6.横向派生表7.附录参考文献 1.简介 子查询是另一个语句中的 SELECT 语句。 子查询也称为内查询&#xff08;Inner …

【建议收藏】职场人口头和书面沟通必备词语,瞬间高大上

这年头&#xff0c;在职场不但要会做&#xff0c;还要会说。 会说还不能平铺直叙的说&#xff0c;还要能把普通的工作说出话来&#xff0c;这就需要一些“考究”的用词。尤其是在某些头部企业的带领下&#xff0c;业务不够、产品不行、解决方案不够新&#xff0c;就用华丽的辞…

OAK相机:启动报错X_LINK_DEVICE_NOT_FOUND

OAK相机&#xff1a;启动报错X_LINK_DEVICE_NOT_FOUND 环境报错原因与解决未设置 udev 规则USB崩溃排线接触不良或相机模块时钟干扰 环境 硬件&#xff1a; 4✖️OV9782相机模组OAK-FFC-4P驱动模组笔记本电脑 软件&#xff1a; Ubuntu18.04python 3.9depthai 2.21.2.0 报错…

ASEMI二极管1N4148(T4)的用途和使用建议

编辑-Z 二极管是一种常见的电子元件&#xff0c;其中1N4148&#xff08;T4&#xff09;是一款广泛使用的快恢复二极管。它具有快速的开关特性和高反向阻挡能力&#xff0c;适用于多种电子应用。本文将介绍1N4148&#xff08;T4&#xff09;的特点、用途和如何正确使用该二极管…

如何使用极狐GitLab 支持 ISO 27001 合规

目录 组织控制 技术控制 了解更多 本文来源&#xff1a;about.gitlab.com 作者&#xff1a;Joseph Longo 译者&#xff1a;武让 极狐GitLab 高级解决方案架构师 作为一体化平台&#xff0c;通过极狐GitLab 可以很容易实现 DevSecOps 全生命周期管理。极狐GitLab 使开发人员能…

无涯教程-JavaScript - FALSE函数

描述 FALSE函数返回逻辑值FALSE。 语法 FALSE () 争论 FALSE函数没有参数。 Notes 您还可以在工作表或公式中直接键入FALSE单词,Microsoft Excel会将其解释为逻辑值FALSE。 提供FALSE功能主要是为了与其他电子表格程序兼容。 适用性 Excel 2007,Excel 2010,Excel 2013…