十七、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_INETIPv4 协议(最常用)
AF_INET6IPv6 协议
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,表示根据 domaintype ​自动选择默认协议。例如:

  • SOCK_STREAM 默认使用 IPPROTO_TCP
  • SOCK_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);

参数

​参数​类型​描述
sockfdint已创建的套接字描述符(由socket()返回)
addrstruct sockaddr*指向保存绑定地址信息的结构体指针(需根据协议族填充对应的结构类型)
addrlensocklen_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);

  参数详解

参数类型描述
sockfdint已通过 bind() 绑定地址的套接字描述符(必须是 ​SOCK_STREAM 类型)。
backlogint已建立连接(完成三次握手)的队列最大长度,决定同时等待 accept() 处理的连接数。

  核心作用

  1. ​转换套接字状态:

    • 将套接字从主动模式​(默认)转为被动模式,使其能够接收客户端的连接请求。
    • 未调用 listen() 的套接字无法调用 accept()
  2. ​管理连接队列:

    • 内核为监听套接字维护两个队列​(具体实现可能因操作系统而异):
      • ​未完成队列(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);

  参数详解

​参数

​类型

​描述

sockfd

int

处于监听状态的套接字描述符(由 listen() 设置)。

addr

struct sockaddr*

输出参数,用于存储客户端地址信息(如 IP 和端口)。若不需要可设为 NULL

addrlen

socklen_t*

输入输出参数:传入 addr 缓冲区的长度,返回实际写入的地址长度。若 addrNULL,可设为 NULL


  返回值

  • ​成功:返回一个新的已连接套接字描述符​(非负整数),专门用于与客户端通信。

  • ​失败:返回 -1,并设置 errno(如 EINTRECONNABORTED)。


  核心作用

  1. ​提取连接:从监听套接字的已完成连接队列​(已完成三次握手)中取出一个客户端连接。

  2. ​生成新套接字:返回的已连接套接字与客户端一一对应,原监听套接字继续接受其他连接。

  3. ​获取客户端地址:通过 addr 参数获取客户端的 IP 地址和端口(可选)。

5、connect函数

函数原型
#include <sys/types.h>
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  参数详解

参数类型描述
sockfdint客户端套接字描述符(由 socket() 创建)。
addrstruct sockaddr*指向服务端地址结构体的指针(如 sockaddr_in)。
addrlensocklen_t地址结构体的长度(单位:字节)。

  返回值

  • 成功:返回 0,套接字进入已连接状态(TCP)或设置默认地址(UDP)。
  • 失败:返回 -1,并设置 errno 表示错误原因。

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

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

相关文章

将外网下载的 Docker 镜像拷贝到内网运行

将外网下载的 Docker 镜像拷贝到内网运行&#xff0c;可以通过以下步骤实现&#xff1a; 一、在有外网访问权限的机器上操作 下载镜像 使用docker pull命令下载所需的镜像。例如&#xff0c;如果你需要下载一个名为nginx的镜像&#xff0c;可以运行以下命令&#xff1a;docke…

《深入理解生命周期与作用域:以C语言为例》

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言 &#x1f30d;文章目入 一、生命周期&#xff1a;变量的存在时间&#xff08;一&#xff09;生命周期的定义&#xff08;二&#xff09;C语言中的生命周期类型&#xff08;三&#…

Hqst的超薄千兆变压器HM82409S在Unitree宇树Go2智能机器狗的应用

本期拆解带来的是宇树科技推出的Go2智能机器狗&#xff0c;这款机器狗采用狗身体形态&#xff0c;前端设有激光雷达&#xff0c;摄像头和照明灯。在腿部设有12个铝合金精密关节电机&#xff0c;并配有足端力传感器&#xff0c;通过关节运动模拟狗的运动&#xff0c;并可做出多种…

壹起航:15年深耕,引领中国工厂出海新征程

在全球化浪潮汹涌澎湃的当下&#xff0c;中国工厂正以前所未有的热情和决心&#xff0c;将目光投向广阔的海外市场。然而&#xff0c;出海之路并非一帆风顺&#xff0c;建立品牌、获取稳定询盘、降低营销成本等难题&#xff0c;如同横亘在企业面前的高山&#xff0c;阻碍着他们…

【差分隐私相关概念】基础合成定理和高级合成技术简单关系

差分隐私中的合成定理用于分析多个机制组合时的隐私损失。基础合成定理和高级合成技术分别在不同场景下提供了隐私预算增长的估计&#xff0c;其关系如下&#xff1a; 基础合成定理&#xff08;线性增长&#xff09; 机制组合&#xff1a;当k个满足(ε, δ)-DP的机制按顺序组…

【异常处理】Clion IDE中cmake时头文件找不到 头文件飘红

如图所示是我的clion项目目录 我自定义的data_structure.h和func_declaration.h在unit_test.c中无法检索到 cmakelists.txt配置文件如下所示&#xff1a; cmake_minimum_required(VERSION 3.30) project(noc C) #设置头文件的目录 include_directories(${CMAKE_SOURCE_DIR}/…

MOS的驱动电流怎么计算?

一、MOS 驱动电流的计算方法 MOS 管在开关时&#xff0c;驱动电路主要是给栅极充放电。栅极电流 不是用来维持电流&#xff0c;而是用来克服电容的充放电需求&#xff0c;尤其是总栅极电荷 Qg。 驱动电流估算公式如下&#xff1a; I_drive Qg f_sw&#xff08;Qg&#xff…

GGML源码逐行调试(下)

目录 前言1. 简述2. 预分配计算图内存2.1 创建图内存分配器2.2 构建最坏情况的计算图2.3 预留计算图内存 3. 分词4. 模型推理与生成4.1 模型推理4.2 采样 结语下载链接参考 前言 学习 UP 主 比飞鸟贵重的多_HKL 的 GGML源码逐行调试 视频&#xff0c;记录下个人学习笔记&#x…

1.5-APP的架构\微信小程序的架构

1.5-APP的架构\微信小程序的架构 APP的三种开发架构&#xff1a; 原生态APP类型 APP-开发架构-原生态-IDEA 演示&#xff1a;remusic项目源码 NP管理器&#xff1a; http://normalplayer.top/ HttpCanary&#xff1a;https://github.com/mingww64/HttpCanary-SSL-Magisk 安全影…

用css画一条弧线

ui里有一条弧线&#xff0c;现在用css实现 关键代码 border-bottom-left-radius: 100% 7px 两个参数分别代表横向和纵向的深度border-bottom-right-radius: 100% 7px

MSCKF及可观性总结

可观性 参考链接 真实VIO系统不能观的维度是4&#xff08;位置和yaw角&#xff09;&#xff0c;由于EKF的转移和观测Jacobian矩阵的线性化点不同、不可观方向噪声的存在&#xff0c;实际MSCKF不能观的维度变成了3&#xff0c;绕重力轴的旋转&#xff08;yaw角&#xff09;被错…

【Hotspot虚拟机创建对象的过程是什么样的?】

1. 类加载检查 触发条件&#xff1a;当遇到 new 指令时&#xff0c;JVM首先检查该指令的参数&#xff08;类符号引用&#xff09;是否已在常量池中。检查内容&#xff1a; 类是否已被加载、解析和初始化。若未加载&#xff0c;则触发类加载过程&#xff08;加载 → 验证 → 准…

南墙WAF非标端口防护实战解析——指定端口安全策略深度剖析

本文系统解析非标端口DDoS攻击防护难点&#xff0c;重点阐述南墙WAF在指定端口防御中的技术突破。通过某金融机构真实攻防案例&#xff0c;结合Gartner最新防御架构模型&#xff0c;揭示如何构建基于智能流量建模的精准防护体系&#xff0c;为金融、政务等关键领域提供可落地的…

Context的全面解析:在不同技术应用中的通用作用与差异

Context的全面解析&#xff1a;在不同技术应用中的通用作用与差异 引言&#xff1a; 在软件开发中&#xff0c;“Context”这个概念被广泛使用。它不仅限于某个特定的技术或编程语言&#xff0c;实际上&#xff0c;Context 作为一种抽象的设计模式&#xff0c;贯穿在许多开发领…

寻找峰值 --- 二分查找

目录 一&#xff1a;题目 二&#xff1a;算法原理 三&#xff1a;代码实现 一&#xff1a;题目 题目链接&#xff1a;162. 寻找峰值 - 力扣&#xff08;LeetCode&#xff09; 二&#xff1a;算法原理 三&#xff1a;代码实现 class Solution { public:int findPeakElemen…

基础算法训练7

目录 库存管理II 翻转对 合并K个升序链表 存在重复元素II 字符串相乘 字符串解码 在每个树行中找最大值 数据流的中位数 被包围的区域 为高尔夫比赛砍树 库存管理II LCR 159. 库存管理 III - 力扣&#xff08;LeetCode&#xff09; 解法一&#xff1a;先进行排序&a…

从单机版到超级APP:MCP如何解锁AI的超能力

MCP&#xff1a;AI界的“万能充电宝”——让AI从此告别“语言不通”的尴尬&#xff01; 开篇&#xff1a;AI咖啡馆的尴尬日常 想象一下这样的场景&#xff1a; 一位AI助手在咖啡馆里手忙脚乱——它想帮用户点杯咖啡&#xff0c;但需要先写代码调用天气API&#xff08;“今天下…

Grafana将弃用AngularJS-我们该如何迁移

AngularJS 弃用时间线 AngularJS 支持已在 Grafana 9 中正式弃用。在 2024 年 5 月发布的 Grafana 11 中&#xff0c;所有 Grafana Cloud 和自托管安装默认关闭该功能。到 Grafana 12 版本时&#xff0c;将完全移除对 AngularJS 的支持&#xff0c;包括配置参数开关 angular_s…

Qt之opengl定点数据添加更多属性

将颜色数据加入到定点数据中去 shader中代码 api中的代码 #include "sunopengl.h"#include <QTime>sunOpengl::sunOpengl(QWidget *parent) { } unsigned int VBO,VAO; float vertices[]{0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.0f, 0.0f, 1.0f…

【Flink运行时架构】作业提交流程

本文介绍在单作业模式下Flink提交作业的具体流程&#xff0c;如下图所示。 客户端将作业提交给YARN的RM&#xff1b;YARN的RM启动Flink JobManager&#xff0c;并将作业提交给JobMaster&#xff1b;JobMaster向Flink内置的RM请求slots&#xff1b;Flink内置的RM向YARN RM请求…