【Linux | 网络】网络编程套接字

【Linux | 网络】网络编程套接字

目录

一、预备知识

1.1 理解IP地址

在IP数据报头部中,有两个IP地址,分别叫做源IP地址,和目的IP地址。

思考:我们光有IP地址就可以完成通信了嘛?
IP地址能够标识互联网中的唯一的一台主机,想象一下发qq消息的例子,有了IP地址能够把消息发送到对方的机器上,但是还需要有一个其他的标识来区分出,这个数据要给哪个程序进行解析。


1.2 认识端口号

两台主机间通信并不是使用机器通信,而是使用机器上的程序进行通信,程序也就是进程,所以需要一个标识来标记主机中的进程,也就是这里所讲的端口号,端口号(port)是传输层协议的内容,能够标识主机中唯一的一个进程

  • 端口号是一个2字节16位的整数
  • 端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程
  • 一个端口号只能被一个进程占用

传输层协议(TCP和UDP)的数据段中有两个端口号,分别叫做源端口号和目的端口号,就是在描述 “数据是谁发的,要发给谁”。


1.3 理解网络套接字

IP地址能够标识互联网中的唯一的一台主机,端口号能够标识主机中唯一的一个进程。也就是说{IP地址,port},就能够标识网络中唯一的一个进程,使用网络进行通信的本质就是使用{IP地址,port}进行通信的,它的名字叫做网络套接字。


1.4 理解 “端口号” 和 “进程ID”

我们之前在学习系统编程的时候,学习了 PID 用来表示唯一一个进程;此处我们的端口号也是唯一表示一个进程。那么这两者之间是怎样的关系?

PID是用来标识主机上的进程的,port是用来标识主机上用来进行网络通信的进程的。由于主机上并不是所有进程都需要进行网络通信的,并且我们希望其他模块(进程模块)与网络模块进行解耦,所以就有了端口号的概念。

一个端口号通常与一个进程进行绑定,一个进程也可以绑定多个端口号,但是一个端口号不能被多个进程绑定。


1.5 认识TCP协议与UDP协议

此处我们先对TCP(Transmission Control Protocol 传输控制协议)和UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识,后面我们再详细讨论TCP和UDP的一些细节问题。大家不要被两个协议特点所迷惑,这两个协议只有不同,没有好坏

TCP协议的特点

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

UDP协议的特点

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

1.6 网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同样有大端小端之分。那么如何定义网络数据流的地址呢?

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节
  • 不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接收数据
  • 如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可

二、socket编程接口

2.1 socket 常见API

// 头文件#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>

2.1.1 socket函数

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)intsocket(int domain,int type,int protocol);

功能:创建一个新的套接字。

参数

  • domain:指定协议族(如AF_INET用于IPv4,AF_INET6用于IPv6)。
  • type:指定套接字类型(如SOCK_STREAM用于TCP,SOCK_DGRAM用于UDP)。
  • protocol:通常指定为0,让系统自动选择协议。

返回值:成功时返回套接字文件描述符,失败时返回-1并设置errno。


2.1.2 bind函数

// 绑定端口号 (TCP/UDP, 服务器)intbind(int socket,conststructsockaddr*address, socklen_t address_len);

功能:将套接字与特定的IP地址和端口号绑定。

参数

  • socket:要绑定的套接字文件描述符。
  • address:指向包含IP地址和端口号的sockaddr结构的指针。
  • address_len:address结构的大小。

返回值:成功时返回0,失败时返回-1并设置errno。


2.1.3 listen函数

// 开始监听socket (TCP, 服务器)intlisten(int socket,int backlog);

功能:使套接字进入监听状态,准备接受连接请求。

参数

  • socket:要监听的套接字文件描述符。
  • backlog:指定系统应为相应套接字排队的最大连接数。

返回值:成功时返回0,失败时返回-1并设置errno。


2.1.4 accept函数

// 接收请求 (TCP, 服务器)intaccept(int socket,structsockaddr* address, socklen_t* address_len);

功能:接受一个连接请求。

参数

  • socket:处于监听状态的套接字文件描述符。
  • address:如果不为NULL,将存储客户端的地址信息。
  • address_len:指向address结构大小的指针,函数返回时存储实际大小。

返回值:成功时返回新的套接字文件描述符用于与客户端通信,失败时返回-1并设置errno。


2.1.5 connect函数

// 建立连接 (TCP, 客户端)intconnect(int sockfd,conststructsockaddr*addr, socklen_t addrlen);

功能:主动与服务器建立连接。

参数

  • sockfd:客户端套接字文件描述符。
  • addr:指向包含服务器地址信息的sockaddr结构的指针。
  • addrlen:addr结构的大小。

返回值:成功时返回0,失败时返回-1并设置errno。


2.2 主机字节序和网络字节序的转换的函数

// 头文件#include<arpa/inet.h>

2.2.1 htonl函数

uint32_thtonl(uint32_t hostlong);

功能:将32位无符号整数从主机字节序转换为网络字节序

参数

  • hostlong :是一个 32 位无符号整数,以主机字节序表示。

返回值:返回转换后的以网络字节序表示的 32 位无符号整数。


2.2.2 ntohl函数

uint32_tntohl(uint32_t netlong);

功能:将32位无符号整数从网络字节序转换回主机字节序

参数

  • netlong:是一个 32 位无符号整数,以网络字节序表示。

返回值:返回转换后的以主机字节序表示的 32 位无符号整数。


2.2.3 htons函数

uint16_thtons(uint16_t hostshort);

功能:将16位无符号整数从主机字节序转换为网络字节序

参数

  • hostshort:是一个 16 位无符号整数,以主机字节序表示。

返回值:返回转换后的以网络字节序表示的 16 位无符号整数。


2.2.4 ntohs函数

uint16_tntohs(uint16_t netshort);

功能:将16位无符号整数从网络字节序转换回主机字节序

参数

  • netshort :是一个 16 位无符号整数,以网络字节序表示。

返回值:返回转换后的以主机字节序表示的 16 位无符号整数。


2.3 IP地址形式转换的函数

#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>

2.3.1 inet_aton函数

intinet_aton(constchar*cp,structin_addr*inp);

功能:将点分十进制的IPv4地址字符串转换为网络字节序的二进制形式。

参数

  • cp :是指向以null结尾的IPv4地址字符串的指针;
  • inp :是指向 struct in_addr 结构的指针,用于存储转换后的地址。

返回值

  • 如果转换成功,则返回非零值;
  • 如果转换失败,则返回零。

2.3.2 inet_addr函数

in_addr_t inet_addr(constchar*cp);

功能:将点分十进制字符串形式的 IP 地址转换为 32 位二进制整数形式(网络字节序)。

参数

  • cp:指向点分十进制字符串形式 IP 地址的指针。

返回值:如果转换成功,返回转换后的 32 位二进制整数;如果转换失败,返回 INADDR_NONE(通常为 0xffffffff)。不过需要注意的是,INADDR_NONE 也是一个有效的 IP 地址(255.255.255.255),这可能会导致一些混淆,因此更推荐使用 inet_aton 函数。


2.3.3 inet_network函数

in_addr_t inet_network(constchar*cp);

功能:将点分十进制字符串形式的 IP 地址转换为 32 位二进制整数形式(主机字节序),并且通常用于提取网络号。

参数

  • cp:指向点分十进制字符串形式 IP 地址的指针。

返回值:如果转换成功,返回转换后的 32 位二进制整数;如果转换失败,返回 -1。


2.3.4 inet_ntoa函数

char*inet_ntoa(structin_addr in);

功能:将网络字节序的IPv4地址(struct in_addr)转换回点分十进制的字符串形式。

参数

  • in:是包含要转换地址的 struct in_addr 结构。

返回值:返回一个指向静态分配的、以null结尾的字符串的指针,该字符串包含转换后的点分十进制IPv4地址。注意,返回的字符串指针指向一个静态分配的区域,因此每次调用 inet_ntoa 时,上次调用返回的字符串可能会被覆盖。


2.4 套接字中发送/接收数据的函数

2.4.1 recvfrom函数

ssize_t recvfrom(int sockfd,void*buf, size_t len,int flags,structsockaddr*src_addr, socklen_t *addrlen);

功能:从指定的套接字接收数据报,并可选地获取发送方的地址信息。

参数

  • sockfd:套接字文件描述符,指向一个已绑定并处于监听状态的UDP套接字。
  • buf:指向用于存储接收到的数据报的缓冲区。
  • len:缓冲区的大小,即最多可以接收的字节数。
  • flags:通常设置为0,但可以指定如MSG_PEEK(查看数据但不移除)等标志。
  • src_addr:指向sockaddr结构的指针,用于存储发送方的地址信息。如果不需要此信息,可以设置为NULL。
  • addrlen:指向socklen_t变量的指针,用于输入src_addr结构的大小,并在函数返回时存储实际写入src_addr的地址长度。如果src_addr为NULL,则此参数也应为NULL。

返回值

  • 如果成功,它将返回一个 ssize_t 类型的值,表示接收到的字节数。
  • 如果发生错误,它将返回 -1,并且全局变量 errno 将被设置为一个描述错误的代码。

2.4.2 sendto函数

ssize_t sendto(int sockfd,constvoid*buf, size_t len,int flags,conststructsockaddr*dest_addr, socklen_t addrlen);

功能:用于向指定的地址发送数据报。

参数

  • sockfd:套接字文件描述符,指向一个已绑定(或未绑定但使用了默认路由)的UDP套接字。
  • buf:指向包含要发送的数据报的缓冲区。
  • len:要发送的数据报的长度(字节数)。
  • flags:通常设置为0,但可以指定如MSG_DONTWAIT(非阻塞发送)等标志。
  • dest_addr:指向sockaddr结构的指针,包含了目标地址的信息。
  • addrlen:dest_addr结构的大小。

返回值

  • 函数返回时,如果成功,它将返回一个 ssize_t 类型的值,表示发送的字节数。通常,这个值应该等于 len,除非发生了错误或发送被中断。
  • 如果发生错误,sendto 将返回 -1,并且全局变量 errno 将被设置为一个描述错误的代码。

2.5 sockaddr结构

网络编程中,socket分为很多种类:

  1. unix socket:也被称为域socket,它允许在同一台主机上的不同进程之间通过文件系统路径进行通信。
  2. 网络socket:它允许在不同主机上之间进行通信,也允许同一台主机上的不同进程之间进行通信。
  3. 原始socket:它允许程序绕过操作系统的网络协议栈,直接发送和接收原始数据包。

由于设计者想用同一套接口就解决上面应用场景,所以就设计出来sockaddr_in结构体类型,通过下图我们可以看到sockaddr_in结构体与sockaddr_un结构体都有一个地址类型,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。

在这里插入图片描述

结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

Read more

开源又实用!CAM++系统为何值得你立刻尝试

开源又实用!CAM++系统为何值得你立刻尝试 1. 这不是另一个语音识别工具,而是一个真正能落地的说话人验证方案 你有没有遇到过这样的场景:需要确认一段录音是不是某位同事说的?想快速判断客服通话中两个声音是否来自同一人?或者在安防系统里,需要从一段监控音频中验证说话人身份?市面上很多语音识别工具只告诉你“说了什么”,但CAM++解决的是更关键的问题——“谁说的”。 CAM++不是语音转文字(ASR),也不是语音合成(TTS),它专注一个被长期低估却极其重要的能力:说话人验证(Speaker Verification)。简单说,它不关心内容,只认声音本身。就像指纹或虹膜识别一样,它把人的声纹变成一串可计算、可比对的数字特征。 更难得的是,这个系统完全开源、开箱即用、中文优化、部署极简。不需要GPU服务器,一台普通开发机就能跑;不需要写代码,点点鼠标就能完成专业级声纹分析;不需要调参经验,预设阈值开箱即准。它不像学术模型那样只停留在论文里,也不像商业API那样藏着高昂费用和隐私风险——它就安静地运行在你的本地机器上,数据不出门,结果自己掌控。 如果你正在寻找一个真正能放进工作流

By Ne0inhk
【工创赛2025-智能物流搬运塔吊方案开源(2分15秒)】西安理工大学工程训练中心

【工创赛2025-智能物流搬运塔吊方案开源(2分15秒)】西安理工大学工程训练中心

一、前言        时光荏苒,岁月如梭。三年的本科竞赛生涯随着工训赛的结束告一段落。竞赛路途中,受到了诸多大佬的帮助和鼓励。为了将这份开源精神传递下去,本团队全体成员一致决定无偿开源本项目机械设计图纸、PCB设计、电控代码、视觉代码及镜像文件、参赛文档以及其他有关设计资料。        请注意,本项目开源文件完全免费,内容遵循CC 4.0 BY-NC-SA版权协议,转载请给出适当的署名,不可用作商业用途,严禁倒卖,若广大网友发现以上行为,请第一时间与我取得联系。        在此,由衷感谢西安理工大学工程训练中心的各位老师对我们竞赛项目的悉心指导与鼎力支持。         这里放一张二代小车同堂的照片作为纪念 二、关于开源项目        运行视频:[开源]2025工训赛智能物流搬运,初赛第八,2分26秒_哔哩哔哩_bilibili        本项目参与了2025年中国大学生工程实践与创新能力大赛全国总决赛,初赛成绩仅1个二环,其余均为一环,总时间2分26秒。决赛由于准备不足以及现场不可预料的因素,成绩不算理想,最后总成绩为全国特等奖。

By Ne0inhk

Git 回退到某个 commit

Git 回退到某个 commit 文章目录 * Git 回退到某个 commit * **核心总结:如何选择?** * **方法一:`git reset` (重置)** * `git reset` 的三种模式: * **操作步骤示例 (使用 `--hard`)** * **方法二:`git revert` (撤销)** * **操作步骤示例** * **方法三:`git checkout` (检出)** * **操作步骤示例** * **离开 "detached HEAD" 状态** * **紧急救援:`git reflog`** 这里我会为你详细解释三种主要的方法: git reset、 git revert 和 git checkout。它们适用于不同的场景,理解它们的区别非常重要。 核心总结:如何选择?

By Ne0inhk