当前位置: 首页 > news >正文

Linux套接字通信学习

Linux套接字通信

在网络通信的时候, 程序猿需要负责的应用层数据的处理(最上层),而底层的数据封装与解封装(如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_INET6src:传入参数,IP地址(点分十进制)dst:传出参数,转换后的 网络字节序的 IP地址。 返回值:转换是否成功
*///网络字节序→String
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
/*af:AF_INET、AF_INET6src: 传入参数,网络字节序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);
//socketlfd = socket(AF_INET, SOCK_STREAM, 0);
//bindbind(lfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr));
//listenlisten(lfd, 128);clit_addr_len = sizeof(clit_addr);
//accept:阻塞等待连接,当连接成功时,客户端的地址结构会填入clit_addrcfd = 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站同名视频

相关文章:

Linux套接字通信学习

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

mybatis-plus 用法总结

MyBatis-Plus&#xff08;简称 MP&#xff09;是 MyBatis 的增强工具&#xff0c;旨在简化开发者的 CRUD 操作。它在 MyBatis 的基础上提供了更多的功能和便利性&#xff0c;如代码生成器、分页插件、性能分析插件等&#xff0c;使开发者能够更高效地进行数据库操作。MyBatis-P…...

小程序配置文件 —— 14 全局配置 - tabbar配置

全局配置 - tabBar配置 tabBar 字段&#xff1a;定义小程序顶部、底部 tab 栏&#xff0c;用以实现页面之间的快速切换&#xff1b;可以通过 tabBar 配置项指定 tab 栏的表现&#xff0c;以及 tab 切换时显示的对应页面&#xff1b; 在上面图中&#xff0c;标注了一些 tabBar …...

Redis-十大数据类型

Reids数据类型指的是value的类型&#xff0c;key都是字符串 redis-server:启动redis服务 redis-cli:进入redis交互式终端 常用的key的操作 redis的命令和参数不区分大小写 &#xff0c;key和value区分 1、查看当前库所有的key keys * 2、判断某个key是否存在 exists key 3、查…...

linux系统编程(七)管道和FIFO

1、管道 使用系统调用pipe可以创建一个新管道&#xff1a; #include <unistd.h> int pipe(int filedes[2]);成功的pipe调用会在数组filedes中返回两个打开的文件描述符&#xff0c;读取端为filedes[0]&#xff0c;写入端为filedes[1]。我们可以使用read/write系统调用在…...

【vLLM大模型TPS测试三部曲】

安装 pip install vllm模型自行下载 例如: https://modelscope.cn/models/jackle/Qwen2.5-Coder-32B-GPTQ-Int4/ 部署测试 export VLLM_MODELQwen2.5-Coder-32B-GPTQ-Int4 # 启动 python3 -m vllm.entrypoints.openai.api_server --model $VLLM_MODEL --deviceauto --enf…...

Elasticsearch:使用 Ollama 和 Go 开发 RAG 应用程序

作者&#xff1a;来自 Elastic Gustavo Llermaly 使用 Ollama 通过 Go 创建 RAG 应用程序来利用本地模型。 关于各种开放模型&#xff0c;有很多话要说。其中一些被称为 Mixtral 系列&#xff0c;各种规模都有&#xff0c;而一种可能不太为人所知的是 openbiollm&#xff0c;这…...

Windows平台ROBOT安装

Windows环境下ROBOT的安装,按照下文进行部署ROBOT的前提是你的python已安装并且环境变量已设置好. 一、安装setuptools 1、下载后安装 https://pypi.python.org/pypi/setuptools/ 下载你需要的包 setuptools-75.6.0.tar.gz 解压下载的包在命令行中进入该包,敲击如下命令后…...

【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码

欢迎拜访&#xff1a;羑悻的小杀马特.-CSDN博客 本篇主题&#xff1a;带你众人皆知的约瑟夫环问题 制作日期&#xff1a;2024.12.29 隶属专栏&#xff1a;C/C题海汇总 目录 引言&#xff1a; 一约瑟夫环问题介绍&#xff1a; 11问题介绍&#xff1a; 1.2起源与历史背景&…...

代码随想录算法训练营第51期第32天 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

理论基础 动态规划&#xff1a;dp&#xff0c;每一个状态都是由上个状态推导出来的&#xff0c;因为我是先写完三道题再看理论的&#xff0c;所以有点感概&#xff1b; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举…...

爱思唯尔word模板

爱思唯尔word模板 有时候并不一定非得latex https://download.csdn.net/download/qq_38998213/90199214 参考文献书签链接...

每日一题 354. 俄罗斯套娃信封问题

354. 俄罗斯套娃信封问题 需要对信封排序 ,重点是再宽度相同时&#xff0c;逐步减少其高度 class Solution { public:int maxEnvelopes(vector<vector<int>>& envelopes) {sort(envelopes.begin(),envelopes.end(),[](const vector<int>&a,const v…...

ASP.net网站的注册、登录和密码修改的操作详解

一、进入注册、登录和密码修改操作详解 ASP.net网站为用户提供不同权限状态下的操作界面。根据用户登录状态&#xff0c;页面会显示不同的选项。 已登录用户的操作 图1 登录后操作界面 当用户已登录系统时&#xff0c;会显示以下内容和功能&#xff1a; 1. 欢迎信息 页面顶部…...

2024.12.29(进程线程实现并发服务器)

作业 多进程多线程并发服务器实现一遍提交。 服务器 #include <myhead.h> #define PORT 12345 #define IP "192.168.124.123"void *fun(void *fd) {int newfd *(int *)fd;char buff[1024];while(1){int res recv(newfd,buff,sizeof(buff),0);if(res 0){p…...

如何在 Ubuntu 上安装 PyTorch

简介 PyTorch 因其易用性、动态计算图和高效性而日益流行&#xff0c;成为实现深度学习模型的首选。如果你想探索这个工具并学习如何在 Ubuntu 上安装 PyTorch&#xff0c;本指南将对你有所帮助&#xff01; 在本教程中&#xff0c;我们将引导你完成在 Ubuntu 系统上使用 Pip…...

8-Gin 中间件 --[Gin 框架入门精讲与实战案例] 【文末有测试代码】

路由中间件 Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。它以性能好、中间件支持灵活著称&#xff0c;非常适合用来构建微服务或 RESTful API 服务。下面我将提供三个使用 Gin 的路由中间件的完整示例。 示例 1: 简单的日志记录中间件 这个中间件会在每个请求处理前后打…...

【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。

目录 一、toString() 方法是啥&#xff1f; &#xff08;一&#xff09;默认的 toString() 方法 &#xff08;二&#xff09;toString() 方法的作用 二、为啥要重写 toString() 方法&#xff1f; &#xff08;一&#xff09;提高代码的可读性 &#xff08;二&#xff09;…...

【论文笔记】Contrastive Learning for Sign Language Recognition and Translation

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Contrastive Learning for…...

Gitlab17.7+Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用)

一、gitlab设置&#xff1a; 1、进入gitlab选择主页在左侧菜单的下面点击管理员按钮。 2、选择左侧菜单的设置&#xff0c;选择网络&#xff0c;在右侧选择出站请求后选择允许来自webhooks和集成对本地网络的请求 3、webhook设置 进入你自己的项目选择左侧菜单的设置&#xff…...

一起来看--红黑树

【欢迎关注编码小哥&#xff0c;学习更多实用的编程方法和技巧】 红黑树是一种自平衡的二叉搜索树&#xff0c;广泛应用于计算机科学中&#xff0c;尤其是在实现关联数组和集合时。它的设计旨在确保在最坏情况下&#xff0c;基本动态集合操作&#xff08;如插入、删除和查找&am…...

SpringBoot整合篇 05、Springboot整合Redission

文章目录 前言Redission详细配置步骤pom依赖application.yaml配置类CacheConfigEnvironmentContext RedissionController单测 前言 本篇博客是SpringBoot整合Redission&#xff0c;若文章中出现相关问题&#xff0c;请指出&#xff01; 所有博客文件目录索引&#xff1a;博客…...

供应链系统设计-供应链中台系统设计(六)- 商品中心概念篇

概述 我们在供应链系统设计-中台系统设计系列&#xff08;五&#xff09;- 供应链中台实践概述 中描述了什么是供应链中台&#xff0c;供应链中台主要包含了那些组成部门。包括业务中台、通用中台等概念。为了后续方便大家对于中台有更深入的理解&#xff0c;我会逐一针对中台…...

胡闹厨房练习(三)

ScriptableObject 一、初步了解 1、实质:是一种特殊类型的Unity对象, 2、作用:用于存储大量数据,而不必依附于游戏场景中的某个GameObject。 3、特点: 可以在不增加场景中对象数量的情况下,管理和存储复杂的数据结构、配置信息、游戏状态等。 4、适用:非常适合用来…...

关于ESD(静电放电)等级的划分

关于ESD&#xff08;静电放电&#xff09;等级的划分&#xff0c;主要依据不同的测试模型和测试标准。以下是对HBM&#xff08;人体模型&#xff09;和CDM&#xff08;充电器件模型&#xff09;两种测试模型下ESD等级划分的详细解释&#xff1a; HBM ESD等级划分 HBM ESD等级…...

探究步进电机与输入脉冲的关系

深入了解步进电机 前言一、 步进电机原理二、 细分三、脉冲数总结 前言 主要是探究以下内容&#xff1a; 1、步进电机的步进角。 2、什么是细分。 3、脉冲的计算。 最后再扩展以下STM32定时器的计算方法。 一、 步进电机原理 其实语言描述怎么样都不直观&#xff0c;我更建议…...

基于YOLOV5+Flask安全帽RTSP视频流实时目标检测

1、背景 在现代工业和建筑行业中&#xff0c;安全始终是首要考虑的因素之一。特别是在施工现场&#xff0c;工人佩戴安全帽是确保人身安全的基本要求。然而&#xff0c;人工监督难免会有疏漏&#xff0c;尤其是在大型工地或复杂环境中&#xff0c;确保每个人都佩戴安全帽变得非…...

Windows内置的服务器IIS(Internet Information Services)托管网站

一. 安装IIS 打开控制面板&#xff1a;在开始菜单搜索“控制面板”并打开它。程序和功能&#xff1a;点击“程序”然后选择“程序和功能”。启用或关闭Windows功能&#xff1a;在左侧菜单中选择“启用或关闭Windows功能”。查找并勾选IIS&#xff1a;在弹出的窗口中&#xff0c…...

虚幻引擎结构之UObject

一. UObject 的介绍 UObject 是虚幻引擎中的核心基础类,所有其他游戏对象和资源类都直接或间接地继承自它。作为虚幻引擎的基石,UObject 提供了多项关键功能,包括内存管理、序列化、反射(introspection)、垃圾回收以及元数据支持。在虚幻引擎中,UObject 类的实例通常被称…...

js的Reflect对象

Reflect 对象是 JavaScript ES6 中引入的一个内建对象&#xff0c;它提供了一系列与对象操作相关的方法。这些方法与 Object 对象上的方法类似&#xff0c;但在行为上有一些差异&#xff0c;并且更加规范和统一。Reflect 对象并不是一个构造函数&#xff0c;不能被 new 操作符调…...

this指向了谁?

看函数在执行的时候是如何调用的&#xff0c; 1 如果这个函数是用普通函数调用模式来进行调用&#xff0c;它内部的this指向了window; 2 如果一个函数在调用的时候是通过对象方法模式来进行调用&#xff0c;则它内部的this就是我们的对象; 3 如果一个函数在调用的时候通过构…...