C++网络编程:TCP服务器与客户端的实现
C++网络编程:TCP服务器与客户端的实现
一、学习目标与重点
本章将深入探讨C++网络编程的核心知识,帮助你掌握TCP服务器与客户端的实现。通过学习,你将能够:
- 理解网络编程的基本概念,掌握TCP/IP协议的核心要点
- 学会使用套接字编程,实现简单的TCP服务器与客户端
- 理解网络编程中的错误处理,提高程序的健壮性
- 学会使用Boost.Asio库,简化网络编程的复杂流程
- 培养网络编程思维,设计高效且稳定的网络应用
二、网络编程基础
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++网络编程的核心知识,包括:
- 网络编程的基本概念与TCP/IP协议
- 套接字编程基础
- TCP服务器的实现
- 多线程服务器的实现
- TCP客户端的实现
- Boost.Asio库的使用
- 综合案例:实现一个简单的聊天服务器
7.2 练习题
- 写一个程序,使用套接字编程实现一个简单的HTTP服务器。
- 编写一个函数,使用Boost.Asio库实现一个简单的FTP客户端。
- 写一个程序,使用多线程实现一个高并发的网络服务器。
- 实现一个类,使用Boost.Asio库实现一个简单的邮件服务器。
- 写一个程序,使用网络编程实现一个简单的实时数据传输系统。
7.3 进阶挑战
- 研究如何使用C++的无锁数据结构优化网络服务器的性能。
- 学习如何使用C++的协程(C++20及以后)与网络编程结合。
- 研究如何使用C++的并发编程优化一个大型网络应用。
- 学习如何使用C++的加密库(如OpenSSL)实现安全的网络通信。
- 研究如何使用C++的网络编程实现一个高可用性的分布式系统。