C++网络编程:TCP服务器与客户端实现
一、学习目标与重点
本章将深入探讨C++网络编程的核心知识,帮助你掌握TCP服务器与客户端的实现。通过学习,你将能够:
- 理解网络编程的基本概念,掌握TCP/IP协议的核心要点
- ,实现简单的TCP服务器与客户端
C++网络编程涉及TCP/IP协议、套接字编程及Boost.Asio库应用。本文通过基础与多线程服务器、客户端代码示例,演示TCP连接、数据收发及错误处理流程。综合案例展示基于Boost.Asio的聊天服务器实现,包含项目结构、核心类设计与构建运行步骤,旨在帮助开发者掌握高效稳定的网络应用开发技能。

本章将深入探讨C++网络编程的核心知识,帮助你掌握TCP服务器与客户端的实现。通过学习,你将能够:
TCP/IP(传输控制协议/互联网协议)是互联网的核心协议,它提供了可靠的、面向连接的通信服务。TCP协议确保数据的可靠传输,通过三次握手建立连接,四次挥手关闭连接。
套接字(Socket)是网络编程的基础,它提供了进程间通信的接口。在C++中,可以使用<sys/socket.h>头文件提供的套接字API进行网络编程。
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
const int PORT = 8080;
const int BUFFER_SIZE = 1024;
int main() {
std::cout << "=== TCP服务器示例 ===" << std::endl;
// 1. 创建套接字
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
std::cerr << "套接字创建失败" << std::endl;
return 1;
}
// 2. 设置套接字选项,允许地址复用
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
std::cerr << "设置套接字选项失败" << std::endl;
close(server_fd);
return 1;
}
// 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, (struct sockaddr*)&address, sizeof(address)) < 0) {
std::cerr << "绑定地址和端口失败" << std::endl;
close(server_fd);
return 1;
}
// 4. 监听连接
if (listen(server_fd, 3) < 0) {
std::cerr << "监听连接失败" << std::endl;
close(server_fd);
return 1;
}
std::cout << "服务器正在监听端口 " << PORT << "..." << std::endl;
// 5. 接受连接
int addrlen = sizeof(address);
int new_socket;
while (true) {
std::cout << "等待客户端连接..." << std::endl;
new_socket = accept(server_fd, (struct sockaddr*)&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);
return 0;
}
#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>
const int PORT = 8080;
const int BUFFER_SIZE = 1024;
void handleClient(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;
} else if (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);
}
int main() {
std::cout << "=== 多线程TCP服务器示例 ===" << std::endl;
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
std::cerr << "套接字创建失败" << std::endl;
return 1;
}
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
std::cerr << "设置套接字选项失败" << std::endl;
close(server_fd);
return 1;
}
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
std::cerr << "绑定地址和端口失败" << std::endl;
close(server_fd);
return 1;
}
if (listen(server_fd, 3) < 0) {
std::cerr << "监听连接失败" << std::endl;
close(server_fd);
return 1;
}
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, (struct sockaddr*)&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);
return 0;
}
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <thread>
const int PORT = 8080;
const int BUFFER_SIZE = 1024;
const std::string SERVER_IP = "127.0.0.1";
void receiveMessages(int sock) {
char buffer[BUFFER_SIZE] = {0};
while (true) {
int valread = read(sock, buffer, BUFFER_SIZE);
if (valread < 0) {
std::cerr << "读取服务器响应失败" << std::endl;
break;
} else if (valread == 0) {
std::cout << "服务器已断开连接" << std::endl;
break;
}
std::cout << "服务器响应:" << buffer << std::endl;
memset(buffer, 0, BUFFER_SIZE);
}
}
int main() {
std::cout << "=== TCP客户端示例 ===" << std::endl;
// 1. 创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
std::cerr << "套接字创建失败" << std::endl;
return 1;
}
// 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);
return 1;
}
// 3. 连接服务器
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
std::cerr << "连接服务器失败" << std::endl;
close(sock);
return 1;
}
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);
return 0;
}
Boost.Asio是一个跨平台的网络编程库,它简化了网络编程的复杂流程,提供了异步、同步、单线程和多线程的编程模型。
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
using namespace std;
const int PORT = 8080;
void handleClient(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;
}
}
int main() {
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;
return 1;
}
return 0;
}
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
using namespace std;
const int PORT = 8080;
const string SERVER_IP = "127.0.0.1";
void receiveMessages(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;
}
}
int main() {
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;
return 1;
}
return 0;
}
ChatServer/
├── include/
│ └── ChatServer.h
├── src/
│ ├── ChatServer.cpp
│ └── main.cpp
└── build/
// include/ChatServer.h
#ifndef CHATSERVER_H
#define CHATSERVER_H
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
using namespace std;
class ChatServer {
private:
boost::asio::io_service io_service;
tcp::acceptor acceptor;
vector<shared_ptr<tcp::socket>> clients;
mutex clients_mutex;
void acceptConnection();
void handleClient(shared_ptr<tcp::socket> socket);
void broadcastMessage(const string& message, const tcp::endpoint& sender_endpoint);
public:
ChatServer(int port);
void run();
};
#endif // CHATSERVER_H
// src/ChatServer.cpp
#include "ChatServer.h"
ChatServer::ChatServer(int port) : acceptor(io_service, tcp::endpoint(tcp::v4(), port)) {}
void ChatServer::run() {
cout << "聊天服务器正在运行..." << endl;
acceptConnection();
io_service.run();
}
void ChatServer::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();
});
}
void ChatServer::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());
}
}
});
}
void ChatServer::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"
int main() {
std::cout << "=== 简单的聊天服务器 ===" << std::endl;
try {
const int PORT = 8080;
ChatServer server(PORT);
server.run();
} catch (const std::exception& e) {
std::cerr << "服务器出错:" << e.what() << std::endl;
return 1;
}
return 0;
}
# 创建构建目录
mkdir -p build && cd build
# 配置CMake
cmake -DCMAKE_BUILD_TYPE=Release ..
# 编译项目
cmake --build . --config Release
# 运行服务器
./ChatServer
# 在另一个终端运行客户端
./ChatClient
本章介绍了C++网络编程的核心知识,包括:

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online