从零到一:TCP 回声服务器与客户端的完整实现与原理详解

目录

一、TCP 通信的核心逻辑

二、TCP 服务器编程步骤

步骤 1:创建监听 Socket

步骤 2:绑定地址与端口(bind)

步骤 3:设置监听状态(listen)

步骤 4:接收客户端连接(accept)

步骤 5:与客户端交互(read/write)

步骤 6:关闭连接(close)

步骤 7:并发处理(可选但重要)

三、TCP 客户端编程步骤

步骤 1:创建客户端 Socket

步骤 2:连接服务器(connect)

步骤 3:与服务器交互(read/write)

步骤 4:关闭连接

四、核心代码解析

1. 辅助工具:InetAddr 类(网络地址处理)

2. 服务器端实现:TcpServer 类

(1)初始化服务器:socket→bind→listen

(2)接收连接与处理请求:accept→read/write

3. 客户端实现:TcpClient

4. 编译脚本:Makefile

五、运行演示

步骤 1:编译程序

步骤 2:启动服务器

步骤 3:启动客户端(新终端)

步骤 4:交互测试

六、常见问题与解决方案

七、扩展与进阶方向

八、总结


在网络编程的学习旅程中,TCP 协议是绕不开的核心内容。它作为一种面向连接的可靠传输协议,支撑着互联网中绝大多数的应用通信。本文将结合一套完整的 C++ 实现代码,从基本原理到具体实践,带你掌握 TCP 编程的全流程 —— 从 socket 创建到多进程并发处理,最终实现一个可交互的 "回声" 程序。

一、TCP 通信的核心逻辑

TCP(Transmission Control Protocol)的通信模型遵循固定的 "连接 - 传输 - 断开" 流程,核心特点是面向连接可靠传输

  • 角色划分:通信双方分为服务器(被动等待连接)和客户端(主动发起连接)
  • 连接建立:通过 "三次握手" 建立可靠连接,确保双方都做好通信准备
  • 数据传输:基于字节流的方式传输数据,通过确认机制保证数据不丢失、不重复
  • 连接关闭:通过 "四次挥手" 优雅关闭连接,确保双方数据都已传输完成

本次实现的 "回声程序" 是 TCP 编程的经典入门案例:客户端发送任意字符串,服务器接收后添加 "server echo#" 前缀返回,直观展示完整通信流程。

二、TCP 服务器编程步骤

服务器的核心功能是 "监听连接→接收请求→处理交互",完整步骤如下:

步骤 1:创建监听 Socket

Socket(套接字)是网络通信的 "门户",本质是操作系统提供的网络通信接口(文件描述符)。

// 创建TCP监听Socket
int listensockfd = socket(AF_INET, SOCK_STREAM, 0);
if (listensockfd < 0) {// 错误处理:创建失败(如协议不支持)perror("socket error");exit(1);
}

参数解析

  • AF_INET:使用 IPv4 地址族(互联网最常用)
  • SOCK_STREAM:指定为流式套接字(TCP 协议的特征)
  • 0:自动选择对应的数据传输协议(此处为 TCP)

步骤 2:绑定地址与端口(bind)

创建 Socket 后,需要将其与本机的具体 IP 和端口绑定,明确 "监听哪个地址的请求"。

// 准备地址结构(网络字节序)
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;                  // IPv4
local_addr.sin_port = htons(8081);                // 端口(主机字节序→网络字节序)
local_addr.sin_addr.s_addr = INADDR_ANY;          // 绑定所有本地IP(多网卡场景适用)// 绑定操作
int ret = bind(listensockfd, (struct sockaddr*)&local_addr, sizeof(local_addr));
if (ret < 0) {perror("bind error");exit(2);
}

关键细节

  • 网络字节序:TCP 规定网络中数据必须使用大端字节序,htons()(host to network short)用于端口转换
  • INADDR_ANY:表示绑定本机所有可用 IP(无需手动指定具体 IP,灵活适配多网卡环境)

步骤 3:设置监听状态(listen)

绑定完成后,需将 Socket 转为 "监听状态",准备接收客户端的连接请求。

// 开始监听(BACKLOG=8:未完成连接队列的最大长度)
int ret = listen(listensockfd, 8);
if (ret < 0) {perror("listen error");exit(3);
}

BACKLOG 参数:限制正在进行三次握手(未完成连接)的最大数量,超过此值的新连接会被暂时拒绝。

步骤 4:接收客户端连接(accept)

监听状态的 Socket 可以通过accept()函数阻塞等待并接收客户端连接。

struct sockaddr_in client_addr;                  // 存储客户端地址
socklen_t client_addr_len = sizeof(client_addr);// 阻塞等待新连接,返回与该客户端通信的Socket
int clientsockfd = accept(listensockfd, (struct sockaddr*)&client_addr, &client_addr_len);
if (clientsockfd < 0) {perror("accept error");continue;  // 忽略错误,继续等待下一个连接
}

核心特性

  • accept()是阻塞函数,若无新连接则一直等待
  • 成功返回新的 Socket 描述符(专门用于与当前客户端通信)
  • 原监听 Socket(listensockfd)继续用于接收其他连接

步骤 5:与客户端交互(read/write)

连接建立后,通过read()write()实现数据收发。

char buffer[4096];
while (true) {// 读取客户端数据ssize_t n = read(clientsockfd, buffer, sizeof(buffer) - 1);if (n > 0) {  // 读取成功buffer[n] = '\0';  // 手动添加字符串结束符// 处理数据(示例:添加前缀后回送)std::string response = "server: " + std::string(buffer);write(clientsockfd, response.c_str(), response.size());}else if (n == 0) {  // 客户端主动关闭连接std::cout << "client closed" << std::endl;break;}else {  // 读取错误(如网络异常)perror("read error");break;}
}

注意事项

  • read()返回值需严格判断:正数为实际读取字节数,0 表示对方关闭,负数表示错误
  • 避免假设 " 一次read()能获取完整数据 "(TCP 是流式协议,数据可能分多次到达)

步骤 6:关闭连接(close)

交互结束后,关闭 Socket 释放资源:

close(clientsockfd);  // 关闭与客户端的连接Socket
// 服务器退出时关闭监听Socket
// close(listensockfd);

步骤 7:并发处理(可选但重要)

单进程服务器一次只能处理一个客户端,实际应用中需支持并发,常用方案:

  • 多进程:通过fork()创建子进程处理每个连接(隔离性好,资源消耗高)
  • 多线程:通过pthread_create()创建线程(资源消耗低,需处理同步)
  • IO 多路复用:用select/epoll(Linux)实现单进程处理多连接(高性能)

三、TCP 客户端编程步骤

客户端流程相对简单,核心是 "连接服务器→交互数据":

步骤 1:创建客户端 Socket

与服务器类似,客户端也需要创建 Socket:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {perror("socket error");exit(1);
}

步骤 2:连接服务器(connect)

客户端通过connect()向服务器发起连接请求(触发三次握手)。

struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8081);                  // 服务器端口
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IPint ret = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (ret < 0) {perror("connect error");exit(1);
}

特点connect()是阻塞函数,直到连接建立或失败才返回。

步骤 3:与服务器交互(read/write)

连接成功后,通过read()/write()与服务器通信:

std::string message;
char buffer[1024];
while (true) {// 输入要发送的数据std::cout << "input message: ";getline(std::cin, message);// 发送数据ssize_t n = write(sockfd, message.c_str(), message.size());if (n <= 0) break;// 接收服务器响应int m = read(sockfd, buffer, sizeof(buffer));if (m > 0) {buffer[m] = '\0';std::cout << "server response: " << buffer << std::endl;} else break;
}

步骤 4:关闭连接

close(sockfd);

四、核心代码解析

1. 辅助工具:InetAddr 类(网络地址处理)

网络编程中,IP 地址和端口需要在 "网络字节序"(大端)和 "主机字节序"(可能为小端)之间转换,InetAddr类封装了这一高频操作:

#pragma once
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define CONV(v) (struct sockaddr *)(v)class InetAddr {
private:struct sockaddr_in _net_addr;  // 网络字节序的地址结构std::string _ip;               // 主机字节序的IP字符串uint16_t _port;                // 主机字节序的端口号// 端口从网络字节序转主机字节序void PortNet2Host() { _port = ::ntohs(_net_addr.sin_port); }// IP从网络字节序转主机字节序(点分十进制字符串)void IpNet2Host() {char ipbuffer[64];::inet_ntop(AF_INET, &_net_addr.sin_addr, ipbuffer, sizeof(ipbuffer));_ip = ipbuffer;}public:// 从sockaddr_in初始化(接收客户端连接时使用)InetAddr(const struct sockaddr_in &addr) : _net_addr(addr) {PortNet2Host();IpNet2Host();}// 获取IP:Port格式字符串(如127.0.0.1:8081)std::string Addr() { return Ip() + ":" + std::to_string(Port()); }// 其他实用接口std::string Ip() { return _ip; }uint16_t Port() { return _port; }struct sockaddr *NetAddr() { return CONV(&_net_addr); }
};

核心作用:自动完成地址转换,让业务代码更简洁,避免重复处理字节序问题。

2. 服务器端实现:TcpServer 类

服务器的核心工作是 "监听连接→接收请求→处理请求",TcpServer类封装了完整流程:

(1)初始化服务器:socket→bind→listen
class TcpServer {
private:uint16_t _port;         // 端口号bool _running;          // 运行状态标识int _listensockfd;      // 监听socket描述符public:TcpServer(int port = 8081) : _port(port), _running(false) {}void InitServer() {// 1. 创建监听socket(AF_INET:IPv4,SOCK_STREAM:TCP)_listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0) {std::cout << "socket error" << std::endl;exit(1);}// 2. 绑定地址(IP+端口)struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;         // IPv4协议local.sin_port = htons(_port);      // 端口转网络字节序local.sin_addr.s_addr = INADDR_ANY; // 绑定所有本地IP(多网卡场景适用)int n = bind(_listensockfd, CONV(&local), sizeof(local));if (n < 0) {std::cout << "bind error" << std::endl;exit(2);}// 3. 开始监听(BACKLOG=8:未完成连接队列最大长度)n = listen(_listensockfd, 8);if (n < 0) {std::cout << "listen error" << std::endl;exit(3);}}
};

关键函数解析

  • socket():创建用于网络通信的文件描述符(类似文件句柄),参数指定协议族(IPv4)和协议类型(TCP)
  • bind():将 socket 与特定地址绑定,INADDR_ANY表示监听本机所有 IP
  • listen():将 socket 转为监听状态,BACKLOG限制同时建立连接的最大数量
(2)接收连接与处理请求:accept→read/write
class TcpServer {// ... 省略前面代码 ...public:void Start() {_running = true;while (_running) {// 接收客户端连接(阻塞等待新连接)struct sockaddr_in peer;socklen_t peerlen = sizeof(peer);int sockfd = accept(_listensockfd, CONV(&peer), &peerlen);if (sockfd < 0) {std::cout << "accept error" << std::endl;continue;}// 打印客户端地址InetAddr addr(peer);std::cout << "client into: " << addr.Addr() << std::endl;// 多进程处理并发(核心)pid_t id = fork();if (id == 0) {  // 子进程close(_listensockfd);  // 子进程不需要监听socket// 二次fork:避免子进程成为僵尸进程(让孙子进程被系统收养)if (fork() > 0) exit(0);HandlerRequest(sockfd);  // 处理当前客户端请求exit(0);}close(sockfd);  // 父进程关闭连接socketwaitpid(id, NULL, 0);  // 回收子进程资源}}// 处理客户端请求(回声逻辑)void HandlerRequest(int sockfd) {char inbuffer[4096];while (true) {// 读取客户端数据ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);if (n > 0) {  // 读取成功inbuffer[n] = 0;  // 手动添加字符串结束符std::string echo_str = "server echo#" + std::string(inbuffer);write(sockfd, echo_str.c_str(), echo_str.size());  // 回送数据std::cout << "server echo: " << inbuffer << std::endl;}else if (n == 0) {  // 客户端关闭连接std::cout << "client closed: " << sockfd << std::endl;break;}else {  // 读取错误break;}}close(sockfd);  // 关闭连接}
};

核心逻辑说明

  • accept():阻塞等待客户端连接,返回新的 socket 描述符(专门用于与该客户端通信)
  • 多进程并发:通过fork()创建子进程处理每个客户端,父进程继续接收新连接;二次fork()避免僵尸进程(子进程退出后由系统回收)
  • 数据处理:read()读取客户端数据,write()回送带前缀的回声,通过返回值判断通信状态(成功 / 关闭 / 错误)

3. 客户端实现:TcpClient

客户端流程相对简单,核心是 "创建 socket→连接服务器→收发数据":

#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>int main(int argc, char *argv[]) {// 解析命令行参数(服务器IP和端口)if (argc != 3) {std::cout << "Usage:./TcpClient <server_ip> <port>" << std::endl;return 1;}std::string server_ip = argv[1];int server_port = std::stoi(argv[2]);// 1. 创建客户端socketint sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {std::cout << "Error in creating socket" << std::endl;return 1;}// 2. 连接服务器(触发三次握手)struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(server_port);  // 端口转网络字节序server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());  // IP转网络字节序int n = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (n < 0) {std::cout << "Error in connecting to server" << std::endl;return 1;}// 3. 循环发送数据并接收回声std::string message;while (true) {char inbuffer[1024];std::cout << "input message to send to server: ";getline(std::cin, message);// 发送数据到服务器n = write(sockfd, message.c_str(), message.size());if (n <= 0) break;// 接收服务器回声int m = read(sockfd, inbuffer, 1024);if (m > 0) {inbuffer[m] = '\0';std::cout << "Server response: " << inbuffer << std::endl;} else break;}close(sockfd);  // 关闭连接return 0;
}

关键函数connect()会触发 TCP 三次握手,阻塞直到连接建立或失败;成功后通过read()/write()与服务器交互。

4. 编译脚本:Makefile

为简化编译流程,使用 Makefile 一键生成服务器和客户端可执行文件:

.PHONY:all
all:server_tcp client_tcp  # 目标:服务器和客户端# 编译服务器(依赖TcpServer.cc,链接pthread库)
server_tcp:TcpServer.ccg++ -o $@ $^ -std=c++17 -lpthread# 编译客户端(依赖TcpClient.cc)
client_tcp:TcpClient.ccg++ -o $@ $^ -std=c++17 -lpthread.PHONY:clean
clean:  # 清理生成的文件rm -f client_tcp server_tcp

五、运行演示

步骤 1:编译程序

make  # 生成server_tcp(服务器)和client_tcp(客户端)

步骤 2:启动服务器

./server_tcp  # 默认监听8081端口

步骤 3:启动客户端(新终端)

./client_tcp 127.0.0.1 8081  # 连接本地服务器(127.0.0.1为本地回环地址)

步骤 4:交互测试

在客户端输入任意内容(如 "hello tcp"),会收到服务器返回的 "server echo#hello tcp";服务器终端会同步打印接收的消息,效果如下:

# 客户端终端
input message to send to server: hello tcp
Server response: server echo#hello tcp# 服务器终端
client into: 127.0.0.1:54321  # 客户端端口为随机分配
server echo: hello tcp

六、常见问题与解决方案

  1. 地址已在使用(Address already in use)

    • 原因:服务器关闭后,端口会进入 TIME_WAIT 状态(默认保留 2MSL 时间),短期内无法重用
    • 解决:创建 socket 后设置 SO_REUSEADDR 选项,允许端口重用:
    int opt = 1;
    setsockopt(_listensockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    
  2. 僵尸进程问题

    • 原因:子进程退出后,父进程未及时回收其资源,导致进程残留
    • 解决:除了代码中的二次fork(),还可注册 SIGCHLD 信号处理函数,自动回收子进程:
    signal(SIGCHLD, SIG_IGN);  // 忽略SIGCHLD信号,系统自动回收子进程
    
  3. 粘包问题

    • 原因:TCP 是流式协议,数据无边界,多次发送的小数据可能被合并传输
    • 解决:定义应用层协议(如 "数据长度 + 实际数据" 格式),确保接收方正确拆分数据。

七、扩展与进阶方向

  1. 错误处理增强:当前用cout输出错误,可改用perror()结合errno打印更详细的错误原因(如 "bind error: Address already in use")。

  2. 线程池替代多进程:多进程资源消耗高,可改用线程池(提前创建固定数量的线程),减少动态创建销毁的开销。

  3. 配置化参数:将端口、BACKLOG 等参数通过命令行或配置文件传入,避免硬编码(如./server_tcp -p 8080 -b 16)。

  4. 功能扩展:基于现有框架实现文件传输(分块发送文件内容)、多客户端群聊(服务器转发消息)等功能。

  5. IO 多路复用:使用select/poll/epoll(Linux)实现单进程处理多连接,大幅提升并发性能(适用于高并发场景)。

八、总结

本文通过一个完整的 TCP 回声程序,展示了网络编程的核心流程:从socket创建、bind绑定、listen监听,到accept接收连接、read/write收发数据,再到多进程并发处理。这些基础操作是理解 HTTP 服务器、RPC 框架等复杂网络应用的基石。

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

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

相关文章

MyBatis-Plus核心内容

MyBatis-Plus MyBatis-Plus 是一个基于 MyBatis的增强工具&#xff0c;旨在简化开发过程&#xff0c;减少重复代码。它在MyBatis的基础上增加了CRUD操作封装&#xff0c;条件构造器、代码生成器等功能。 一、核心特性与优势 1. 核心特性 无侵入&#xff1a;只做增强不做改变&am…

计算机网络摘星题库800题笔记 第4章 网络层

第4章 网络层4.1 网络层概述题组闯关1.在 Windows 的网络配置中&#xff0c;“默认网关” 一般被设置为 ( ) 的地址。 A. DNS 服务器 B. Web 服务器 C. 路由器 D. 交换机1.【参考答案】C 【解析】只有在计算机上正确安装网卡驱动程序和网络协议&#xff0c;并正确设置 IP 地址信…

非root用户在linux中配置zsh(已解决ncurses-devel报错)

Zsh&#xff08;Z Shell&#xff09;是一款功能强大的交互式 Unix shell&#xff0c;以其高度可定制性和丰富的功能著称&#xff0c;被视为 Bash 的增强替代品。它支持智能补全、主题美化、插件扩展&#xff08;如 Oh My Zsh 框架&#xff09;、自动纠错、全局别名等特性&#…

《Foundations and Recent Trends in Multimodal Mobile Agents: A Survey》论文精读笔记

论文链接&#xff1a;https://arxiv.org/pdf/2411.02006 摘要 文章首先介绍了核心组件&#xff0c;并探讨了移动基准和交互环境中的关键代表性作品&#xff0c;旨在全面理解研究重点及其局限性。 接着&#xff0c;将这些进展分为两种主要方法&#xff1a; 基于提示的方法&a…

npm安装时一直卡住的解决方法

npm install 卡住通常是由于网络问题或缓存问题导致的。以下是几种解决方法&#xff1a; 方法1&#xff1a;清理npm缓存 npm cache clean --force npm install方法2&#xff1a;删除node_modules和package-lock.json重新安装 rm -rf node_modules package-lock.json npm instal…

[密码学实战]使用Java生成国密SM2加密证书等(四十三)

[密码学实战]使用Java生成国密SM2加密证书等(四十三) 本文将详细介绍如何通过Java代码生成符合国密标准的SM2加密证书,包括密钥对生成、证书扩展属性配置、PEM格式保存等关键步骤。 一. 运行结果示例 二. 国密算法与加密证书 国密算法(SM系列)是中国自主研发的密码算法体…

从零开始之stm32之CAN通信

从小白的视角了解并实现简单的STM32F103的CAN通信&#xff0c;直接上手。一、CAN协议简介CAN总线上传输的信息称为报文&#xff0c;当总线空闲时任何连接的单元都可以开始发送新的报文&#xff0c;有5种类型的帧&#xff1a;数据帧、遥控帧、错误帧、过载帧、帧间隔。数据帧有两…

Java 课程,每天解读一个简单Java之利用条件运算符的嵌套来完成此题:学习成绩>=90分的同学用A表示,60-89分之间的用B表示, * 60分以下

package ytr250812;/*题目&#xff1a;利用条件运算符的嵌套来完成此题&#xff1a;学习成绩>90分的同学用A表示&#xff0c;60-89分之间的用B表示&#xff0c;* 60分以下*/import java.util.Scanner;public class GradeEvaluator {public static void main(String[] args) …

Word XML 批注范围克隆处理器

该类用于处理 Word 文档&#xff08;XML 结构&#xff09;中被批注标记的文本范围&#xff0c; 实现指定内容的深度克隆&#xff0c;并将其插入到目标节点之后。 适用于在生成或修改 .docx 文件时复制批注内容块。/*** Word XML 批注范围克隆处理器* * 该类用于处理 Word 文档&…

MQTT:Java集成MQTT

目录Git项目路径一、原生java架构1.1 导入POM文件1.2 编写测试用例二、SpringBoot集成MQTT2.1 导入POM文件2.2 在YML文件中增加配置2.3 新建Properties配置文件映射配置2.4 创建连接工厂2.5 增加入站规则配置2.6 增加出站规则配置2.7 创建消息发送网关2.8 测试消息发送2.9 项目…

day 16 stm32 IIC

1.IIC概述1基于对话的形式完成&#xff0c;不需要同时进行发送和接收所以删掉了一根数据线&#xff0c;变成半双工2为了安全起见添加了应答机制3可以接多个模块&#xff0c;且互不干扰4异步时序&#xff0c;要求严格&#xff0c;发送过程中不能暂停&#xff0c;所以需要同步时序…

AMD KFD的BO设计分析系列 0:开篇

开启我始终不敢碰的GPU存储系列&#xff0c;先上个图把核心关系表达下&#xff0c;以此纪念。注&#xff1a;图中kfdm_mm误写&#xff0c;应该为kfd_mm&#xff0c;不修改了&#xff0c;请大家不要介意。

EUDR的核心内容,EUDR认证的好处,EUDR意义

近年来&#xff0c;全球森林退化问题日益严峻&#xff0c;毁林行为不仅加剧气候变化&#xff0c;还威胁生物多样性和原住民权益。为应对这一挑战&#xff0c;欧盟于2023年6月正式实施《欧盟零毁林法案》&#xff08;EU Deforestation-free Regulation, EUDR&#xff09;&#x…

数据分析专栏记录之 -基础数学与统计知识

数据分析专栏记录之 -基础数学与统计知识&#xff1a; 1、描述性统计 均值 data_set [10, 20, 30, 40, 50] mean sum(data_set)/len(data_set)np 里面的函数&#xff0c;对二维进行操作时&#xff0c; 默认每一列 mean1 np.mean(data_set) print(mean, mean1)s 0 for i…

《星辰建造师:C++多重继承的奇幻史诗》

&#x1f30c;&#x1f525; 《星辰建造师&#xff1a;多重继承与this指针的终极史诗》 &#x1f525;&#x1f30c;—— 一场融合魔法、科技与哲学的C奇幻冒险&#x1f320;&#x1f30c; 序章&#xff1a;代码宇宙的诞生 &#x1f30c;&#x1f320;在无尽的代码维度中&#…

云计算-OpenStack 运维开发实战:从 Restful API 到 Python SDK 全场景实现镜像上传、用户创建、云主机部署全流程

一、python-Restful Api 简介 Restful API 是一种软件架构风格,基于 HTTP 协议设计,通过统一的接口(如 URL 路径)和标准的 HTTP 方法(GET/POST/PUT/DELETE 等)实现资源(如数据、文件等)的操作,具有无状态、可缓存、客户端 - 服务器分离等特点。方法如下 用 GET 请求获…

RxJava 在 Android 中的深入解析:使用、原理与最佳实践

前言RxJava 是一个基于观察者模式的响应式编程库&#xff0c;它通过可观察序列和函数式操作符的组合&#xff0c;简化了异步和事件驱动程序的开发。在 Android 开发中&#xff0c;RxJava 因其强大的异步处理能力和简洁的代码风格而广受欢迎。本文将深入探讨 RxJava 的使用、核心…

面试实战 问题三十 HTTP协议中TCP三次握手与四次挥手详解

HTTP协议中TCP三次握手与四次挥手详解 在HTTP协议中&#xff0c;连接建立和断开依赖于底层的TCP协议。虽然HTTP本身不定义握手过程&#xff0c;但所有HTTP通信都通过TCP三次握手建立连接&#xff0c;通过四次挥手断开连接。以下是详细解析&#xff1a;一、TCP三次握手&#xff…

读《精益数据分析》:双边市场的核心指标分析

双边市场数据分析指南&#xff1a;从指标体系到实战落地&#xff08;基于《精益数据分析》框架&#xff09;在互联网平台经济中&#xff0c;双边市场&#xff08;如电商、出行、外卖、自由职业平台等&#xff09;的核心矛盾始终是"供需平衡与效率优化"。这类平台连接…

Queue参考代码

queue.c #include "queue.h" #include "stdlib.h" // 初始化循环队列 void initializeCircularQueue(CircularQueue *cq, uint8_t *buffer, uint32_t size) {cq->front 0;cq->rear 0;cq->count 0;cq->size size;cq->data buffer; }…