C++网络编程:TCP服务器与客户端的实现

C++网络编程:TCP服务器与客户端的实现

C++网络编程:TCP服务器与客户端的实现

在这里插入图片描述

一、学习目标与重点

本章将深入探讨C++网络编程的核心知识,帮助你掌握TCP服务器与客户端的实现。通过学习,你将能够:

  1. 理解网络编程的基本概念,掌握TCP/IP协议的核心要点
  2. 学会使用套接字编程,实现简单的TCP服务器与客户端
  3. 理解网络编程中的错误处理,提高程序的健壮性
  4. 学会使用Boost.Asio库,简化网络编程的复杂流程
  5. 培养网络编程思维,设计高效且稳定的网络应用

二、网络编程基础

2.1 TCP/IP协议简介

TCP/IP(传输控制协议/互联网协议)是互联网的核心协议,它提供了可靠的、面向连接的通信服务。TCP协议确保数据的可靠传输,通过三次握手建立连接,四次挥手关闭连接。

2.2 套接字编程基础

套接字(Socket)是网络编程的基础,它提供了进程间通信的接口。在C++中,可以使用<sys/socket.h>头文件提供的套接字API进行网络编程。

三、TCP服务器的实现

3.1 基本服务器结构

#include<iostream>#include<string>#include<cstring>#include<sys/socket.h>#include<netinet/in.h>#include<unistd.h>#include<arpa/inet.h>constint PORT =8080;constint BUFFER_SIZE =1024;intmain(){ std::cout <<"=== TCP服务器示例 ==="<< std::endl;// 1. 创建套接字int server_fd =socket(AF_INET, SOCK_STREAM,0);if(server_fd <0){ std::cerr <<"套接字创建失败"<< std::endl;return1;}// 2. 设置套接字选项,允许地址复用int opt =1;if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,&opt,sizeof(opt))<0){ std::cerr <<"设置套接字选项失败"<< std::endl;close(server_fd);return1;}// 3. 绑定地址和端口 sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port =htons(PORT);if(bind(server_fd,(structsockaddr*)&address,sizeof(address))<0){ std::cerr <<"绑定地址和端口失败"<< std::endl;close(server_fd);return1;}// 4. 监听连接if(listen(server_fd,3)<0){ std::cerr <<"监听连接失败"<< std::endl;close(server_fd);return1;} std::cout <<"服务器正在监听端口 "<< PORT <<"..."<< std::endl;// 5. 接受连接int addrlen =sizeof(address);int new_socket;while(true){ std::cout <<"等待客户端连接..."<< std::endl; new_socket =accept(server_fd,(structsockaddr*)&address,(socklen_t*)&addrlen);if(new_socket <0){ std::cerr <<"接受连接失败"<< std::endl;continue;}char client_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET,&address.sin_addr, client_ip, INET_ADDRSTRLEN); std::cout <<"客户端 "<< client_ip <<" 已连接"<< std::endl;// 6. 处理连接char buffer[BUFFER_SIZE]={0};int valread =read(new_socket, buffer, BUFFER_SIZE);if(valread <0){ std::cerr <<"读取数据失败"<< std::endl;close(new_socket);continue;} std::cout <<"收到来自客户端的消息: "<< buffer << std::endl;// 7. 发送响应 std::string response ="服务器已收到消息: "+ std::string(buffer);send(new_socket, response.c_str(), response.length(),0); std::cout <<"响应已发送"<< std::endl;// 8. 关闭连接close(new_socket); std::cout <<"客户端连接已关闭"<< std::endl;}// 9. 关闭服务器套接字close(server_fd);return0;}

3.2 多线程服务器

#include<iostream>#include<string>#include<cstring>#include<sys/socket.h>#include<netinet/in.h>#include<unistd.h>#include<arpa/inet.h>#include<thread>#include<vector>constint PORT =8080;constint BUFFER_SIZE =1024;voidhandleClient(int client_socket,const std::string& client_ip){char buffer[BUFFER_SIZE]={0};while(true){int valread =read(client_socket, buffer, BUFFER_SIZE);if(valread <0){ std::cerr <<"客户端 "<< client_ip <<" 读取数据失败"<< std::endl;break;}elseif(valread ==0){ std::cout <<"客户端 "<< client_ip <<" 已断开连接"<< std::endl;break;} std::cout <<"收到来自客户端 "<< client_ip <<" 的消息: "<< buffer << std::endl; std::string response ="服务器已收到消息: "+ std::string(buffer);send(client_socket, response.c_str(), response.length(),0); std::cout <<"响应已发送给客户端 "<< client_ip << std::endl;memset(buffer,0, BUFFER_SIZE);}close(client_socket);}intmain(){ std::cout <<"=== 多线程TCP服务器示例 ==="<< std::endl;int server_fd =socket(AF_INET, SOCK_STREAM,0);if(server_fd <0){ std::cerr <<"套接字创建失败"<< std::endl;return1;}int opt =1;if(setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,&opt,sizeof(opt))<0){ std::cerr <<"设置套接字选项失败"<< std::endl;close(server_fd);return1;} sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port =htons(PORT);if(bind(server_fd,(structsockaddr*)&address,sizeof(address))<0){ std::cerr <<"绑定地址和端口失败"<< std::endl;close(server_fd);return1;}if(listen(server_fd,3)<0){ std::cerr <<"监听连接失败"<< std::endl;close(server_fd);return1;} std::cout <<"服务器正在监听端口 "<< PORT <<"..."<< std::endl;int addrlen =sizeof(address);int new_socket; std::vector<std::thread> client_threads;while(true){ std::cout <<"等待客户端连接..."<< std::endl; new_socket =accept(server_fd,(structsockaddr*)&address,(socklen_t*)&addrlen);if(new_socket <0){ std::cerr <<"接受连接失败"<< std::endl;continue;}char client_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET,&address.sin_addr, client_ip, INET_ADDRSTRLEN); std::cout <<"客户端 "<< client_ip <<" 已连接"<< std::endl; client_threads.emplace_back(handleClient, new_socket, client_ip);}for(auto& thread : client_threads){if(thread.joinable()){ thread.join();}}close(server_fd);return0;}

四、TCP客户端的实现

4.1 基本客户端结构

#include<iostream>#include<string>#include<cstring>#include<sys/socket.h>#include<netinet/in.h>#include<unistd.h>#include<arpa/inet.h>#include<thread>constint PORT =8080;constint BUFFER_SIZE =1024;const std::string SERVER_IP ="127.0.0.1";voidreceiveMessages(int sock){char buffer[BUFFER_SIZE]={0};while(true){int valread =read(sock, buffer, BUFFER_SIZE);if(valread <0){ std::cerr <<"读取服务器响应失败"<< std::endl;break;}elseif(valread ==0){ std::cout <<"服务器已断开连接"<< std::endl;break;} std::cout <<"服务器响应: "<< buffer << std::endl;memset(buffer,0, BUFFER_SIZE);}}intmain(){ std::cout <<"=== TCP客户端示例 ==="<< std::endl;// 1. 创建套接字int sock =socket(AF_INET, SOCK_STREAM,0);if(sock <0){ std::cerr <<"套接字创建失败"<< std::endl;return1;}// 2. 配置服务器地址 sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_port =htons(PORT);if(inet_pton(AF_INET, SERVER_IP.c_str(),&serv_addr.sin_addr)<=0){ std::cerr <<"无效的服务器地址"<< std::endl;close(sock);return1;}// 3. 连接服务器if(connect(sock,(structsockaddr*)&serv_addr,sizeof(serv_addr))<0){ std::cerr <<"连接服务器失败"<< std::endl;close(sock);return1;} std::cout <<"已连接到服务器 "<< SERVER_IP <<":"<< PORT << std::endl;// 4. 接收服务器消息的线程 std::thread recv_thread(receiveMessages, sock); recv_thread.detach();// 5. 发送消息 std::string message;while(true){ std::cout <<"请输入消息(输入\"quit\"退出): "; std::getline(std::cin, message);if(message =="quit"){break;}send(sock, message.c_str(), message.length(),0);}// 6. 关闭套接字close(sock);return0;}

五、Boost.Asio库的使用

5.1 Boost.Asio基础

Boost.Asio是一个跨平台的网络编程库,它简化了网络编程的复杂流程,提供了异步、同步、单线程和多线程的编程模型。

5.2 使用Boost.Asio实现TCP服务器

#include<iostream>#include<string>#include<boost/asio.hpp>using boost::asio::ip::tcp;usingnamespace std;constint PORT =8080;voidhandleClient(tcp::socket socket){try{char buffer[1024];// 读取客户端消息 size_t len = socket.read_some(boost::asio::buffer(buffer)); string message(buffer, len); cout <<"收到客户端消息: "<< message << endl;// 发送响应 string response ="服务器已收到消息: "+ message; boost::asio::write(socket, boost::asio::buffer(response)); cout <<"响应已发送"<< endl;}catch(const std::exception& e){ cerr <<"处理客户端连接时出错: "<< e.what()<< endl;}}intmain(){ cout <<"=== Boost.Asio TCP服务器示例 ==="<< endl;try{ boost::asio::io_service io_service;// 创建acceptor tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), PORT)); cout <<"服务器正在监听端口 "<< PORT <<"..."<< endl;while(true){// 接受连接 tcp::socket socket(io_service); acceptor.accept(socket);// 处理连接 thread t(handleClient,move(socket)); t.detach();}}catch(const std::exception& e){ cerr <<"服务器出错: "<< e.what()<< endl;return1;}return0;}

5.3 使用Boost.Asio实现TCP客户端

#include<iostream>#include<string>#include<boost/asio.hpp>using boost::asio::ip::tcp;usingnamespace std;constint PORT =8080;const string SERVER_IP ="127.0.0.1";voidreceiveMessages(tcp::socket& socket){try{char buffer[1024];while(true){ size_t len = socket.read_some(boost::asio::buffer(buffer)); string response(buffer, len); cout <<"服务器响应: "<< response << endl;}}catch(const std::exception& e){ cerr <<"读取服务器响应时出错: "<< e.what()<< endl;}}intmain(){ cout <<"=== Boost.Asio TCP客户端示例 ==="<< endl;try{ boost::asio::io_service io_service;// 解析服务器地址 tcp::resolver resolver(io_service); tcp::resolver::query query(tcp::v4(), SERVER_IP,to_string(PORT)); tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);// 连接服务器 tcp::socket socket(io_service); boost::asio::connect(socket, endpoint_iterator); cout <<"已连接到服务器 "<< SERVER_IP <<":"<< PORT << endl;// 接收服务器消息的线程 thread t(receiveMessages,ref(socket)); t.detach();// 发送消息 string message;while(true){ cout <<"请输入消息(输入\"quit\"退出): ";getline(cin, message);if(message =="quit"){break;} boost::asio::write(socket, boost::asio::buffer(message));}}catch(const std::exception& e){ cerr <<"客户端出错: "<< e.what()<< endl;return1;}return0;}

六、综合案例:实现一个简单的聊天服务器

6.1 项目结构

ChatServer/ ├── include/ │ └── ChatServer.h ├── src/ │ ├── ChatServer.cpp │ └── main.cpp └── build/ 

6.2 核心代码

// include/ChatServer.h#ifndefCHATSERVER_H#defineCHATSERVER_H#include<iostream>#include<string>#include<vector>#include<thread>#include<mutex>#include<boost/asio.hpp>using boost::asio::ip::tcp;usingnamespace std;classChatServer{private: boost::asio::io_service io_service; tcp::acceptor acceptor; vector<shared_ptr<tcp::socket>> clients; mutex clients_mutex;voidacceptConnection();voidhandleClient(shared_ptr<tcp::socket> socket);voidbroadcastMessage(const string& message,const tcp::endpoint& sender_endpoint);public:ChatServer(int port);voidrun();};#endif// CHATSERVER_H// src/ChatServer.cpp#include"ChatServer.h"ChatServer::ChatServer(int port):acceptor(io_service, tcp::endpoint(tcp::v4(), port)){}voidChatServer::run(){ cout <<"聊天服务器正在运行..."<< endl;acceptConnection(); io_service.run();}voidChatServer::acceptConnection(){ shared_ptr<tcp::socket> socket =make_shared<tcp::socket>(io_service); acceptor.async_accept(*socket,[this, socket](const boost::system::error_code& ec){if(!ec){ cout <<"客户端 "<< socket->remote_endpoint()<<" 已连接"<< endl;{ lock_guard<mutex>lock(clients_mutex); clients.push_back(socket);}handleClient(socket);}else{ cerr <<"接受连接失败: "<< ec.message()<< endl;}acceptConnection();});}voidChatServer::handleClient(shared_ptr<tcp::socket> socket){auto endpoint = socket->remote_endpoint();char buffer[1024]; socket->async_read_some(boost::asio::buffer(buffer),[this, socket, endpoint](const boost::system::error_code& ec, size_t bytes_transferred){if(!ec){ string message(buffer, bytes_transferred); cout <<"收到来自 "<< endpoint <<" 的消息: "<< message << endl;broadcastMessage(message, endpoint);handleClient(socket);}else{ cout <<"客户端 "<< endpoint <<" 已断开连接"<< endl;{ lock_guard<mutex>lock(clients_mutex); clients.erase(remove(clients.begin(), clients.end(), socket), clients.end());}}});}voidChatServer::broadcastMessage(const string& message,const tcp::endpoint& sender_endpoint){ string full_message = sender_endpoint.address().to_string()+":"+to_string(sender_endpoint.port())+" 说: "+ message;{ lock_guard<mutex>lock(clients_mutex);for(auto socket : clients){if(socket->remote_endpoint()!= sender_endpoint){ boost::asio::async_write(*socket, boost::asio::buffer(full_message),[](const boost::system::error_code& ec, size_t /*bytes_transferred*/){if(ec){ cerr <<"发送消息失败: "<< ec.message()<< endl;}});}}}}// src/main.cpp#include<iostream>#include"ChatServer.h"intmain(){ std::cout <<"=== 简单的聊天服务器 ==="<< std::endl;try{constint PORT =8080; ChatServer server(PORT); server.run();}catch(const std::exception& e){ std::cerr <<"服务器出错: "<< e.what()<< std::endl;return1;}return0;}

6.3 项目构建与运行

# 创建构建目录mkdir-p build &&cd build # 配置CMake cmake -DCMAKE_BUILD_TYPE=Release ..# 编译项目 cmake --build.--config Release # 运行服务器 ./ChatServer # 在另一个终端运行客户端 ./ChatClient 

七、总结与练习

7.1 本章总结

本章介绍了C++网络编程的核心知识,包括:

  1. 网络编程的基本概念与TCP/IP协议
  2. 套接字编程基础
  3. TCP服务器的实现
  4. 多线程服务器的实现
  5. TCP客户端的实现
  6. Boost.Asio库的使用
  7. 综合案例:实现一个简单的聊天服务器

7.2 练习题

  1. 写一个程序,使用套接字编程实现一个简单的HTTP服务器。
  2. 编写一个函数,使用Boost.Asio库实现一个简单的FTP客户端。
  3. 写一个程序,使用多线程实现一个高并发的网络服务器。
  4. 实现一个类,使用Boost.Asio库实现一个简单的邮件服务器。
  5. 写一个程序,使用网络编程实现一个简单的实时数据传输系统。

7.3 进阶挑战

  1. 研究如何使用C++的无锁数据结构优化网络服务器的性能。
  2. 学习如何使用C++的协程(C++20及以后)与网络编程结合。
  3. 研究如何使用C++的并发编程优化一个大型网络应用。
  4. 学习如何使用C++的加密库(如OpenSSL)实现安全的网络通信。
  5. 研究如何使用C++的网络编程实现一个高可用性的分布式系统。

Read more

【洛谷】从记忆化搜索到动态规划 状态表示 + 转移方程 + 空间优化全攻略

【洛谷】从记忆化搜索到动态规划 状态表示 + 转移方程 + 空间优化全攻略

文章目录 * 从记忆化搜索到动态规划 * 记忆化搜索 * 递归改递推 * 动态规划 * 下楼梯 * 数字三角形 小编提醒:在动态规划问题中,将数组命名为f和dp都可以。 从记忆化搜索到动态规划 记忆化搜索 在搜索的过程中,如果搜索树中有很多重复的结点,此时可以通过⼀个 “备忘录”,记录第⼀次搜索到 的结果。当下⼀次搜索到这个结点时,直接在 “备忘录” ⾥⾯找结果。其中,搜索树中的⼀个⼀个结点,也称为⼀个⼀个状态。 ⽐如经典的斐波那契数列问题: int f[N];// 备忘录intfib(int n){// 搜索之前先往备忘录⾥⾯瞅瞅if(f[n]!=-1)return f[n];if(n ==0|

By Ne0inhk
Yolo11环境配置win+Python+Anaconda--小白目标检测学习专用(超详细)

Yolo11环境配置win+Python+Anaconda--小白目标检测学习专用(超详细)

本文基于Anaconda和Pycharm已经安装 目录 一、下载Yolo11的源代码 二、部署环境 1.打开Anaconda软件 2.创建环境 3.运行环境 4.安装必要的包 注意:如果你下载包很慢 4.1安装Torch 4.1.1【GPU版本】 4.1.2【CPU版本】 4.2安装其他依赖包 三、运行项目 1.打开项目 2.更换解释器 3.验证运行 四、如何训练以及数据集配置 1.训练代码 2.数据集配置文件的创建 3.训练结果图的参数介绍 4.WiderPerson数据集(附加) 一、下载Yolo11的源代码  1.

By Ne0inhk
Flutter 三方库 hashids2 基于鸿蒙安全内核的深度隐匿映射适配:数字指纹泄露防御层、生成短小精悍唯一不可逆加盐哈希,护航全链路请求 URL 隐私-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 hashids2 基于鸿蒙安全内核的深度隐匿映射适配:数字指纹泄露防御层、生成短小精悍唯一不可逆加盐哈希,护航全链路请求 URL 隐私-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 hashids2 基于鸿蒙安全内核的深度隐匿映射适配:突破高敏感数字指纹泄露防御层、生成短小精悍唯一不可逆加盐哈希,护航全链路请求 URL 隐私资产 在鸿蒙应用的高度依赖数据隐私(如隐藏数据库递增 ID、生成短网址或混淆用户主页链接)中,如何将枯燥的数字转换为非连续、看似随机且人类友好的标识符?hashids2 库提供了一套基于 Hashids 协议的工业级加密 ID 生成方案。本文将详解该库在 OpenHarmony 上的适配要点。 前言 什么是 hashids2?当你在 URL 中展示 user/123 时,攻击者很容易通过猜测 124 或 125 来爬取你的数据。hashids2 能够根据你设定的盐值(Salt)。将整数 123 转换为类似

By Ne0inhk

优选算法——位运算

👇作者其它专栏 《数据结构与算法》《算法》《C++起始之路》 1.前要知识 《位操作符的妙用》 2.相关题解 2.1判定字符是否唯一 算法思路: 利用【位图】的思想,每一个【比特位】代表一个【字符】,一个int类型的变量的32位足够表示所有的小写字母。比特位里若为0,表示这个字符没有出现过;若为1,表示该字符出现过。 可以用一个【整数】来充当【哈希表】。 class Solution { public: bool isUnique(string astr) { //利用鸽巢原理优化 if(astr.size()>26) return false; int bitmap=0; for(auto i:

By Ne0inhk