十七、TCP编程
TCP 编程是网络通信的核心,其 API 围绕面向连接的特性设计,涵盖服务端和客户端的交互流程。以下是基于 C 语言的 TCP 编程核心 API 及使用流程的详细解析:
核心 API 概览
| 函数 | 角色 | 描述 |
|---|---|---|
socket() | 通用 | 创建套接字,指定协议族(IPv4/IPv6)和类型(SOCK_STREAM)。 |
bind() | 服务端 | 将套接字绑定到特定 IP 地址和端口。 |
listen() | 服务端 | 将套接字设为监听模式,等待客户端连接请求。 |
accept() | 服务端 | 接受客户端连接,返回用于通信的新套接字。 |
connect() | 客户端 | 客户端主动连接服务端。 |
send()/write() | 通用 | 发送数据(TCP 保证数据顺序,可能拆包/粘包)。 |
recv()/read() | 通用 | 接收数据(需处理部分读取和缓冲区管理)。 |
close() | 通用 | 关闭套接字,终止连接。 |
shutdown() | 通用 | 优雅关闭连接(可选关闭读/写方向)。 |
1、socket函数
函数原型与头文件
#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);
参数详解
domain(协议族/地址族)
定义通信使用的协议族,常见值包括:
| 值 | 描述 |
|---|---|
AF_INET | IPv4 协议(最常用) |
AF_INET6 | IPv6 协议 |
AF_UNIX/AF_LOCAL | 本地进程间通信(UNIX 域套接字) |
AF_PACKET | 底层数据包接口(如原始以太网帧捕获) |
如果是IPV6编程,要使用struct sockddr_in6结构体(man 7 IPV6),通常使用struct sockaddr_storage来编程。
type(套接字类型)
指定数据传输的语义,常用类型:
| 值 | 描述 |
|---|---|
SOCK_STREAM | 面向连接的流式套接字(TCP,可靠传输) |
SOCK_DGRAM | 无连接的数据报套接字(UDP,尽最大努力交付) |
SOCK_RAW | 原始套接字(直接访问 IP/ICMP 等协议) |
protocol(具体协议)
通常设为 0,表示根据 domain 和 type 自动选择默认协议。例如:
SOCK_STREAM默认使用IPPROTO_TCPSOCK_DGRAM默认使用IPPROTO_UDP
若需显式指定协议,可用:
| 值 | 描述 |
|---|---|
IPPROTO_TCP | 强制使用 TCP 协议 |
IPPROTO_UDP | 强制使用 UDP 协议 |
IPPROTO_ICMP | 用于原始套接字的 ICMP 协议 |
返回值
- 成功:返回一个非负整数(套接字文件描述符),后续操作(如
bind,connect)均基于此描述符。 - 失败:返回
-1,并设置errno表示错误原因(如EACCES,EAFNOSUPPORT)。
典型使用场景
创建 TCP 套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {perror("socket() failed");exit(EXIT_FAILURE);
}
- 用于 HTTP、FTP 等需要可靠传输的应用。
创建 UDP 套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {perror("socket() failed");exit(EXIT_FAILURE);
}
- 用于 DNS 查询、实时音视频传输等场景。
创建原始套接字(需 root 权限)
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd == -1) {perror("socket() failed");exit(EXIT_FAILURE);
}
- 可手动构造 IP 或 ICMP 头部,用于网络探测工具(如
ping)。
2、bind函数
bind() 函数是网络编程中用于将套接字(socket)与特定的IP地址和端口绑定的关键步骤,常用于服务端设置监听地址。以下是 bind() 的详细解析,包含函数原型、参数解释、使用示例及常见问题:
函数原型
#include <sys/types.h>
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数
| 参数 | 类型 | 描述 |
|---|---|---|
sockfd | int | 已创建的套接字描述符(由socket()返回) |
addr | struct sockaddr* | 指向保存绑定地址信息的结构体指针(需根据协议族填充对应的结构类型) |
addrlen | socklen_t | 地址结构体的长度(字节数) |
地址结构体
1. IPv4 地址结构 (struct sockaddr_in)
struct sockaddr_in {sa_family_t sin_family; // 地址族(如 AF_INET)in_port_t sin_port; // 端口号(需用 `htons()` 转换字节序)struct in_addr sin_addr; // IPv4 地址(需用 `inet_pton()` 或 `htonl(INADDR_ANY)`)unsigned char sin_zero[8]; // 填充字段(一般置零)
};struct in_addr {uint32_t s_addr; // 32位 IPv4 地址(网络字节序)
};
2. IPv6 地址结构 (struct sockaddr_in6)
struct sockaddr_in6 {sa_family_t sin6_family; // 地址族(AF_INET6)in_port_t sin6_port; // 端口号(网络字节序)uint32_t sin6_flowinfo; // IPv6 流信息(通常为0)struct in6_addr sin6_addr; // IPv6 地址uint32_t sin6_scope_id; // 接口范围标识符(用于本地链路地址)
};struct in6_addr {unsigned char s6_addr[16]; // 128位 IPv6 地址
};
3. 通用地址结构 (struct sockaddr)
struct sockaddr {sa_family_t sa_family; // 地址族(AF_xxx)char sa_data[14];// 具体地址数据(由子结构展开填充)
};
使用场景与示例
1. 服务端绑定 IP 和端口
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) { perror("socket"); exit(1); }// 允许地址重用(避免服务端重启时 bind 失败)
int optval = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));struct sockaddr_in addr = {0};
addr.sin_family = AF_INET; // IPv4
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定所有本地 IP
addr.sin_port = htons(8080); // 绑定端口 8080if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind");close(server_fd);exit(1);
}// 后续可调用 listen() 启动监听
2. 客户端绑定特定源地址
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in client_addr = {0};
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(12345); // 指定客户端端口
inet_pton(AF_INET, "192.168.1.100", &client_addr.sin_addr); // 指定源 IPbind(client_fd, (struct sockaddr*)&client_addr, sizeof(client_addr));
connect(client_fd, server_addr, sizeof(server_addr));
3、listen函数
函数原型
#include <sys/types.h>
#include <sys/socket.h>int listen(int sockfd, int backlog);
参数详解
| 参数 | 类型 | 描述 |
|---|---|---|
sockfd | int | 已通过 bind() 绑定地址的套接字描述符(必须是 SOCK_STREAM 类型)。 |
backlog | int | 已建立连接(完成三次握手)的队列最大长度,决定同时等待 accept() 处理的连接数。 |
核心作用
-
转换套接字状态:
- 将套接字从主动模式(默认)转为被动模式,使其能够接收客户端的连接请求。
- 未调用
listen()的套接字无法调用accept()。
-
管理连接队列:
- 内核为监听套接字维护两个队列(具体实现可能因操作系统而异):
- 未完成队列(SYN_RCVD 状态):客户端已发送 SYN,但未完成三次握手。
- 已完成队列(ESTABLISHED 状态):已完成三次握手,等待
accept()取出。
backlog参数通常指已完成队列的最大长度(Linux 中默认上限由/proc/sys/net/core/somaxconn定义)。
- 内核为监听套接字维护两个队列(具体实现可能因操作系统而异):
使用场景与示例
1. 服务端启动监听
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
// ...绑定地址(bind())...// 设置监听队列长度为 128
if (listen(server_fd, 128) == -1) {perror("listen() failed");close(server_fd);exit(EXIT_FAILURE);
}// 循环接受客户端连接
while (1) {struct sockaddr_in client_addr;socklen_t addrlen = sizeof(client_addr);int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addrlen);// ...处理 client_fd...
}
2. backlog 的合理取值
经验值:通常设为 SOMAXCONN(系统定义的最大值,如 Linux 默认 4096)。
调整方法(Linux):# 临时修改 somaxconn
echo 4096 > /proc/sys/net/core/somaxconn# 永久修改(需编辑 /etc/sysctl.conf)
net.core.somaxconn = 4096
- 注意:实际允许的连接数受系统资源和并发模型(如多线程、epoll)影响。
4、accept函数
函数原型
#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数详解
| 参数 | 类型 | 描述 |
|---|---|---|
|
|
| 处于监听状态的套接字描述符(由 |
|
|
| 输出参数,用于存储客户端地址信息(如 IP 和端口)。若不需要可设为 |
|
|
| 输入输出参数:传入 |
返回值
-
成功:返回一个新的已连接套接字描述符(非负整数),专门用于与客户端通信。
-
失败:返回
-1,并设置errno(如EINTR、ECONNABORTED)。
核心作用
-
提取连接:从监听套接字的已完成连接队列(已完成三次握手)中取出一个客户端连接。
-
生成新套接字:返回的已连接套接字与客户端一一对应,原监听套接字继续接受其他连接。
-
获取客户端地址:通过
addr参数获取客户端的 IP 地址和端口(可选)。
5、connect函数
函数原型
#include <sys/types.h>
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数详解
| 参数 | 类型 | 描述 |
|---|---|---|
sockfd | int | 客户端套接字描述符(由 socket() 创建)。 |
addr | struct sockaddr* | 指向服务端地址结构体的指针(如 sockaddr_in)。 |
addrlen | socklen_t | 地址结构体的长度(单位:字节)。 |
返回值
- 成功:返回
0,套接字进入已连接状态(TCP)或设置默认地址(UDP)。 - 失败:返回
-1,并设置errno表示错误原因。
相关文章:
十七、TCP编程
TCP 编程是网络通信的核心,其 API 围绕面向连接的特性设计,涵盖服务端和客户端的交互流程。以下是基于 C 语言的 TCP 编程核心 API 及使用流程的详细解析: 核心 API 概览 函数角色描述socket()通用创建套接字,指定协议族…...
DeepSeek vs Grok vs ChatGPT:三大AI工具优缺点深度解析
一、DeepSeek:低成本与中文专精的本地化AI 优点 中文处理能力卓越 DeepSeek针对中文语法和文化背景进行了深度优化,尤其在古文翻译、诗歌创作和技术文档生成中表现突出,远超ChatGPT的中文支持能力。高效推理与低成本 采用混合专家ÿ…...
微信小程序中的openid的作用
微信小程序中的openid的作用 引言 在当今数字化时代,用户体验成为了产品成功与否的关键因素之一。微信小程序作为连接用户与服务的重要桥梁,在提升用户体验方面发挥着重要作用。其中, openid(开放身份标识符)是微信小…...
spring--声明式事务
声明式事务 1、回顾事务 要么都成功,要么都失败! 事务在项目开发中,十分重要,涉及数据的一致性问题 确保完整性和一致性 事务ACID: 原子性:事务是原子性操作,由一系列动作组成,…...
小甲鱼第004讲:变量和字符串(下)| 课后测试题及答案
问答题: 0. 请问下面代码有没有毛病,为什么? 请问下面代码为什么会出错,应该如何解决? 答:这是由于在字符串中,反斜杠()会与其随后的字符共同构成转义字符。 为了避免这种不测情况的发生,我们可以在字符串的引号前面…...
MergeX亮相GTC2025:开启全球广告流量交易新篇章
全球流量盛宴GTC2025深圳启幕,共探出海新蓝海 2025年4月24日至25日,GTC2025全球流量大会将在深圳福田会展中心9号馆隆重召开。作为跨境出海领域内规模最大、资源最丰富、产业链最完备的年度盛会,此次大会将汇聚众多行业精英,共同探…...
Python(10.2)Python可变与不可变类型内存机制解密:从底层原理到工程实践
目录 一、类型特性引发的内存现象1.1 电商促销活动事故分析1.2 内存机制核心差异 二、内存地址追踪实验2.1 基础类型验证2.2 复合对象实验 三、深度拷贝内存分析3.1 浅拷贝陷阱3.2 深拷贝实现 四、函数参数传递机制4.1 默认参数陷阱4.2 安全参数模式 五、内存优化最佳实践5.1 字…...
STM32(基于标准库)
参考博客:江科大STM32笔记 Stm32外设 一、GPIO 基础 GPIO位结构 I/O引脚的保护二极管是对输入电压进行限幅的上面的二极管接VDD, 3.3V,下面接VSS, 0V,当输入电压 >3.3V 那上方这个二极管就会导通,输入电压产生的电流就会大部分充入VD…...
国家优青ppt美化_青年科学基金项目B类ppt案例模板
国家优青 国家优青,全称“国家优秀青年基金获得者”。2025改名青年科学基金B类。 作为自然基金人才资助类型,支持青年学者在基础研究方面自主选择研究方向开展创新研究。它是通往更高层次科研荣誉的重要阶梯,是准杰青梯队。 / WordinPPT /…...
解决 ECharts 图表无数据显示问题
问题: 在开发项目时,后端明明已经成功返回了数据,但在展示手账发布数量趋势和树洞帖子发布数量趋势的 ECharts 图表中,却只有坐标轴,没有任何数据显示。 以我的VUE项目开发可视化面板为例,下面将详细分析可…...
spacy安装失败报错
报错 使用命令pip install spacy安装spacy时总是报错(python -m pip install spacy方式安装同样报错) 解决办法 使用conda安装,conda能够避免很多不必要的依赖包。 命令:conda install spacy 安装成功列表展示...
C++在Linux上生成动态库并调用接口测试
加减乘除demo代码 项目结构 CPP/ ├── calculator.cpp ├── calculator.h ├── main.cpp 头文件 #ifndef CALCULATOR_H #define CALCULATOR_H#ifdef __cplusplus extern "C" {#endifdouble add(double a, double b);double subtract(double a, double b…...
第三篇:Python数据结构深度解析与工程实践
第一章:列表与字典 1.1 列表的工程级应用 1.1.1 动态数组实现机制 Python列表底层采用动态数组结构,初始分配8个元素空间,当空间不足时按0,4,8,16,25,35...的公式扩容,每次扩容增加约12.5%的容量 通过sys模块可验证扩容过程: import sys lst = [] prev_size = 0 for …...
前端性能测试工具 —— WebPageTest
测试工具介绍 WebPageTest 是一个用于测量和分析网页性能的工具。它提供了详细的诊断信息,帮助用户了解网页在不同条件下的表现。用户可以选择全球不同的测试地点,使用真实的浏览器,并自定义网络条件进行测试。WebPageTest 还支持核心网络指…...
北邮LLMs在导航中的应用与挑战!大模型在具身导航中的应用进展综述
作者:Jinzhou Lin, Han Gao, Xuxiang Feng, Rongtao Xu, Changwei Wang, Man Zhang, Li Guo, Shibiao Xu 单位:北京邮电大学人工智能学院,中国科学院自动化研究所多模态人工智能系统国家重点实验室,中科院空间信息研究所…...
Windows下ElasticSearch8.x的安装步骤
下载ElasticSearch:https://www.elastic.co/downloads/elasticsearch (我下载的是目前最新版8.17.4)解压ElasticSearch 进入到ElasticSearch的bin目录下双击elasticsearch.bat 弹出控制台并开始执行,在这一步会输出初始账号和密码…...
【高性能缓存Redis_中间件】一、快速上手redis缓存中间件
一、铺垫 在当今的软件开发领域,消息队列扮演着至关重要的角色。它能够帮助我们实现系统的异步处理、流量削峰以及系统解耦等功能,从而提升系统的性能和可维护性。Redis 作为一款高性能的键值对数据库,不仅提供了丰富的数据结构,…...
AI Agent入门指南
图片来源网络 一、开箱暴击:你以为的"智障音箱",其实是赛博世界的007 1.1 从人工智障到智能叛逃:Agent进化史堪比《甄嬛传》 青铜时代(2006-2015) “小娜同学,关灯” “抱歉&…...
React 第三十节 使用 useState 和 useEffect Hook实现购物车
不使用 redux 实现 购物车案例 使用 React 自带的 useState 和 useEffect Hook 即可实现购物车 export default function ShoppingCar() {// 要结算的商品 总数 以及总价const [totalNum, setTotalNum] useState(0)const [totalPerice, setTotalPerice] useState(0)// 商品…...
Git的简介和简单的命令使用介绍
Git 是一种分布式版本控制系统,常用于跟踪文件的变化,协作开发和管理代码版本。以下是 Git 的基本概念和使用方式: 仓库(Repository):Git 仓库是用来存储项目文件和版本历史的地方。可以通过在本地或远程创…...
概念辨析:Redis 多路 I/O 复用和多线程
Redis 多路 I/O 复用是在 Redis 2.0 引入的,而 Redis 多线程是在 Redis 6.0 引入的,两者不是同一个概念。 多路复用的本质还是同步 I/O,因为最终都需要主线程调用 read() 方法把数据拷贝到用户态。 在并发量非常大的情况下,Redi…...
海洋大地测量基准与水下导航系列之八我国海洋水下定位装备发展现状
中国国家综合PNT体系建设重点可概括为“51N”,“5”指5大基础设施,包括重点推进下一代北斗卫星导航系统、积极发展低轨导航增强系统、按需发展水下导航系统、大力发展惯性导航系统、积极探索脉冲星导航系统;“1”是实现1个融合发展࿰…...
计算机系统设计中的一些常用方法
面试中经常被问到: 有一个亿qq号,找出重复的 给你512m内存,找出5g文件中最大的数字 订单超时实现精准关单 … 当然,还有经常遇到的问题: 接口业务逻辑复杂或查数据库慢,相应耗时高 网络因为丢包导致服…...
【征程 6】工具链 VP 示例中 Cmakelists 解读
1. 引言 在文章【征程 6】VP 简介与单算子实操中,介绍了 VP 是什么,并以单算子 rotate 为例,介绍了 VP API 使用方法。在【征程 6】工具链 VP 示例中日志打印解读 中介绍了 VP 单算子示例中用到的日志打印的头文件应该怎么写。接下来和大家一…...
深入解析 Jenkins Agent 的 .jnlp 启动文件
🧩 深入解析 Jenkins Agent 的 .jnlp 启动文件 在 Jenkins 中,通过 JNLP(Java Network Launch Protocol)方式连接 Agent 是一种常见且灵活的方式。你可能曾见过类似这样的命令: java -jar agent.jar -jnlpUrl file:/…...
谷歌开源代理开发工具包(Agent Development Kit,ADK):让多智能体应用的构建变得更简
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
0x01、Redis 主从复制的实现原理是什么?
Redis 主从复制概述 Redis 的主从复制是一种机制,允许一个主节点(主实例)将数据复制到一个或多个从节点(从实例)。通过这一机制,从节点可以获取主节点的数据并与之保持同步。 复制流程 开始同步…...
noscript 标签是干什么的
vue public目录下的 index.html 会有 <noscript> 标签不知道是干吗的。 其实 noscript 标签在不支持或是禁用JavaScript 的浏览器中显示替代的内容。这个元素可以包含任何 HTML 元素。这个标签的用法也非常简单: <noscript><strong>Were sorry …...
[创业之路-366]:投资尽职调查 - 尽调核心逻辑与核心影响因素:价值、估值、退出、风险、策略
目录 一、VC投资的本质是冒着不确定性风险进行买卖、生意,为了赚取高额回报 1、VC投资的核心本质 2、VC投资的运作机制 3、VC投资的风险与挑战 4、VC投资的底层逻辑 5、总结:VC投资的本质再定义 二、尽调核心逻辑 1、尽调的含义 2、尽调的逻辑方…...
MOP数据库中的EXPLAIN用法
EXPLAIN 是 SQL 中的一个非常有用的工具,主要用于分析查询语句的执行计划。执行计划能展示数据库在执行查询时的具体操作步骤,像表的读取顺序、使用的索引情况、数据的访问方式等,这有助于我们对查询性能进行优化。 语法 不同的数据库系统&…...
