//服务器
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <map>
#include <mutex>
#include <sstream>
#include <fstream>
#include <set>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT 8888
#define BUFFER_SIZE 1024
// 控制台句柄
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// 设置颜色
void setColor(int color) {
SetConsoleTextAttribute(hConsole, color);
}
void resetColor() {
SetConsoleTextAttribute(hConsole, 7);
}
// 敏感词集合和互斥锁
std::set<std::string> sensitiveWords;
std::mutex wordMutex;
// 客户端连接信息
std::map<SOCKET, std::string> clientSockets; // socket -> username
std::map<std::string, SOCKET> usernameToSocket; // username -> socket
std::mutex clientMutex;
// 获取客户端 IP 地址
std::string getClientIP(SOCKET sock) {
sockaddr_in addr;
int addrLen = sizeof(addr);
if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) {
const char* ip = inet_ntoa(addr.sin_addr);
return std::string(ip ? ip : "未知IP");
}
return "未知IP";
}
// 广播消息(替换敏感词)
void broadcastMessage(const std::string& sender, const std::string& message, SOCKET excludeSocket = INVALID_SOCKET) {
std::lock_guard<std::mutex> lock(clientMutex);
std::string fullMsg = "[" + sender + "] " + message;
setColor(11); std::cout << fullMsg << std::endl; resetColor();
for (const auto& pair : clientSockets) {
if (pair.first != excludeSocket) {
send(pair.first, fullMsg.c_str(), fullMsg.size() + 1, 0);
}
}
}
// 检查用户名是否合法
bool isValidUsername(const std::string& username) {
if (username.empty()) {
return false;
}
// 检查是否重复
std::lock_guard<std::mutex> lockClient(clientMutex);
if (usernameToSocket.find(username) != usernameToSocket.end()) {
return false;
}
return true;
}
// 踢出用户
void kickUser(const std::string& username) {
std::lock_guard<std::mutex> lock(clientMutex);
auto it = usernameToSocket.find(username);
if (it != usernameToSocket.end()) {
SOCKET sock = it->second;
std::string msg = "[系统消息] 你已被管理员踢出聊天室";
send(sock, msg.c_str(), msg.size() + 1, 0);
setColor(12); std::cout << "[踢出] 用户 " << username << " 被管理员踢出" << std::endl; resetColor();
broadcastMessage("系统消息", username + " 被管理员踢出聊天室");
usernameToSocket.erase(username);
clientSockets.erase(sock);
closesocket(sock);
} else {
setColor(12); std::cout << "[错误] 用户 " << username << " 不存在或已离线" << std::endl; resetColor();
}
}
// 查看在线用户
void listUsers() {
std::lock_guard<std::mutex> lock(clientMutex);
setColor(11); std::cout << "[在线用户] 当前在线用户列表:" << std::endl; resetColor();
for (const auto& pair : clientSockets) {
setColor(10); std::cout << " - " << pair.second << " (" << getClientIP(pair.first) << ")" << std::endl; resetColor();
}
}
// 客户端处理线程
DWORD WINAPI handleClient(LPVOID lpParam) {
SOCKET clientSocket = (SOCKET)lpParam;
char buffer[BUFFER_SIZE];
// 接收用户名
int bytes = recv(clientSocket, buffer, BUFFER_SIZE, 0);
if (bytes <= 0) {
setColor(12); std::cerr << "[错误] 无法接收用户名" << std::endl; resetColor();
closesocket(clientSocket);
return 0;
}
std::string username(buffer);
// 验证用户名是否合法
if (!isValidUsername(username)) {
std::string errorMsg;
if (username.empty()) {
errorMsg = "[错误] 用户名不能为空,请重新连接并输入用户名";
} else {
errorMsg = "[错误] 用户名非法(可能重复),请重新连接并使用其他用户名";
}
send(clientSocket, errorMsg.c_str(), errorMsg.size() + 1, 0);
closesocket(clientSocket);
return 0;
}
// 存储用户名
{
std::lock_guard<std::mutex> lock(clientMutex);
clientSockets[clientSocket] = username;
usernameToSocket[username] = clientSocket;
}
setColor(10); std::cout << "[连接] 用户 " << username << " (" << getClientIP(clientSocket) << ") 已加入游戏" << std::endl; resetColor();
broadcastMessage("系统消息", username + " 加入了聊天室。");
// 循环接收消息
while ((bytes = recv(clientSocket, buffer, BUFFER_SIZE, 0)) > 0) {
buffer[bytes] = '\0'; // 确保字符串结尾
std::string msg(buffer);
// 先显示完整消息
setColor(11); std::cout << "[" << username << "] " << msg << std::endl; resetColor();
broadcastMessage(username, msg, clientSocket);
ZeroMemory(buffer, BUFFER_SIZE);
}
// 客户端断开连接
{
std::lock_guard<std::mutex> lock(clientMutex);
usernameToSocket.erase(username);
clientSockets.erase(clientSocket);
}
setColor(10); std::cout << "[断开] 用户 " << username << " (" << getClientIP(clientSocket) << ") 离开了游戏" << std::endl; resetColor();
broadcastMessage("系统消息", username + " 离开了游戏。");
closesocket(clientSocket);
return 0;
}
// 接收新连接线程
DWORD WINAPI listenForConnections(LPVOID lpParam) {
SOCKET serverSocket = (SOCKET)lpParam;
while (true) {
SOCKET clientSocket = accept(serverSocket, NULL, NULL);
if (clientSocket == INVALID_SOCKET) {
setColor(12); std::cerr << "[错误] accept 失败" << std::endl; resetColor();
continue;
}
setColor(14); std::cout << "[新连接] 客户端已连接" << std::endl; resetColor();
CreateThread(NULL, 0, handleClient, (LPVOID)clientSocket, 0, NULL);
}
return 0;
}
// 控制台输入处理线程
DWORD WINAPI handleConsoleInput(LPVOID lpParam) {
std::string input;
while (std::getline(std::cin, input)) {
if (input.empty()) continue;
if (input == "/list") {
listUsers();
} else if (input.find("/kick") == 0) {
std::istringstream iss(input);
std::string cmd, username;
iss >> cmd >> username;
if (!username.empty()) {
kickUser(username);
} else {
setColor(14); std::cout << "[命令] 用法: /kick <用户名>" << std::endl; resetColor();
}
} else if (input == "/exit") {
setColor(12); std::cout << "[关闭] 服务器即将关闭..." << std::endl; resetColor();
exit(0);
} else {
setColor(14); std::cout << "[命令] 未知命令: " << input << std::endl; resetColor();
}
}
return 0;
}
int main() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
setColor(12); std::cerr << "[错误] WSAStartup 失败" << std::endl; resetColor();
return 1;
}
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) {
setColor(12); std::cerr << "[错误] 创建 socket 失败" << std::endl; resetColor();
WSACleanup();
return 1;
}
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(PORT);
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
setColor(12); std::cerr << "[错误] 绑定失败" << std::endl; resetColor();
closesocket(serverSocket);
WSACleanup();
return 1;
}
if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
setColor(12); std::cerr << "[错误] 监听失败" << std::endl; resetColor();
closesocket(serverSocket);
WSACleanup();
return 1;
}
setColor(11); std::cout << "[启动] 服务器已启动,正在监听端口 " << PORT << "..." << std::endl; resetColor();
CreateThread(NULL, 0, listenForConnections, (LPVOID)serverSocket, 0, NULL);
CreateThread(NULL, 0, handleConsoleInput, NULL, 0, NULL);
// 主线程等待
while (true) {
Sleep(1000);
}
closesocket(serverSocket);
WSACleanup();
return 0;
}
//客户端
#include <iostream>
#include <winsock2.h>
#include <windows.h> // Sleep(), SetConsoleTextAttribute
#include <thread>
#include <string>
#pragma comment(lib, "ws2_32.lib")
#define PORT 8888
#define BUFFER_SIZE 1024
SOCKET clientSocket;
// 定义控制台句柄
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// 设置控制台颜色函数
void setSystemMessageColor() {
SetConsoleTextAttribute(hConsole, 11); // 蓝绿色
}
void setUserMessageColor() {
SetConsoleTextAttribute(hConsole, 10); // 亮绿色
}
void setDefaultColor() {
SetConsoleTextAttribute(hConsole, 7); // 默认白灰色
}
void setInputPromptColor() {
SetConsoleTextAttribute(hConsole, 15); // 亮白色
}
// 接收消息线程函数
DWORD WINAPI ReceiveMessages(LPVOID lpParam) {
char buffer[BUFFER_SIZE];
while (true) {
ZeroMemory(buffer, BUFFER_SIZE);
int bytes = recv(clientSocket, buffer, BUFFER_SIZE, 0);
if (bytes <= 0) {
setDefaultColor();
std::cout << "[断开] 服务器已断开连接" << std::endl;
closesocket(clientSocket);
WSACleanup();
Sleep(2000); // 等待2秒后退出
exit(0);
}
std::string message(buffer);
/*
// 判断消息类型并设置颜色
if (message.find("[系统消息]") == 0) {
setSystemMessageColor();
} else if (message.size() > 2 && message[0] == '[') {
size_t endBracket = message.find(']');
if (endBracket != std::string::npos && endBracket < message.size() - 1) {
std::string prefix = message.substr(0, endBracket + 1);
if (prefix != "[系统消息]") {
setUserMessageColor();
}
}
} else {
setUserMessageColor(); // 无前缀的消息也作为用户消息处理
}
*/
std::cout << message << std::endl;
}
return 0;
}
// 主函数
int main() {
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
std::cerr << "[错误] 无法初始化 Winsock" << std::endl;
return 1;
}
clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "[错误] 无法创建 socket" << std::endl;
WSACleanup();
return 1;
}
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.5");
if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
int error = WSAGetLastError();
if (error == WSAEADDRNOTAVAIL) {
std::cerr << "[错误] IP地址无效,请检查IP是否正确。" << std::endl;
} else if (error == WSAECONNREFUSED) {
std::cerr << "[错误] 服务器未启动或拒绝连接,请确认服务器正在运行。" << std::endl;
} else {
std::cerr << "[错误] 连接服务器失败,错误代码:" << error << std::endl;
}
closesocket(clientSocket);
WSACleanup();
Sleep(2000);
return 1;
}
std::string username;
std::cout << "请输入用户名: ";
std::cin >> username;
send(clientSocket, username.c_str(), username.size() + 1, 0); // 发送用户名
// 创建接收消息的线程
HANDLE hThread = CreateThread(NULL, 0, ReceiveMessages, NULL, 0, NULL);
if (hThread == NULL) {
std::cerr << "[错误] 创建线程失败" << std::endl;
closesocket(clientSocket);
WSACleanup();
return 1;
}
// 设置输入提示
setInputPromptColor();
std::cout << ">> ";
setDefaultColor();
std::string message;
while (std::getline(std::cin, message)) {
if (message.empty()) continue;
send(clientSocket, message.c_str(), message.size() + 1, 0);
setInputPromptColor();
std::cout << ">> ";
setDefaultColor();
}
CloseHandle(hThread);
closesocket(clientSocket);
WSACleanup();
return 0;
}
基于此程序,把它改成一个匹配制游戏,新用户连接后,服务器向客户端发送欢迎界面,得到答复后将该用户加入匹配队列,当队列里有两人时,弹出两人,新建一个房间线程(两人对战),游戏逻辑空着不写,进程结束后将两人返回欢迎界面。