实现了一个基本的 TCP 服务器,可以接受多个客户端连接,然后持续接收客户端发送的信息, 最后将接收到的信息再发送回客户端 。
源码
头文件(TCPServerTest.h)
cpp
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <functional> // 添加这个头文件以使用 std::bind
#include <string>
#pragma comment(lib, "ws2_32.lib")
class TCPSever {
public:
TCPSever(int port);
~TCPSever();
int createSocket(); //创建套接字
int serverPreparation(); //服务器连接准备
int startConnectClient(); //接受连接客户端
void handleClient(SOCKET clientSocket); //处理客户端
int Port;
WSADATA wsaData;
SOCKET serverSocket;
struct sockaddr_in serverAddr, clientAddr;
int addrLen = sizeof(clientAddr);
};
源文件(TCPServerTest.cpp)
cpp
#include "TCPServerTest.h"
TCPSever::TCPSever(int port):Port(port)
{
// 初始化 Windows Sockets,传入的参数MAKEWORD(2, 2)指定使用 Winsock 2.2 版本, 成功返回0,失败返回非0的错误值
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { //
std::cerr << "Error initializing Winsock\n";
}
else
{
createSocket();
serverPreparation();
startConnectClient();
}
}
TCPSever::~TCPSever()
{
closesocket(this->serverSocket); // 关闭与服务端的连接,关闭服务端套接字
WSACleanup(); //清理 Winsock 库资源
}
int TCPSever::createSocket()
{
// 创建套接字
this->serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (this->serverSocket == INVALID_SOCKET) {
std::cerr << "Error creating socket\n";
WSACleanup();
return 1;
}
}
int TCPSever::serverPreparation()
{
// 设置服务器地址信息
this->serverAddr.sin_family = AF_INET;
this->serverAddr.sin_addr.s_addr = INADDR_ANY;
this->serverAddr.sin_port = htons(Port);
// bind 函数 可以绑定套接字到指定地址和端口
if (bind(this->serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "Error binding socket\n";
closesocket(this->serverSocket);
WSACleanup();
return 1;
}
// listen函数:开始监听连接,参数10表示等待连接队列的最大长度为 10
if (listen(this->serverSocket, 10) == SOCKET_ERROR) {
std::cerr << "Error listening for connections\n";
closesocket(this->serverSocket);
WSACleanup();
return 1;
}
std::cout << "Server listening on port " << Port << std::endl;
}
int TCPSever::startConnectClient()
{
while (true) //循环接收数据
{
SOCKET newClientSocket;
// 接受客户端连接 accept函数:在循环中等待客户端连接,一旦有客户端连接,会返回一个新的套接字用于与该客户端通信,并获取客户端的地址信息
newClientSocket = accept(this->serverSocket, (struct sockaddr*)&clientAddr, &addrLen);
if (newClientSocket == INVALID_SOCKET) {
std::cerr << "Error accepting connection\n";
continue;
}
char clientIP[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN) == nullptr) {
std::cerr << "Error converting client IP address\n";
}
else {
std::cout << "Client: " << std::string(clientIP)+ ":" + std::to_string(ntohs(clientAddr.sin_port)) << std::endl;
std::thread clientThread(std::bind(&TCPSever::handleClient, this, newClientSocket)); //连上后和客户端进行数据
clientThread.detach();
}
}
}
void TCPSever::handleClient(SOCKET clientSocket)
{
const char* welcomeMessage = "Welcome to the server!\n";
send(clientSocket, welcomeMessage, strlen(welcomeMessage), 0); // 向客户端发送信息
char buffer[1024];
while (true)
{
int bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (bytesRead > 0) {
buffer[bytesRead] = '\0';
std::cout << "Received: " << buffer << std::endl;
std::string response = "received: " + std::string(buffer);
send(clientSocket, response.c_str(), response.length(), 0);
}
else {
std::cerr << "Connection closed by client\n";
closesocket(clientSocket);
return;
}
}
}
调用:
cpp
#include "TCPServerTest.h"
int main() {
TCPSever server = TCPSever(8888);
}
结果
可以看到服务端可以支持多个客户端连接
客户端代码见: