Linux套接字通信学习

news2025/2/4 7:56:44

Linux套接字通信

代码源码:https://github.com/say-Hai/TcpSocketLearn/tree/CThreadSocket

在网络通信的时候, 程序猿需要负责的应用层数据的处理(最上层),而底层的数据封装与解封装(如TCP/IP协议栈的功能)通常由操作系统、网络协议栈或相关网络库(如Socket库)实现。(程序员只需要调用对应的API接口)

什么是Socket编程?

Socket套接字目的是将TCP/IP协议相关软件移植到UNIX类系统中。设计者开发了一个接口,以便应用程序能简单地调用该接口通信,这个接口不断完善,最终形成了Socket套接字。

简单来说:套接字对应程序猿来说就是一套网络通信的接口,使用这套接口就可以完成网络通信。

一、字节序:大端与小端

  • 小端:主机字节序

    • 低位字节存储到内存的低位地址
    • PC机的数据存储默认小端
    • img
  • 大端:网络字节序

    • 低位字节存储到内存的高位地址
    • 套接字通信过程中操作的数据都是大端存储的,包括:接收/发送的数据、IP地址、端口
    • img
  • 主机字节序与网络字节序的转换函数

    • #include <arpa/inet.h>
      // h: host, 主机字节序
      // n: net, 网络字节序
      // s:short(port) 
      // l:long(IP)
      
      // 这套api主要用于 网络通信过程中 IP 和 端口 的 转换
      // 将一个短整形从主机字节序 -> 网络字节序
      uint16_t htons(uint16_t hostshort);
      serv_addr.sin_port = htons(SERV_PORT);//举例:将端口号主机字节序转成网络字节序
      
      // 将一个整形从主机字节序 -> 网络字节序
      uint32_t htonl(uint32_t hostlong);	
      
      // 将一个短整形从网络字节序 -> 主机字节序
      uint16_t ntohs(uint16_t netshort)
      // 将一个整形从网络字节序 -> 主机字节序
      uint32_t ntohl(uint32_t netlong);
      

二、IP地址转换

IP地址本质是一个整形数,但是在使用的过程中都是通过一个字符串来描述,下面的函数描述了如何将一个字符串类型的IP地址进行大小端转换

//IP地址转换函数(点分十进制(字符串)→ 网络二进制)
int inet_pton(int af, const char *src, void *dst);
/*
		af:AF_INET、AF_INET6
		src:传入参数,IP地址(点分十进制)
		dst:传出参数,转换后的 网络字节序的 IP地址。 
		返回值:转换是否成功
*/

//网络字节序→String
const char *inet_ntop(int af, const void *src,
                             char *dst, socklen_t size);
/*
		af:AF_INET、AF_INET6
		src: 传入参数,网络字节序IP地址
		dst:传出参数,主机字节序(string IP)
		size: dst 的大小。
		返回值:指向dst的指针,含格式化后的字符串形式 IP 地址。
*/
//仅限处理IPv4
// 点分十进制IP -> 大端整形
in_addr_t inet_addr (const char *cp);
// 大端整形 -> 点分十进制IP
char* inet_ntoa(struct in_addr in);

三、sockaddr 数据结构

//sockaddr的地址结构  man手册:man 7 ip
struct sockaddr_in {
	sa_family_t    sin_family; /* address family: AF_INET */
	in_port_t      sin_port;   /* port in network byte order */
	struct in_addr sin_addr;   /* internet address */
};

四、套接字相关函数

socket函数:

创建套接字

#include <sys/socket.h>

int socket(int domain, int type, int protocol);
/*
domain: 使用的地址族协议AF_INET、AF_INET6
type:SOCK_STREAM流式传输协议、SOCK_DGRAM报文传输协议
protocol: 一般写0即可, 使用默认的协议
返回值:新套接字所对应文件描述符
*/

bind函数:

给socket绑定地址结构

int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
/*
sockfd: socket文件描述符,socket()的返回值
addr: 传入的地址结构(包括IP和端口号)
			(struct sockaddr *)&addr struct sockaddr_in addr;
			addr.sin_family = AF_INET;
			addr.sin_port = htons(8888);
			addr.sin_addr.s_addr = htonl(INADDR_ANY);
addrlen: sizeof(addr) 地址结构的大小。
*/

listen函数:

设置同时与服务器建立连接的上限数

 int listen(int sockfd, int backlog);
/*
sockfd: socket文件描述符,socket()的返回值
backlog: 上限连接数,max = 128
*/

★accept函数:

阻塞等待客户端建立连接,返回一个与客户端成功连接的socket文件描述符(一个新的用于通信的文件描述符

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
sockfd: 监听的文件描述符,socket()的返回值
addr:传出参数, 存储了建立连接的客户端的地址信息
addrlen: 传入传出参数,存储addr指向的内存大小
返回值:返回能与客户端进行数据通信的 socket 对应的文件描述。
*/
  1. accept函数被调用时,它会阻塞程序的执行,直到有客户端连接请求到达。
  2. 当有客户端连接请求到达时,accept函数会接受连接请求,并创建一个新的套接字用于与客户端通信。这个新的套接字是一个专门用于与该客户端进行通信的套接字,而原始的服务器套接字仍然保持在监听状态以接受其他连接请求。
  3. 如果提供了非空的addr参数,accept函数将会填充客户端的地址信息,包括IP地址和端口号。
  4. accept函数返回新创建的套接字的文件描述符。通过该文件描述符,服务器可以与客户端进行通信,发送和接收数据。

connect函数:

客户端socket与服务器建立连接。成功连接服务器之后, 客户端会自动随机绑定一个端口

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
sockfd: socket文件描述符,socket()函数返回值
addr:要连接的服务器的地址结构(客户端通常无法自己指定端口号,由操作系统动态分配)			
	struct sockaddr_in srv_addr;		// 服务器地址结构
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_port = 9527 	跟服务器bind时设定的 port 完全一致。
	inet_pton(AF_INET, "服务器的IP地址",&srv_adrr.sin_addr.s_addr);
addrlen:服务器的地址结构的大小
*/

write/send函数:

向已建立链接的Socket的写缓冲区中写入数据

#include  <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
ssize_t send(int fd, const void *buf, size_t len, int flags);
/*
	fd:         用于通信的文件描述符, accept() 函数的返回值
    buf:      存放要写入的数据的缓冲区首地址
    count:   想要写入的字节数
    len: 要发送的字符串的长度
    flags: 特殊的属性, 一般不使用, 指定为 0
    返回值:实际发送的字节数
*/

read/recv函数:

从一个已建立连接的 Socket 读缓冲区中读取数据

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t recv(int fd, void *buf, size_t size, int flags);
/*
	fd:			用于通信的文件描述符,accept() 函数的返回值
    buf:      	一个指向内存区域的指针,用于存储读取到的数据
    count:   	指定最多读取的字节数,即缓冲区大小。
    size: 参数buf指向的内存的容量
	flags: 特殊的属性, 一般不使用, 指定为 0
	返回值:实际发送的字节数 = 0,代表连接断开;= -1 接收数据失败
*/

⭐五、TCP通信流程

TCP面向连接、安全、流式传输。传输层协议

  • 面向连接:是一个双向连接,通过三次握手完成,断开连接需要通过四次挥手完成。
  • 安全:tcp通信过程中,会对发送的每一数据包都会进行校验, 如果发现数据丢失, 会自动重传
  • 流式传输:发送端和接收端处理数据的速度,数据的量都可以不一致

TCP通信流程图

img

5.1 服务端文件描述符种类

在tcp的服务器端, 有两类文件描述符(客户端只有通信的文件描述符)

监听的文件描述符 lfd

  • 只需要有一个
  • 不负责和客户端通信, 负责检测客户端的连接请求, 检测到之后调用accept就可以建立新的连接

通信的文件描述符 cfd

  • 负责和建立连接的客户端通信
  • 如果有N个客户端和服务器建立了新的连接, 通信的文件描述符就有N个,每个客户端和服务器都对应一个通信的文件描述符

⭐⭐⭐5.2 文件描述符的内存结构

强烈建议结合视频观看

文件描述符对应的内存结构

  • 一个文件文件描述符对应两块内存, 一块内存是读缓冲区, 一块内存是写缓冲区

    • 注意:read/wirte函数并不是将数据直接发送/读取到网络流中,而是通过两个缓冲区进行操作,然后再由操作系统的内核来将缓冲区的数据发送到网络流中。
  • 读数据: 通过文件描述符将内存中的数据读出, 这块内存称之为读缓冲区

  • 写数据: 通过文件描述符将数据写入到内存中, 这块内存称之为写缓冲区

    image-20241229150626584

监听的文件描述符:

  • 客户端的连接请求发送到服务器端监听的文件描述符的读缓冲区
  • 读缓冲区中有数据, 说明有新的客户端连接
  • 调用accept()函数, 这个函数会检测监听文件描述符的读缓冲区
    • 检测不到数据, 该函数阻塞
    • 如果检测到数据, 解除阻塞, 新的连接建立

通信的文件描述符:

  • 客户端和服务器端都有通信的文件描述符
  • 发送数据:调用函数 write() / send(),数据进入到内核中
    • 数据并没有被发送出去, 而是将数据写入到了通信的文件描述符对应的写缓冲区中
    • 内核检测到通信的文件描述符写缓冲区中有数据, 内核会将数据发送到网络中
  • 接收数据: 调用的函数 read() / recv(), 从内核读数据
    • 数据如何进入到内核程序猿不需要处理, 数据进入到通信的文件描述符的读缓冲区中
    • 数据进入到内核, 必须使用通信的文件描述符, 将数据从读缓冲区中读出即可
int main(int argc, char* argv[]) {

    struct sockaddr_in serv_addr, clit_addr;
    socklen_t clit_addr_len;
//初始化服务器IP地址和端口号
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//socket
    lfd = socket(AF_INET, SOCK_STREAM, 0);
//bind
    bind(lfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr));
//listen
    listen(lfd, 128);
    clit_addr_len = sizeof(clit_addr);
//accept:阻塞等待连接,当连接成功时,客户端的地址结构会填入clit_addr
    cfd = accept(lfd, (struct sockaddr *)&clit_addr, &clit_addr_len);
//TCP通信已建立,处理逻辑省略
    close(lfd);
    close(cfd);
}

5.3 客户端通信流程

只有一个用于通信的套接字cfd

int main(int argc, char *argv[])
{
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv_addr;
    serv_addr.sin_port = htons(SERV_PORT);
    serv_addr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
    int ret = connect(cfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr));
//通信已建立,自行处理数据逻辑
    close(cfd);
    return 0;
}

参考文献

爱编程的大丙:B站同名视频

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

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

相关文章

git clone 和 conda 换源

文章目录 git clone 通过 sshconda 创建虚拟环境通过 env.yml 文件conda 换源 git clone 通过 ssh git clone ssh://用户名IP地址:/仓库名字.gitconda 创建虚拟环境通过 env.yml 文件 conda env create -f environment.ymlconda 换源 Step 1 生成 .bashrc 文件在家目录下。…

机床数据采集网关在某机械制造企业的应用

随着工业4.0时代的到来&#xff0c;智能制造已成为制造业转型升级的重要方向。数控机床作为现代制造业的核心设备&#xff0c;其运行状态和加工参数的数据实时采集与分析对于提升生产效率、优化生产流程具有关键意义。 背景概述 某机械制造企业拥有多台数控机床&#xff0c;这…

c# RSA加解密工具,.netRSA加解密工具

软件介绍 名称: c# RSA加解密工具,.netRSA加解密工具依赖.net版本: .net 8.0工具类型: WinForm源码下载 c# RSA加解密工具,.netRSA加解密工具 依赖项 WinFormsRSA.csproj <Project

穷举vs暴搜vs深搜vs回溯vs剪枝_全排列_子集

46. 全排列 递归解决&#xff1a;一开始选一个数&#xff0c;递归进入下一层再选一个新的数&#xff0c;直到到最后一个数。反会上一层遍历其它数。 每次递归到叶子节点就找到了一种组合&#xff0c;思路有了具体怎么实现&#xff1f; 1.怎么记录每条路径&#xff1f; 定义一个…

【Trick】获取kaggle账号的token和api(用于数据集下载)

0&#xff1a;操作背景 由于未来的科研需要用到Unet&#xff0c;但是运行学长的史山代码无法跑通&#xff0c;自己写了一个Unet并load学长的数据集效果也很差&#xff0c;于是打算从最最基础的开始&#xff0c;上github调用一个Unet并成功在公有数据集上跑一遍实例。 Unet的g…

VS Code AI开发之Copilot配置和使用详解

随着AI开发工具的迅速发展&#xff0c;GitHub Copilot在Cursor、Winsuf、V0等一众工具的冲击下&#xff0c;推出了免费版本。接下来&#xff0c;我将为大家介绍GitHub Copilot的配置和使用方法。GitHub Copilot基于OpenAI Codex模型&#xff0c;旨在为软件开发者提供智能化的代…

论文解读 | NeurIPS'24 Lambda:学习匹配先验以处理无标记垂悬问题场景下的实体对齐任务...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 尹航&#xff0c;上海交通大学博士生 内容简介 我们研究了带有无标记悬挂问题的实体对齐&#xff08;EA&#xff09;任务&#xff0c;即部分实体在另一个…

评分模型在路网通勤习惯分析中的应用——提出问题(1)

1、问题的由来、目标和意义 最近一段时间和公司其它业务部门讨论时&#xff0c;发现一个有趣的交通路网问题&#xff0c;车辆从S点行驶到V点共用时40分钟&#xff0c;这段时间内路网中的卡口摄像头识别到了车辆通过的信息。如下图所示&#xff1a; 设计师需要通过这些有限的路…

Spring Security day 11.23

ok了今天学习一个关于登录角色权限的管理框架&#xff0c;我们一起取看看吧 一.权限控制 1.1 认证和授权概念 前面我们已经完成了后台管理系统的部分功能&#xff0c;例如检查项管 理、检查组管理、套餐管理、预约设置等。接下来我们需要思 考 2 个问题&#xff1a; 问题 1 …

【IC验证】verilog及systemverilog特殊特性的分析

verilog及systemverilog特殊特性的分析 1.概述2.赋值延迟&#xff08;0&#xff09;总结&#xff08;1&#xff09;情况一&#xff1a;initial中进行阻塞赋值和非阻塞赋值&#xff08;不延迟&#xff09;a代码b 电路图c 结果 &#xff08;2&#xff09;时钟a 代码b 电路图c 结果…

FPGA流水线考虑因素

流水线考虑因素 另一种提升性能的方法是对拥有多个逻辑级数的长数据路径进行重新组织&#xff0c;并将其分配在多个时钟周期上。这种方法 以时延和流水线开销逻辑管理为代价&#xff0c;来达到加快时钟周期和提高数据吞吐量的目的。 由于 FPGA 器件带有大量的寄存器&#x…

关于 PCB线路板细节锣槽问题 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/144783817 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

SQL创建和操纵表

本文介绍创建、更改和删除表的基本知识。 1. 创建表 SQL 不仅用于表数据操纵&#xff0c;而且还用来执行数据库和表的所有操作&#xff0c;包括表本身的创建和处理。一般有两种创建表的方法&#xff1a; 多数DBMS 都具有交互式创建和管理数据库表的工具&#xff1b;表也可以…

RPA系列-uipath 学习笔记4

使用Uipath 处理hover的问题 备注&#xff1a;使用uipath stversion&#xff1a;2024.10.6,所有学习来源自uipath Academy 首先&#xff0c;打开uipath给我们提供的一个网站 ACME,这个网站呢&#xff0c;需要提前注册一下的哈。 今天呢&#xff0c;就是记录一下&#xff0c;怎…

Unity编译Android apk包进度奇慢或gradle报错的解决方案

最近遇到Unity编译Android apk进度卡在"Calling IPostGenerateGradleAndroidProject callbacks"进度一直不变&#xff0c;如下图&#xff1a; 最后提示编译失败&#xff0c;类似错误如下&#xff1a; Picked up JAVA_TOOL_OPTIONS: -Dfile.encodingUTF-8FAILURE: Bu…

探究音频丢字位置和丢字时间对pesq分数的影响

丢字的本质 丢字的本质是在一段音频中一小段数据变为0 丢字对主观感受的影响 1. 丢字位置 丢字的位置对感知效果有很大影响。如果丢字发生在音频信号的静音部分或低能量部分&#xff0c;感知可能不明显&#xff1b;而如果丢字发生在高能量部分或关键音素上&#xff0c;感知…

CAT3D: Create Anything in 3D with Multi-View Diffusion Models 论文解读

24年5月的论文&#xff0c;上一版就是ReconFusion 目录 一、概述 二、相关工作 1、2D先验 2、相机条件下的2D先验 3、多视角先验 4、视频先验 5、前馈方法 三、Method 1、多视角扩散模型 2、新视角生成 3、3D重建 一、概述 该论文提出一种CAT3D方法&#xff0c;实现…

模型工作流:自动化的模型内部三角面剔除

1. 关于自动减面 1.1 自动减面的重要性及现状 三维模型是游戏、三维家居设计、数字孪生、VR/AR等几乎所有三维软件的核心资产&#xff0c;模型的质量和性能从根本上决定了三维软件的画面效果和渲染性能。其中&#xff0c;模型减面工作是同时关乎质量和性能这两个要素的重要工…

黑马程序员Java笔记整理(day08)

1.代码块 静态代码块 实例代码块 2.内部类 成员内部类 静态内部类 局部内部类 匿名内部类 认识 常见使用形式 应用场景 简化版本 另一个应用场景 3.函数式编程 Lambda 函数简化 方法引用 4.常用API String ArrayList 5.GUI编程 快速认识 事件处理 三种常用写法 第一种 第二…

redis延迟队列

Redis延迟队列 Redis延迟队列是基于Redis构建的消息队列&#xff0c;用来处理需延迟执行的任务。 基本原理 它借助Redis的有序集合&#xff08;Sorted Set&#xff09;数据结构达成目的。会把任务及其执行时间分别当成成员与分值存进有序集合&#xff0c;由于执行时间作为分值&…