news 2026/5/6 8:09:45

C++:实现echo服务端和客户端(附带源码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++:实现echo服务端和客户端(附带源码)

一、项目背景详细介绍

在网络编程学习中,Echo(回显)服务是最经典、也是最重要的入门示例之一。

所谓Echo 服务,指的是:

客户端发送什么数据,服务端原样返回什么数据

虽然功能非常简单,但它几乎覆盖了TCP 网络编程的全部核心知识点

  • 客户端 / 服务端模型

  • TCP 三次握手

  • 套接字创建与关闭

  • 阻塞式 I/O

  • 字节流的收发过程

  • 多客户端并发的基础思路

在真实工程中,Echo 服务常用于:

  • 网络连通性测试

  • 性能压测基准

  • 协议调试

  • 网络框架最小可运行原型(MVP)

C++ 学习角度来看,实现一个 Echo 服务可以系统训练:

  • Linux Socket API 使用

  • 字符缓冲区管理

  • 错误处理与边界判断

  • 客户端与服务端解耦设计

因此,本项目的目标是:

使用 C++ 从零实现一个完整、可运行、可教学的 Echo 服务端和客户端


二、项目需求详细介绍

2.1 功能需求

服务端(Echo Server)

  1. 监听指定端口

  2. 接受客户端 TCP 连接

  3. 接收客户端发送的数据

  4. 将接收到的数据原样返回给客户端

  5. 支持客户端断开连接


客户端(Echo Client)

  1. 连接服务端

  2. 从终端读取用户输入

  3. 将输入发送给服务端

  4. 接收服务端回显数据并显示

  5. 支持主动退出


2.2 技术要求

  • 使用TCP 协议

  • 基于Linux / Unix Socket API

  • 阻塞式通信(便于教学)

  • 服务端与客户端逻辑清晰分离

  • 所有代码集中在一个代码块

  • 使用注释模拟多文件结构

  • 所有函数提供详细中文注释


2.3 教学要求

  • 代码结构清晰、易读

  • 注释解释“为什么这样写”

  • 可直接复制编译运行

  • 适合作为网络编程第一课项目


三、相关技术详细介绍

3.1 TCP 网络通信模型

TCP 通信是一个典型的客户端 / 服务端模型

Client -----> Server | | send recv recv send

核心流程如下:

服务端流程

  1. socket()—— 创建监听套接字

  2. bind()—— 绑定 IP 和端口

  3. listen()—— 进入监听状态

  4. accept()—— 接受客户端连接

  5. recv()/send()—— 数据通信


客户端流程

  1. socket()—— 创建套接字

  2. connect()—— 连接服务端

  3. send()—— 发送数据

  4. recv()—— 接收回显


3.2 阻塞式 I/O 特点

  • recv()在无数据时会阻塞

  • send()在缓冲区满时可能阻塞

  • 优点:逻辑简单、易理解

  • 缺点:并发能力有限(后续可优化)


3.3 字节流通信特点

TCP 是字节流协议

  • 没有消息边界

  • 一次send≠ 一次recv

  • Echo 服务中直接原样返回即可


四、实现思路详细介绍

4.1 总体设计思路

  1. 使用一个程序同时支持:

    • Echo 服务端模式

    • Echo 客户端模式

  2. 通过命令行选择运行模式

  3. 服务端使用循环处理客户端(单客户端版,便于教学)

  4. 客户端循环发送并接收数据


4.2 服务端处理流程

  1. 创建监听 socket

  2. 绑定端口并监听

  3. 接受客户端连接

  4. 循环:

    • 接收数据

    • 直接发送回客户端

  5. 客户端断开后关闭连接


4.3 客户端处理流程

  1. 连接服务端

  2. 从终端读取一行输入

  3. 发送给服务端

  4. 接收回显数据并打印

  5. 输入quit退出


五、完整实现代码

/**************************************************** * 文件名:Echo.cpp * 描述:C++ Echo 服务端 & 客户端(教学示例) ****************************************************/ #include <iostream> #include <string> #include <cstring> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> using namespace std; const int PORT = 8888; const int BUFFER_SIZE = 1024; /**************************************************** * Echo 服务端实现 ****************************************************/ void runEchoServer() { // 1. 创建监听套接字 int listenFd = socket(AF_INET, SOCK_STREAM, 0); if (listenFd < 0) { cerr << "socket 创建失败" << endl; return; } // 2. 绑定地址和端口 sockaddr_in serverAddr{}; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); serverAddr.sin_addr.s_addr = INADDR_ANY; if (bind(listenFd, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) { cerr << "bind 失败" << endl; close(listenFd); return; } // 3. 开始监听 if (listen(listenFd, 5) < 0) { cerr << "listen 失败" << endl; close(listenFd); return; } cout << "Echo 服务端启动,监听端口 " << PORT << endl; // 4. 接受客户端连接 sockaddr_in clientAddr{}; socklen_t clientLen = sizeof(clientAddr); int connFd = accept(listenFd, (sockaddr*)&clientAddr, &clientLen); if (connFd < 0) { cerr << "accept 失败" << endl; close(listenFd); return; } cout << "客户端已连接" << endl; char buffer[BUFFER_SIZE]; // 5. Echo 处理循环 while (true) { memset(buffer, 0, BUFFER_SIZE); // 接收客户端数据 int bytes = recv(connFd, buffer, BUFFER_SIZE, 0); if (bytes <= 0) { cout << "客户端断开连接" << endl; break; } cout << "收到数据:" << buffer << endl; // 原样返回(Echo) send(connFd, buffer, bytes, 0); } // 6. 关闭连接 close(connFd); close(listenFd); } /**************************************************** * Echo 客户端实现 ****************************************************/ void runEchoClient(const string& serverIp) { // 1. 创建套接字 int sockFd = socket(AF_INET, SOCK_STREAM, 0); if (sockFd < 0) { cerr << "socket 创建失败" << endl; return; } // 2. 配置服务器地址 sockaddr_in serverAddr{}; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); inet_pton(AF_INET, serverIp.c_str(), &serverAddr.sin_addr); // 3. 连接服务器 if (connect(sockFd, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) { cerr << "connect 失败" << endl; close(sockFd); return; } cout << "已连接到 Echo 服务端" << endl; char buffer[BUFFER_SIZE]; string input; // 4. 发送与接收循环 while (true) { cout << "请输入内容(quit退出): "; getline(cin, input); if (input == "quit") { break; } // 发送数据 send(sockFd, input.c_str(), input.size(), 0); // 接收回显 memset(buffer, 0, BUFFER_SIZE); int bytes = recv(sockFd, buffer, BUFFER_SIZE, 0); if (bytes <= 0) { cout << "服务器断开连接" << endl; break; } cout << "Echo 返回:" << buffer << endl; } // 5. 关闭套接字 close(sockFd); } /**************************************************** * 主函数 ****************************************************/ int main() { cout << "请选择运行模式 (1-服务端 2-客户端): "; int mode; cin >> mode; cin.ignore(); if (mode == 1) { runEchoServer(); } else if (mode == 2) { string ip; cout << "请输入服务器IP: "; cin >> ip; cin.ignore(); runEchoClient(ip); } return 0; }

六、代码详细解读(仅解读方法作用)

  • runEchoServer:启动 Echo 服务端,接收并原样返回客户端数据

  • runEchoClient:连接服务端,发送数据并接收回显

  • socket:创建 TCP 套接字

  • bind / listen / accept:服务端三步走

  • connect:客户端发起连接

  • send / recv:完成数据通信


七、项目详细总结

通过本项目,你已经完整掌握:

  • TCP 客户端 / 服务端通信流程

  • Linux Socket API 的核心使用方式

  • 阻塞式 I/O 通信模型

  • 字节流收发的基本规律

  • 网络程序的最小可运行结构

这是后续学习:

  • 多客户端并发服务器

  • epoll / select

  • 高性能网络框架

  • RPC / HTTP 服务器

必经基础项目


八、项目常见问题及解答

Q1:为什么 recv 返回 0?
A:表示对端正常关闭连接。

Q2:一次 send 一定对应一次 recv 吗?
A:不一定,TCP 是字节流协议。

Q3:为什么服务端只能处理一个客户端?
A:这是教学简化版本,后续可扩展为多线程或 epoll。


九、扩展方向与性能优化

  1. 支持多客户端并发(多线程 / epoll)

  2. 添加消息长度协议

  3. 非阻塞 I/O

  4. 日志系统

  5. 封装为网络库

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 13:27:39

AI证件照背景虚化秘籍:云端打造专业级人像

AI证件照背景虚化秘籍&#xff1a;云端打造专业级人像 你是不是也遇到过这种情况&#xff1f;作为自媒体博主&#xff0c;需要一张拿得出手的形象照——既要显得专业、正式&#xff0c;又不想太死板。可市面上大多数证件照工具只能换纯色背景&#xff08;白、蓝、红&#xff0…

作者头像 李华
网站建设 2026/5/2 21:08:02

从需求到架构:企业知识库AI助手的敏捷开发实践

从需求到架构:企业知识库AI助手的敏捷开发实践——以用户价值为核心的迭代式系统构建 元数据框架 标题 从需求到架构:企业知识库AI助手的敏捷开发实践——以用户价值为核心的迭代式系统构建 关键词 企业知识库、AI助手、敏捷开发、检索增强生成(RAG)、需求工程、系统架…

作者头像 李华
网站建设 2026/4/29 21:35:42

cv_unet_image-matting处理速度慢?GPU利用率提升优化教程

cv_unet_image-matting处理速度慢&#xff1f;GPU利用率提升优化教程 1. 引言&#xff1a;图像抠图性能瓶颈与优化目标 在基于 U-Net 架构的 cv_unet_image-matting 图像抠图项目中&#xff0c;尽管模型具备高精度的人像分割能力&#xff0c;但在实际使用过程中&#xff0c;用…

作者头像 李华
网站建设 2026/4/29 21:35:55

Scrapy ImagesPipeline和FilesPipeline自定义使用

Scrapy 作为 Python 生态中强大的爬虫框架&#xff0c;内置了ImagesPipeline和FilesPipeline两个核心管道&#xff0c;专门用于处理图片、文件的下载需求。默认配置虽能满足基础场景&#xff0c;但实际开发中&#xff0c;我们常需要自定义存储路径、过滤文件格式、处理下载异常…

作者头像 李华
网站建设 2026/4/30 19:25:55

Fun-ASR真实体验分享:会议录音转文字超高效

Fun-ASR真实体验分享&#xff1a;会议录音转文字超高效 在远程办公和线上协作日益普及的今天&#xff0c;会议记录已成为日常工作中不可或缺的一环。然而&#xff0c;手动整理录音不仅耗时耗力&#xff0c;还容易遗漏关键信息。有没有一种工具&#xff0c;能将会议录音快速、准…

作者头像 李华
网站建设 2026/5/2 14:49:18

Alkyne-PEG-Do;Alkyne-PEG-Dopamine:炔基 PEG 多巴胺的核心性能一览

试剂基本信息中文名称&#xff1a;丙炔聚乙二醇多巴胺&#xff1b;丙炔-聚乙二醇-多巴胺英文名称&#xff1a;Alkyne-PEG-Do&#xff1b;Dopamine-PEG-Alkyne&#xff1b;Alkyne-PEG-Dopamine外观&#xff1a;液体或固体粉末溶解性&#xff1a;溶于有机溶剂纯度&#xff1a;95%…

作者头像 李华