#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <process.h>
#include <vector>
#include <string>
#include <sstream>
#include <mutex>
#include <deque>
#include <chrono>
#include <algorithm>
#pragma comment(lib, "ws2_32.lib")
struct ClientInfo {
SOCKET socket;
std::string ip;
std::string nickname;
std::deque<std::chrono::steady_clock::time_point> messageTimes; // 防刷屏
int sensitiveWordCount = 0; // 敏感词计数
};
std::vector<ClientInfo> clients;
std::mutex clientsMutex;
// 敏感词列表
const std::vector<std::string> SENSITIVE_WORDS = {"78", "91", "蛋", "1241"};
// 替换字符串中的子串
std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
return str;
}
// 敏感词过滤函数
std::string FilterSensitiveWords(const std::string& msg) {
std::string result = msg;
for (const auto& word : SENSITIVE_WORDS) {
result = ReplaceAll(result, word, "***");
}
return result;
}
// 检查是否刷屏
bool CheckFlooding(ClientInfo& client) {
auto now = std::chrono::steady_clock::now();
client.messageTimes.push_back(now);
// 保留最近5秒的消息时间
while (!client.messageTimes.empty() && now - client.messageTimes.front() > std::chrono::seconds(5)) {
client.messageTimes.pop_front();
}
// 如果最近2秒内发送超过5条消息,视为刷屏
if (client.messageTimes.size() >= 5 &&
now - client.messageTimes[client.messageTimes.size() - 5] < std::chrono::seconds(2)) {
return true;
}
return false;
}
// 广播消息给所有客户端(可指定排除的 socket)
void BroadcastMessage(const std::string& msg, SOCKET sender = INVALID_SOCKET) {
std::lock_guard<std::mutex> lock(clientsMutex);
for (const auto& client : clients) {
if (client.socket != sender) {
send(client.socket, msg.c_str(), static_cast<int>(msg.size()), 0);
}
}
}
// 向指定 socket 发送消息
void SendToOne(SOCKET sock, const std::string& msg) {
send(sock, msg.c_str(), static_cast<int>(msg.size()), 0);
}
// 踢出指定昵称的用户
void KickUser(const std::string& nickname) {
std::lock_guard<std::mutex> lock(clientsMutex);
for (auto it = clients.begin(); it != clients.end(); ++it) {
if (it->nickname == nickname) {
std::string kickMsg = "[系统] 你已被管理员踢出聊天室";
std::string broadcastMsg = "[系统] 用户 " + nickname + " 被踢出聊天室";
std::cout << broadcastMsg << std::endl;
SendToOne(it->socket, kickMsg);
BroadcastMessage(broadcastMsg, it->socket);
closesocket(it->socket);
clients.erase(it);
return;
}
}
std::cout << "未找到昵称为 " << nickname << " 的用户。" << std::endl;
}
// 客户端处理线程
unsigned __stdcall ClientThread(void* lpParam) {
SOCKET clientSocket = (SOCKET)lpParam;
char buffer[1024];
int bytesReceived;
// 接收昵称
bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (bytesReceived <= 0) {
std::cerr << "接收昵称失败。" << std::endl;
closesocket(clientSocket);
return 0;
}
buffer[bytesReceived] = '\0';
std::string nickname(buffer);
// 获取IP地址
sockaddr_in clientAddr;
int addrLen = sizeof(clientAddr);
getpeername(clientSocket, (sockaddr*)&clientAddr, &addrLen);
std::string ip(inet_ntoa(clientAddr.sin_addr));
ClientInfo info;
info.socket = clientSocket;
info.ip = ip;
info.nickname = nickname;
{
std::lock_guard<std::mutex> lock(clientsMutex);
clients.push_back(info);
}
std::string joinMsg = "[系统] " + info.nickname + " 加入了聊天室";
std::string fullJoinMsg = "[系统][" + info.ip + "][" + info.nickname + "] " + "加入了聊天室";
std::cout << fullJoinMsg << std::endl;
BroadcastMessage(joinMsg);
while (true) {
bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
if (bytesReceived <= 0) {
break;
}
buffer[bytesReceived] = '\0';
std::string rawMsg(buffer);
std::string filteredMsg = FilterSensitiveWords(rawMsg);
// 查找当前用户信息
ClientInfo* currentUser = nullptr;
{
std::lock_guard<std::mutex> lock(clientsMutex);
for (auto& client : clients) {
if (client.socket == clientSocket) {
currentUser = &client;
break;
}
}
}
if (!currentUser) {
break;
}
// 防刷屏检测
if (CheckFlooding(*currentUser)) {
std::string kickMsg = "[系统] 你因刷屏被踢出聊天室";
std::string broadcastMsg = "[系统] 用户 " + currentUser->nickname + " 因刷屏被踢出聊天室";
std::cout << broadcastMsg << std::endl;
SendToOne(currentUser->socket, kickMsg);
BroadcastMessage(broadcastMsg, currentUser->socket);
KickUser(currentUser->nickname);
break;
}
// 检查是否包含敏感词
bool containsSensitiveWord = false;
for (const auto& word : SENSITIVE_WORDS) {
if (rawMsg.find(word) != std::string::npos) {
containsSensitiveWord = true;
break;
}
}
if (containsSensitiveWord) {
currentUser->sensitiveWordCount++;
std::string warnMsg = "[系统] 用户 " + currentUser->nickname + " 发送敏感词,已警告 " + std::to_string(currentUser->sensitiveWordCount) + " 次";
std::cout << warnMsg << std::endl;
BroadcastMessage(warnMsg);
if (currentUser->sensitiveWordCount >= 3) {
std::string kickMsg = "[系统] 你因连续发送敏感词被踢出聊天室";
std::string broadcastMsg = "[系统] 用户 " + currentUser->nickname + " 因连续发送敏感词被踢出聊天室";
std::cout << broadcastMsg << std::endl;
SendToOne(currentUser->socket, kickMsg);
BroadcastMessage(broadcastMsg, currentUser->socket);
KickUser(currentUser->nickname);
break;
}
continue; // 不广播该消息
}
// 正常广播消息
std::string clientMsg = "[" + currentUser->nickname + "] : " + filteredMsg;
std::string serverMsg = "[" + currentUser->ip + "][" + currentUser->nickname + "] : " + rawMsg;
std::cout << serverMsg << std::endl;
BroadcastMessage(clientMsg, clientSocket);
}
std::string leaveMsg = "[系统] " + nickname + " 离开了聊天室";
std::cout << leaveMsg << std::endl;
BroadcastMessage(leaveMsg);
{
std::lock_guard<std::mutex> lock(clientsMutex);
for (auto it = clients.begin(); it != clients.end(); ++it) {
if (it->socket == clientSocket) {
clients.erase(it);
break;
}
}
}
closesocket(clientSocket);
return 0;
}
// 服务器控制台输入线程
unsigned __stdcall ServerInputThread(void* lpParam) {
while (true) {
std::string input;
std::getline(std::cin, input);
if (input == "/list") {
std::lock_guard<std::mutex> lock(clientsMutex);
std::cout << "当前在线用户:" << std::endl;
for (const auto& client : clients) {
std::cout << "[" << client.ip << "] " << client.nickname << std::endl;
}
} else if (input.substr(0, 6) == "/kick ") {
if (input.size() > 6) {
KickUser(input.substr(6));
} else {
std::cout << "请输入要踢出的用户昵称。" << std::endl;
}
} else if (!input.empty()) {
std::string msg = "[管理员] : " + input;
std::cout << msg << std::endl;
BroadcastMessage(msg);
}
}
return 0;
}
int main() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup 失败。" << std::endl;
return 1;
}
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) {
std::cerr << "创建 socket 失败。" << std::endl;
WSACleanup();
return 1;
}
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(12345);
serverAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "绑定失败。" << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
std::cerr << "监听失败。" << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
std::cout << "【聊天室服务器已启动】" << std::endl;
std::cout << "请输入命令:" << std::endl;
std::cout << " /list 查看在线用户" << std::endl;
std::cout << " /kick 昵称 踢出用户" << std::endl;
_beginthreadex(nullptr, 0, ServerInputThread, nullptr, 0, nullptr);
while (true) {
SOCKET clientSocket = accept(serverSocket, nullptr, nullptr);
if (clientSocket != INVALID_SOCKET) {
_beginthreadex(nullptr, 0, ClientThread, (void*)clientSocket, 0, nullptr);
}
}
closesocket(serverSocket);
WSACleanup();
return 0;
}
修改这份代码,你自己找Bug,自行修改
最新发布