开发中三个小经验:strncpy,list::front,string::c_str

开发中三个小经验:strncpy,list::front,string::c_str - 常高伟的专栏 - 博客频道 - youkuaiyun.com

开发中三个小经验:strncpy,list::front,string::c_str

分类: 软件设计 587人阅读 评论(1) 收藏 举报

最近一段时间开发非常忙,优快云很少光顾。开发中有些经验教训,这里和大家分享。

 

1、strncpy的使用

一直以来,在我记忆中,使用strncpy只要注意三点就可以了:

1)源字符串结束时,停止拷贝。

2)拷贝n个后,停止拷贝。

3)strncpy(dst_buf, src_buf, sizeof(dst_buf) - 1),n设置为目标缓存大小减1,防止丢失字符串结尾符。

开发中遇到的一个问题,由于N值设计的不合理,导致内存越界。原因是:strncpy复制源字符串后,会把剩下的字符串,也就是n-sizeof(源字符串)设置为0。

 

2、list::front

受c接口的影响,一直以为如果list为空,则list::front会返回NULL。事实却非如此:无法根据list::front的返回值判断list是否为空,即便是为空,他返回的也可能不是NULL。所以,调用list::front之前,需要使用list::empty来判断是否为空。

 

3、string::c_str

函数中定义一个string str; str.c_str返回的地址z在堆中,而不是在栈中。但是,不可以return str.c_str,因为函数返回后,str就被释放,str.c_str返回的地址也被系统回收。

#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,自行修改
最新发布
09-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值