使用C++实现简单的TCP服务器和客户端
- 介绍
- 准备工作
- 1. TCP服务器实现
- 代码结构
- 解释
- 2. TCP客户端实现
- 代码结构
- 解释
- 3. 测试
- 1.编译:
- 2.运行
- 结语
介绍
本文将通过一个简单的例子,介绍如何使用C++实现一个基本的TCP服务器和客户端。这个例子展示了如何创建服务器端接收客户端的连接,如何处理接收到的数据,并如何将数据发送回客户端。
我们会分两部分来介绍:
- TCP服务器的实现:包括如何创建服务器、监听端口、接收客户端请求等。
- TCP客户端的实现:客户端如何连接到服务器、发送数据并接收服务器返回的数据。
准备工作
首先,确保你的开发环境中已经安装了C++编译器(如GCC或Clang)以及支持的标准库。在Linux或类Unix系统上进行开发是最常见的,下面的代码也适用于这种环境。
1. TCP服务器实现
代码结构
首先,来看下TCP服务器的代码实现:
#include <arpa/inet.h>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include <thread>
#define SERVER_PORT 12345
#define BUFFER_SIZE 1024
class TcpServer {
public:
TcpServer(int port);
~TcpServer();
void Start();
void Stop();
private:
void ListenThreadFunc();
void ReceiveThreadFunc(int clientSocket);
void CloseListenSocket();
void CloseDataSocket();
int mListenSocket;
int mDataSocket;
int mPort;
std::unique_ptr<std::thread> mListenThread;
bool mRunning;
};
TcpServer::TcpServer(int port) : mListenSocket(-1), mDataSocket(-1), mPort(port), mListenThread(nullptr), mRunning(false) {}
TcpServer::~TcpServer() {
Stop();
}
void TcpServer::Start() {
if (mRunning) {
std::cerr << "Server is already running!" << std::endl;
return;
}
mRunning = true;
mListenSocket = socket(AF_INET, SOCK_STREAM, 0);
if (mListenSocket == -1) {
std::cerr << "Failed to create listen socket!" << std::endl;
return;
}
sockaddr_in serverAddr{};
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(mPort);
serverAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(mListenSocket, reinterpret_cast<const sockaddr*>(&serverAddr), sizeof(serverAddr)) == -1) {
std::cerr << "Failed to bind socket!" << std::endl;
CloseListenSocket();
return;
}
if (listen(mListenSocket, 5) == -1) {
std::cerr << "Failed to listen on socket!" << std::endl;
CloseListenSocket();
return;
}
mListenThread = std::make_unique<std::thread>([this] { ListenThreadFunc(); });
}
void TcpServer::Stop() {
if (!mRunning) return;
mRunning = false;
CloseListenSocket();
CloseDataSocket();
if (mListenThread && mListenThread->joinable()) {
mListenThread->join();
}
}
void TcpServer::ListenThreadFunc() {
while (mRunning) {
sockaddr_in clientAddr{};
socklen_t len = sizeof(clientAddr);
int clientSocket = accept(mListenSocket, reinterpret_cast<sockaddr*>(&clientAddr), &len);
if (clientSocket == -1) {
std::cerr << "Accept failed!" << std::endl;
continue;
}
std::cout << "Client connected!" << std::endl;
std::thread receiveThread([this, clientSocket] { ReceiveThreadFunc(clientSocket); });
receiveThread.detach();
}
}
void TcpServer::ReceiveThreadFunc(int clientSocket) {
char buffer[BUFFER_SIZE];
while (mRunning) {
memset(buffer, 0, BUFFER_SIZE);
int res = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (res <= 0) {
std::cerr << "Receive failed or client disconnected!" << std::endl;
break;
}
std::cout << "Received from client: " << buffer << std::endl;
send(clientSocket, "Message received", 17, 0); // Send acknowledgment back
}
CloseDataSocket();
}
void TcpServer::CloseListenSocket() {
if (mListenSocket != -1) {
close(mListenSocket);
mListenSocket = -1;
}
}
void TcpServer::CloseDataSocket() {
if (mDataSocket != -1) {
close(mDataSocket);
mDataSocket = -1;
}
}
int main() {
TcpServer server(SERVER_PORT);
server.Start();
std::this_thread::sleep_for(std::chrono::minutes(10)); // Server will run for 10 minutes
server.Stop();
return 0;
}
解释
TcpServer
类:定义了一个TcpServer
类来处理服务器的启动、停止以及接收客户端请求。Start()
方法:创建一个监听套接字,绑定端口并开始监听。ListenThreadFunc()
方法:负责监听来自客户端的连接请求。ReceiveThreadFunc()
方法:每个客户端连接都会创建一个新的线程来处理数据接收。
2. TCP客户端实现
代码结构
接下来是客户端的实现。客户端需要连接到服务器,发送请求并接收响应。
#include <iostream>
#include <string>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <sys/socket.h>
#define SERVER_PORT 12345
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024
int main() {
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == -1) {
std::cerr << "Create socket failed!" << std::endl;
return -1;
}
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(SERVER_PORT);
serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
if (connect(clientSocket, reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr)) == -1) {
std::cerr << "Connect to server failed!" << std::endl;
close(clientSocket);
return -1;
}
std::cout << "Connected to server!" << std::endl;
// Send message
std::string message = "Hello from client!";
send(clientSocket, message.c_str(), message.size(), 0);
// Receive response
char buffer[BUFFER_SIZE];
ssize_t receivedBytes = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (receivedBytes == -1) {
std::cerr << "Receive failed!" << std::endl;
} else if (receivedBytes == 0) {
std::cerr << "Server closed connection!" << std::endl;
} else {
buffer[receivedBytes] = '\0';
std::cout << "Received from server: " << buffer << std::endl;
}
close(clientSocket);
return 0;
}
解释
TcpClient
:客户端通过connect()
与服务器建立连接,使用send()
发送数据,并通过recv()
接收服务器的响应。
3. 测试
1.编译:
- 编译服务器:
g++ TcpServer.cpp -o TcpServer -std=c++17 -lpthread
- 编译客户端
g++ TcpClient.cpp -o TcpClient -std=c++17
2.运行
- 启动服务器:
./TcpServer
- 启动客户端
./TcpClient
结语
通过这篇文章,我们实现了一个简单的 TCP 服务器和客户端示例,学习了如何使用 C++ 来进行网络编程。在实际项目中,你可以根据需求进一步扩展这些功能,比如加入多线程处理、客户端消息队列、认证机制等。