Linux进阶篇:网络编程

开篇:本文解决什么问题?

  • 对TCP/UDP协议的核心特性与区别理解不透彻
  • 不熟悉socket套接字的基础操作及I/O模型的适用场景
  • 缺乏高并发网络程序的设计思路,无法应对海量连接的性能瓶颈

一、网络协议基础

1. TCP与UDP核心特性对比

TCP和UDP是传输层核心协议,适用于不同的网络通信场景,核心差异体现在可靠性、传输方式等方面:

|------|--------------------|---------------------|
| 特性 | TCP | UDP |
| 连接特性 | 面向连接(三次握手建立连接) | 无连接(无需建立连接直接发送) |
| 可靠性 | 可靠传输(重传、确认、排序) | 不可靠传输(无确认、重传机制) |
| 传输方式 | 字节流传输 | 数据报传输 |
| 拥塞控制 | 支持拥塞控制、流量控制 | 无拥塞控制 |
| 适用场景 | 文件传输、HTTP/HTTPS等 | 视频直播、游戏、DNS等 |

2. TCP关键机制

三次握手:建立TCP连接,确保双方收发能力正常

四次挥手:关闭TCP连接,保证数据全部传输完成

滑动窗口:实现流量控制,避免发送方速率过快导致接收方缓冲区溢出

拥塞控制:通过慢启动、拥塞避免等算法,适应网络拥塞状态

二、Socket编程基础

1. Socket核心操作函数

(1)套接字创建:socket()
cpp 复制代码
int socket(int domain, int type, int protocol);

参数:

  • domain :协议域(如 AF_INET 表示IPv4, AF_UNIX 表示本地域)
  • type :套接字类型( SOCK_STREAM 为TCP流套接字, SOCK_DGRAM 为UDP数据报套接字)
  • protocol :协议类型(通常设为0,自动匹配type对应的协议)

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

(2)地址绑定:bind()
cpp 复制代码
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

将套接字与本地IP地址端口绑定, addr 为协议地址结构(如 struct sockaddr_in ), addrlen 为地址长度。

(3)监听连接:listen()
cpp 复制代码
int listen(int sockfd, int backlog);

仅用于TCP套接字,将套接字转为监听状态, backlog 指定半连接队列的最大长度。

(4)接受连接:accept()
cpp 复制代码
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

阻塞等待并接受TCP客户端连接,返回新的套接字描述符用于与客户端通信, addr 用于获取客户端地址。

(5)发起连接:connect()
cpp 复制代码
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

TCP客户端用于向服务器发起连接, addr 为服务器的协议地址。

(6)数据收发

TCP收发: read() / write() 、 recv() / send() ,基于字节流传输

UDP收发: recvfrom() / sendto() ,需指定对方地址,基于数据报传输

2. TCP服务端/客户端通信流程

服务端: socket() → bind() → listen() → accept() → 收发数据 → 关闭套接字

客户端: socket() → connect() → 收发数据 → 关闭套接字

三、I/O模型与多路复用

1. 常见I/O模型

|---------|------------------------------------------|----------------|
| I/O模型 | 特点 | 适用场景 |
| 阻塞I/O | 调用I/O函数后阻塞,直到数据就绪 | 简单场景、低并发连接 |
| 非阻塞I/O | 轮询检查数据就绪,未就绪时返回错误 | 需实时响应的简单场景 |
| I/O多路复用 | 通过 select / poll / epoll 监听多个fd,数据就绪后再处理 | 高并发连接(如百万级连接) |
| 信号驱动I/O | 通过信号通知数据就绪,异步触发 | 特定场景(如UDP数据接收) |
| 异步I/O | 内核完成数据读写后通知进程,全程无阻塞 | 高性能后台服务 |

2. I/O多路复用核心实现

(1)select/poll

select :监听有限数量的文件描述符(默认1024),需轮询检查就绪fd,效率较低

poll :突破fd数量限制,仍需轮询,性能提升有限

(2)epoll(高性能I/O多路复用)

epoll是Linux特有的多路复用机制,支持海量文件描述符,核心函数:

cpp 复制代码
int epoll_create(int size); // 创建epoll实例
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); // 注册/修改/删除fd
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); // 等待fd就绪

触发模式:水平触发(LT)和边沿触发(ET),ET模式需配合非阻塞I/O使用,效率更高。

优势:基于事件驱动,无需轮询,仅处理就绪fd,适合高并发场景。

四、高并发网络程序设计

1. 多进程/多线程模型

多进程:通过 fork() 创建子进程处理客户端连接,进程间独立,稳定性高,但创建/切换开销大。

多线程:通过pthread创建线程处理连接,开销小于进程,但需解决线程同步问题。

2. 线程池/进程池模型

预先创建一定数量的进程/线程,复用资源处理客户端请求,避免频繁创建/销毁的开销,组成:

任务队列:存储客户端连接任务

工作线程/进程:循环获取任务并处理

同步机制:互斥锁+条件变量保证任务队列线程安全

3. 反应堆模型(Reactor)

基于epoll的事件驱动模型,将I/O事件、信号事件等抽象为事件,由反应堆框架统一处理:

主反应堆:监听客户端连接事件,接受连接后将fd注册到子反应堆

子反应堆:处理已连接fd的读写事件,实现高并发处理

4. 协程模型

基于用户态的轻量级线程,切换开销远小于线程,通过协程调度器实现高并发,如libco、ucontext等库,适用于I/O密集型场景。

五、网络编程关键问题

1. 粘包问题(TCP)

TCP是字节流传输,易出现粘包,解决方法:固定消息长度;消息头部添加长度字段;使用特殊分隔符分割消息。

2. 半关闭与优雅退出

通过 shutdown() 实现套接字的半关闭,保证数据全部收发完成后再关闭连接,避免数据丢失。

3. 端口复用

通过 setsockopt() 设置 SO_REUSEADDR 选项,允许端口快速复用,避免服务重启时出现"地址已被使用"的错误。

4. 心跳机制

在TCP连接中添加心跳包,检测对方是否在线,避免无效连接占用资源,通常通过定时发送小数据包实现。

相关推荐
2301_811958382 小时前
服务器自己账号下安装conda
linux·python·conda
一颗青果2 小时前
Socket编程实例(UDP)
网络·网络协议·udp
whltaoin2 小时前
25年12月26日-福州某科技公司一面面试原题
java·linux·docker·面试·职场和发展·k8s·springboot
网硕互联的小客服2 小时前
如何搭建个人邮局或者企业邮局?使用什么邮局系统好?
linux·运维·服务器·安全
九皇叔叔2 小时前
CentOS 容器安装部署
linux·运维·centos
云动课堂2 小时前
【运维实战】企业级 NFS 文件共享服务 · 一键自动化部署方案 (适配银河麒麟 V10 /openEuler /CentOS)
运维·centos·自动化
真上帝的左手2 小时前
7. 网络安全-等保
网络·安全·web安全
一颗青果2 小时前
Socket编程(TCP)
网络·网络协议·tcp/ip
蓝影铁哥2 小时前
浅谈5款Java微服务开发框架
java·linux·运维·开发语言·数据库·微服务·架构