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

Linux系统编程(5)——网络协议

常用网络测试工具:

ifconfig //查看主机上,网卡网络信息
ping //测试 两台主机之间是否连通
telnet //远程登录工具
ssh // 硬件 (开发板)的远程登陆,加密登录
wireshark //网络抓包工具,抓取网络上的数据

一、 TCP首部

1.序号

占4个字节,在一个 TCP 连接中传送的字节流中给每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则指的是本报文段所发送的数据的第一个字节的序号。

2.确认号

占4个字节,期望收到对方下一个报文段的第一个数据字节的序号。

3.数据偏移

指出TCP报文段的首部长度。TCP首部最小长度为20字节,最大长度为60字节。

4.确认ACK和同步SYN

仅当ACK=1,确认号有效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。

在建立连接时用来同步信号。SYN置为1表示这是一个连接请求或连接接受报文。

TCP三次握手

  • 客户端向服务器发送SYN
  • 服务器回复SYN+ACK
  • 客户端回复ACK

5.终止FIN

用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。

TCP四次挥手

  • 客户端结束给服务器发FIN
  • 服务器回复ACK
  • 服务器结束给客户端发FIN
  • 客户端回复ACK

二、 UDP首部

用户数据报UDP有两个字段:数据字段和首部字段。首部字段很简单,只有8个字节(图5-5),由四个字段组成,每个字段的长度都是两个字节。各字段意义如下:

  • (1)源端口 ---- 源端口号。在需要对方回信时选用。不需要时可用全0。
  • (2)目的端口 ---- 目的端口号。这在终点交付报文时必须要使用到。
  • (3)长度 ---- UDP用户数据报的长度,其最小值是8(仅有首部)。
  • (4)检验和 ---- 检测UDP用户数据报在传输中是否有错。有错就丢失。

三、 HTTP协议报文

HTTP有两类报文:请求报文和响应报文。

HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:

  • 开始行(start line):描述请求或响应的基本信息;
  • 首部行(header):使用键值对形式更详细地说明报文;
  • 实体主体(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。

1. 请求报文

  • 请求报文里的开始行,叫作请求行由三部分构成:
  • 请求方法:是一个动词,如 GET/POST,表示对资源的操作;
  • CRL(请求目标):通常是一个 URL,标记了请求方法要操作的资源;
  • 版本号:表示报文使用的 HTTP 协议版本。

这三个部分通常使用空格(space)来分隔,最后要用 CRLF 表示结束。CRLF表示"\r\n"标志,每一行末尾都要有CRLF,首部行全部结束也需要一个CRLF标志。

2. 响应报文

响应报文里的开始行,在这里叫“状态行”(status line),意思是服务器响应的状态。

比起请求行来说,状态行要简单一些,同样也是由三部分构成:

  • 版本号:表示报文使用的HTTP协议版本;
  • 状态码:一个三位数,用代码的形式表示处理的结果,例如200是服务器成功。
  • 短语:数字状态码的补充,更详细的解释文字。

四、服务器模型

服务器可以实现与客户端的连接通信,现在所见的服务器,都是可以同时支持多个用户访问。在前面学习网络编程时,自己编写的服务器都只能实现连接一个服务器,那么让多个用户同时访问的服务器怎么做到的。

1. 迭代服务器:

可以线性处理多个客户端,但不能同时与客户端通信。就像排队一样,通信完一个再通信下一个。

while(1) { accept; while(1) { recv; send; } }

2. 并发服务器:

支持同时多个用户进行访问,称作并发服务器,这种服务器一般需要用到多进程或线程来实现多用户的同时访问。

多进程的特点:

  • 资源开销大。
  • 进程的退出,需要考虑僵尸态资源的回收。

多线程特点:

  • 相比进程,线程的创建和调度开销小。
  • 共享资源方便。
  • 存在线程间资源的竞争和同步问题。
while(1) { accept; pid_t pid=fork; if(pid==0) { while(1) { recv; send; } } }

3. IO多路复用:

在一个进程中,有多个进程或线程,每个进程可以管理多个客户端的输入输出。

目的:提高并发的程度。

特点:

  • 宏观并行,微观串行。
  • 系统开销小。
  • 系统不必创建进程/线程,也不必维护进程/线程。

IO操作模型有四种:阻塞IO,非阻塞IO,信号驱动IO,IO多路复用。

3.1 阻塞IO

在读取数据时,内核没有数据,程序会阻塞等待,直到有数据的输入。

例如:scanf、getchar、recv的默认方式。

特点:实现简单,效率低。

3.2 非阻塞IO

当区内核读取数据时,如果内核无数据,会立即返回一个EWOULDBLOCK的错误码。非阻塞方式如果想获得数据,必须配合轮询操作,一直不断读取数据,直到有数据。

特点:耗CPU资源。

非阻塞IO是在阻塞IO的基础上调整其为不再阻塞等待,有两种方式:

  • 在程序开始阶段调整文件间的打开方式为非阻塞。
fd=open(filename,O_RDONLY|O_NONBLOCK);
  • 在程序执行阶段调整文件的执行方式为非阻塞。
int flag=fcntl(fd,F_GETFL,0); flag=flag|O_NONBLOCK; fcntl(fd,F_SETFL,flag);

3.3 信号驱动IO

建立SIGIO信号处理程序,有数据来时,内核会给进程发信号,进程收到信号后,调用信号处理函数去做读写操作。

特点:异步处理,效率高,只能处理一路IO。

使用流程

  • 1.设置信号处理函数。
  • 2.给文件描述符绑定调用进程作为属主。
  • 3.设置标志。O_NONBLOCK使能非阻塞IO。O_ASYNC使能信号驱动IO。
signal(SIGIO,do_handler); fcntl(fd,F_SETOWN,pid); fcntl(fd,F_SETLF,flag|O_ASYNC|O_NONBLOCK);

3.4 IO多路复用

用一个函数(select/epoll)监控多路IO,如果哪一路IO有读到数据,则对应的函数返回,告诉用户进程有就绪的IO,用户进程就去读数据。

3.4.1 select实现IO多路复用:

使用流程:

  • 1. 准备监控表。
  • 2. 添加文件到集合中。
  • 3. 准备nfds。
  • 4. 调用selec监听函数。
  • 5. 检测到select返回,用FD_ISSET确认哪路IO,然后读取。

缺点

  • 文件描述符数量受限。(在linux中限制1024个)
  • 底层通过遍历实现,效率不高。
  • 返回的是“就绪个数”,应用层还要进一步筛选。
1. socket; 2. bind; 3. listen; 4. 创建监控表 fd_set readfds; FD_ZERO(&readfds); FD_SET(fd,&readfds); nfds=fd+1; while(1) { fd_set backfds=readfds; int ret=select(nfds,&backfds,NULL,NULL,NULL); if(ret>0) { for(i=0;i<nfds;i++) { if(FD_ISSET(i,&readfds)) { if(i==fd)//如果是监听表里的则建设连接 { connfd=accept; if(connet>nfds) nfds=connet+1; } }else//不是说明已连接直接通信 { recv; } } } } }
3.4.1 epoll实现IO多路复用:

相比 select epoll 主要解决这些性能问题:

  • 1. 突破文件描述符数量限制
  • 2. 返回结果就是触发事件列表,不需要再遍历整个输入集合查找
  • 3. 使用共享内存思路,减少用户态与内核态之间反复传递大块监听信息

使用流程:

  • 1.创建一个epoll实例
  • 2.控制fd再epoll中的监听行为
  • 3.等待epoll监听到的IO事件,返回一个已触发的事件列表
1.socket 2.bind 3.listen //1.epoll_create 创建监控用的epoll对象 //2.添加监控的fd --epoll_ctl listenfd while (1) { ret = epoll_wait(); if (ret > 0) { for (i = 0; i < ret; ++i) { if (rev[i].data.fd == listenfd) { connfd = accept(); }else //通信 { recv; } } } }

4. 函数扩展

1). fcntl函数

语法:int fcntl(int fd, int cmd, ... /* arg */ );

功能:修改指定文件的属性信息。

参数

  • @fd --- 指定的文件描述符
  • @cmd --- 要调整的文件属性宏名
  • @ ... --- 可变长的属性值参数

2). select函数

头文件:#include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h>

语法:int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

功能:监控多路IO,看是否有就绪的IO。

参数

  • @nfds --- 表示要监控的集合中最大的文件描述符加1
  • @readfds --- 是否可读的集合
  • @writefds --- 是否可写的集合
  • @exceptfds --- 是否异常的集合
  • @timeout --- 0表示select是一个非阻塞调用;大于0表示select阻塞对应的一段时间;NULL表示默认阻塞调用。
    struct timeval { long tv_sec; long tv_uses; }; //使用: struct timeval tv={3,0}; select(nfds,&readfds,NULL,NULL,&tv);

返回值:成功返回就绪的文件描述符的数量,失败返回-1且errno被设置。

注意:select函数调用完之后,会将监控结果的文件描述符集合带回给用户端,原先的集合不再是需要监控的文件描述符集合,而是就绪的文件描述符集合。如果需要循环监控,则需要中间变量保存原先的集合。

配套函数

void FD_CLR(int fd, fd_set *set); --- 从set集合中清除fd
int FD_ISSET(int fd, fd_set *set); --- 判断fd是否在set集合中
void FD_SET(int fd, fd_set *set); --- 将fd添加到set集合中
void FD_ZERO(fd_set *set); --- 将set集合清零

3). epoll_create函数

头文件:#include<sys/epoll.h>

语法:int epoll_create(int size);

功能:创建一个 epoll 实例。

参数

  • @size --- size 不是最大监听数量,只是对内核初始分配的建议。

返回值:成功返回epoll对象的描述符,失败返回-1。

4). epoll_ctl函数

语法:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数:

  • @epfd --- epoll对象
  • @op --- 对描述符执行的操作
    EPOLL_CTL_ADD :添加监听
    EPOLL_CTL_DEL :删除监听
    EPOLL_CTL_MOD :修改监听
  • @fd --- 要操作的fd
  • @event --- 事件的结构体

struct epoll_event{

uint32_tevents; -------epoll事件

epoll_data_tdata;-------用户的数据

}

typedef union epolldata{

void* ptr;

intfd; ---------关心的文件

uint32_t u32;

uint64_t u64;

}epoll_data_t;

events事件的宏定义

EPOLLIN 文件可读 EPOLLOUT 文件可写 EPOLLERR 异常条件 EPOLLET 边沿触发,描述符跳变时上报

5). epoll_wait函数

语法:int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

功能:等待epoll监听到的IO事件,最多返回 maxevents 个事件。

参数

  • @epfd --- 监听的epoll对象
  • @events --- 获得就绪时间的一个结构体指针(可以看作一个数组名)
  • @maxevents --- 数组长度
  • @timeout --- 设置超时时间,单位ms。0表非阻塞,-1表阻塞,大于0表阻塞多少毫秒

返回值:成功返回就绪的文件描述符数量,失败返回-1且errno被设置。

http://www.cnnetsun.cn/news/1265002.html

相关文章:

  • VS Code 只有 Ask、没有 Agent 选项的处理记录
  • 问题解决:npm 无法加载文件 D:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本
  • Simulink双三相永磁同步电机控制仿真! 1.矢量控制,包括两种电机建模,VSD模型和双d...
  • 一款轻量高效的 M3U8 在线播放工具,开发者调试必备
  • ROS基础学习4-发布者publisher编程实现
  • 基于协同过滤的房屋租赁推荐系统:打造便捷租房体验
  • (实战篇)手把手实战:利用永恒之蓝(MS17-010)漏洞深入理解渗透测试+修复方法
  • 最新全工具版本Qt+OpenCV通用视觉框架全套源码。 工具可扩展。 除了opencv和相机s...
  • 最近在捣鼓信号异常检测的时候,发现有个挺有意思的方法。不需要复杂的深度学习框架,直接在MATLAB里用传统信号处理+图像处理思路就能搞定。咱们先看个实际案例
  • AI应用架构师总结:智能搜索系统优化的8个核心指标与提升方法
  • 序列线性规划(SLP)在可再生能源系统优化中的应用与挑战
  • 提示工程架构师必学:Agentic AI中的强化学习结合策略
  • Qwen3-VL-8B与STM32的跨界实践:嵌入式设备视觉问答原型开发
  • 轻松抓取虫虫钢琴在线音频的实用技巧
  • GLM-4V-9B开源大模型应用:科研论文插图智能解读+方法复现提示生成
  • SpringBoot Jar包热更新秘籍:零停机修改配置文件的运维技巧
  • postgresql链接详解
  • 深夜还在敲代码?别硬撑,你已经很辛苦了
  • Python实战:身份证OCR识别与结构化数据提取全攻略
  • MLX90614红外测温传感器在天空星HC32F4A0开发板上的SMBus驱动移植与实战
  • Heron Handoff 插件:Figma 设计标注的离线革命与跨平台协作新体验
  • Qwen3-ASR-1.7B模型安全教程:防御对抗样本攻击
  • Mac 上高效编写 LaTeX:VSCode + LaTeX Workshop 完全配置指南
  • SillyTavern进阶配置指南:打造个性化AI对话体验
  • 从零到上线:基于快马平台实战构建可部署的trae国际版音乐应用
  • 【bypass-paywalls-chrome-clean】:开源内容访问优化工具的核心价值与实战指南
  • 模电实战:从比例到积分,运算电路的工程设计与避坑指南
  • GD32实战:用485和Ymodem协议实现远程固件升级(含完整代码解析)
  • 【MySQL】索引原理详解
  • Sambert镜像快速入门:部署、测试、应用,一站式语音合成体验