diff --git a/others/C++/chat_room/Makefile b/others/C++/chat_room/Makefile new file mode 100644 index 0000000..ed5438d --- /dev/null +++ b/others/C++/chat_room/Makefile @@ -0,0 +1,29 @@ +# FLTK聊天室项目Makefile +CXX = g++ +FLTK_CONFIG = fltk-config +CXXFLAGS = -std=c++17 -Wall -Iinclude +LDFLAGS = $(shell $(FLTK_CONFIG) --use-images --ldstaticflags) -static -lws2_32 + +SRC_DIR = src +OBJ_DIR = obj +INC_DIR = include + +SRCS = $(wildcard $(SRC_DIR)/*.cpp) +OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SRCS)) +TARGET = chat_room.exe + +all: $(OBJ_DIR) $(TARGET) + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + +$(TARGET): $(OBJS) + $(CXX) $^ -o $@ $(LDFLAGS) + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + rm -rf $(OBJ_DIR)/*.o $(TARGET) + +.PHONY: all clean diff --git a/others/C++/chat_room/include/Application.hpp b/others/C++/chat_room/include/Application.hpp new file mode 100644 index 0000000..8ffc6fd --- /dev/null +++ b/others/C++/chat_room/include/Application.hpp @@ -0,0 +1,17 @@ +#ifndef APPLICATION_HPP +#define APPLICATION_HPP + +class MainWindow; + +class Application { +public: + Application(int argc, char** argv); + ~Application(); + + int run(); + +private: + MainWindow* mainWindow; +}; + +#endif // APPLICATION_HPP diff --git a/others/C++/chat_room/include/ChatRoom.hpp b/others/C++/chat_room/include/ChatRoom.hpp new file mode 100644 index 0000000..b5e31f3 --- /dev/null +++ b/others/C++/chat_room/include/ChatRoom.hpp @@ -0,0 +1,36 @@ +#ifndef CHATROOM_HPP +#define CHATROOM_HPP + +#include "NetworkManager.hpp" +#include "User.hpp" +#include +#include +#include + +class ChatRoom { +public: + ChatRoom(); + ~ChatRoom(); + + bool createRoom(int port, const std::string& password = ""); + bool joinRoom(const std::string& ip, int port, const std::string& password); + void sendChatMessage(const std::string& message); + void addUser(const std::string& username); + void removeUser(const std::string& username); + + const std::vector& getMessages() const; + const std::vector>& getUsers() const; + std::string getCurrentUsername() const; + bool isConnected() const; + NetworkManager* getNetworkManager() const; + std::string getRoomPassword() const; + std::string getLocalIP() const; + +private: + std::unique_ptr network; + std::vector messages; + std::vector> users; + std::string roomPassword; +}; + +#endif // CHATROOM_HPP diff --git a/others/C++/chat_room/include/ChatWindow.hpp b/others/C++/chat_room/include/ChatWindow.hpp new file mode 100644 index 0000000..31b7321 --- /dev/null +++ b/others/C++/chat_room/include/ChatWindow.hpp @@ -0,0 +1,31 @@ +#ifndef CHATWINDOW_HPP +#define CHATWINDOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include "ChatRoom.hpp" + +class ChatWindow : public Fl_Window { +public: + ChatWindow(int w, int h, const char* title, std::shared_ptr chatRoom); + + void appendMessage(const std::string& message); + void updateUserList(); + +private: + std::shared_ptr chatRoom; + Fl_Text_Display* messageDisplay; + Fl_Text_Buffer* messageBuffer; + Fl_Input* messageInput; + Fl_Button* sendButton; + Fl_Browser* userList; + + static void onSendMessageCallback(Fl_Widget* w, void* data); +}; + +#endif // CHATWINDOW_HPP diff --git a/others/C++/chat_room/include/ConfigManager.hpp b/others/C++/chat_room/include/ConfigManager.hpp new file mode 100644 index 0000000..23376ab --- /dev/null +++ b/others/C++/chat_room/include/ConfigManager.hpp @@ -0,0 +1,22 @@ +#ifndef CONFIGMANAGER_HPP +#define CONFIGMANAGER_HPP + +#include +#include + +class ConfigManager { +public: + static ConfigManager& getInstance(); + + void loadConfig(const std::string& filename); + void saveConfig(const std::string& filename); + + std::string getValue(const std::string& key); + void setValue(const std::string& key, const std::string& value); + +private: + ConfigManager() = default; + std::map configMap; +}; + +#endif // CONFIGMANAGER_HPP diff --git a/others/C++/chat_room/include/Constants.hpp b/others/C++/chat_room/include/Constants.hpp new file mode 100644 index 0000000..03ee64b --- /dev/null +++ b/others/C++/chat_room/include/Constants.hpp @@ -0,0 +1,14 @@ +#ifndef CONSTANTS_HPP +#define CONSTANTS_HPP + +class Constants { +public: + static const int DEFAULT_PORT = 12345; + static const int MAIN_WINDOW_WIDTH = 400; + static const int MAIN_WINDOW_HEIGHT = 300; + static const int CHAT_WINDOW_WIDTH = 800; + static const int CHAT_WINDOW_HEIGHT = 600; + static const char MESSAGE_DELIMITER = '|'; +}; + +#endif // CONSTANTS_HPP diff --git a/others/C++/chat_room/include/MainWindow.hpp b/others/C++/chat_room/include/MainWindow.hpp new file mode 100644 index 0000000..767c5b8 --- /dev/null +++ b/others/C++/chat_room/include/MainWindow.hpp @@ -0,0 +1,45 @@ +#ifndef MAINWINDOW_HPP +#define MAINWINDOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include "ChatRoom.hpp" + +class MainWindow : public Fl_Window { +public: + MainWindow(int w, int h, const char* title); + ~MainWindow(); + + void showCreateRoomDialog(); + void showJoinRoomDialog(); + void showUsernameDialog(); + +private: + Fl_Button* createRoomBtn; + Fl_Button* joinRoomBtn; + Fl_Box* titleBox; + + Fl_Window* createRoomDialog; + Fl_Window* joinRoomDialog; + Fl_Window* usernameDialog; + + Fl_Input* portInput; + Fl_Input* ipInput; + Fl_Input* passwordInput; + Fl_Input* usernameInput; + + std::unique_ptr chatRoom; + + static void onCreateRoomCallback(Fl_Widget* w, void* data); + static void onJoinRoomCallback(Fl_Widget* w, void* data); + static void onCreateRoomConfirmCallback(Fl_Widget* w, void* data); + static void onJoinRoomConfirmCallback(Fl_Widget* w, void* data); + static void onUsernameConfirmCallback(Fl_Widget* w, void* data); +}; + +#endif // MAINWINDOW_HPP diff --git a/others/C++/chat_room/include/Message.hpp b/others/C++/chat_room/include/Message.hpp new file mode 100644 index 0000000..e5707cd --- /dev/null +++ b/others/C++/chat_room/include/Message.hpp @@ -0,0 +1,36 @@ +#ifndef MESSAGE_HPP +#define MESSAGE_HPP + +#include +#include +#include +#include "User.hpp" + +enum class MessageType { + NORMAL, // 普通消息 + SYSTEM, // 系统消息 + TEXT_MESSAGE, // 文本消息 + USER_JOIN, // 用户加入 + USER_LEAVE, // 用户离开 + AUTH // 认证消息 +}; + +class Message { +public: + Message(const std::shared_ptr& sender, + const std::string& content, + MessageType type = MessageType::NORMAL); + + const std::shared_ptr& getSender() const; + const std::string& getContent() const; + MessageType getType() const; + std::chrono::system_clock::time_point getTimestamp() const; + +private: + std::shared_ptr sender; + std::string content; + MessageType type; + std::chrono::system_clock::time_point timestamp; +}; + +#endif // MESSAGE_HPP diff --git a/others/C++/chat_room/include/MessageProtocol.hpp b/others/C++/chat_room/include/MessageProtocol.hpp new file mode 100644 index 0000000..1ba5784 --- /dev/null +++ b/others/C++/chat_room/include/MessageProtocol.hpp @@ -0,0 +1,15 @@ +#ifndef MESSAGEPROTOCOL_HPP +#define MESSAGEPROTOCOL_HPP + +#include +#include +#include "Message.hpp" + +class MessageProtocol { +public: + static std::string encodeMessage(const Message& message); + static Message decodeMessage(const std::string& data); + static bool validateMessage(const std::string& data); +}; + +#endif // MESSAGEPROTOCOL_HPP diff --git a/others/C++/chat_room/include/NetworkEventHandler.hpp b/others/C++/chat_room/include/NetworkEventHandler.hpp new file mode 100644 index 0000000..286b907 --- /dev/null +++ b/others/C++/chat_room/include/NetworkEventHandler.hpp @@ -0,0 +1,34 @@ +#ifndef NETWORKEVENTHANDLER_HPP +#define NETWORKEVENTHANDLER_HPP + +#include +#include "ChatRoom.hpp" +#include "MessageProtocol.hpp" + +#include +#include +#include +#include "ChatRoom.hpp" +#include "Message.hpp" + +class NetworkEventHandler { +public: + explicit NetworkEventHandler(std::shared_ptr chatRoom); + ~NetworkEventHandler(); + + void start(); + void stop(); + + void onConnected(); + void onMessageReceived(const std::string& message); + void onError(const std::string& error); + +private: + void heartbeatCheck(); + + std::shared_ptr chatRoom; + std::atomic running; + std::thread heartbeatThread; +}; + +#endif // NETWORKEVENTHANDLER_HPP diff --git a/others/C++/chat_room/include/NetworkManager.hpp b/others/C++/chat_room/include/NetworkManager.hpp new file mode 100644 index 0000000..5d6743c --- /dev/null +++ b/others/C++/chat_room/include/NetworkManager.hpp @@ -0,0 +1,38 @@ +#ifndef NETWORKMANAGER_HPP +#define NETWORKMANAGER_HPP + +#include +#include +#include +#include +#include +#include +#include + +class NetworkManager { +public: + using MessageCallback = std::function; + + NetworkManager(); + ~NetworkManager(); + + bool startServer(int port); + bool connectToServer(const std::string& ip, int port); + void sendMessage(const std::string& message); + std::vector receiveMessages(); + bool isConnected() const; + + void setMessageCallback(MessageCallback callback) { + messageCallback = callback; + } + +private: + SOCKET serverSocket; + SOCKET clientSocket; + std::vector clients; + std::mutex clientsMutex; + std::thread acceptThread; + MessageCallback messageCallback; +}; + +#endif // NETWORKMANAGER_HPP diff --git a/others/C++/chat_room/include/RoomInfo.hpp b/others/C++/chat_room/include/RoomInfo.hpp new file mode 100644 index 0000000..56ef3be --- /dev/null +++ b/others/C++/chat_room/include/RoomInfo.hpp @@ -0,0 +1,29 @@ +#ifndef ROOMINFO_HPP +#define ROOMINFO_HPP + +#include + +class RoomInfo { +public: + RoomInfo(const std::string& name, + const std::string& ip, + int port, + const std::string& password); + + const std::string& getName() const; + const std::string& getIpAddress() const; + int getPort() const; + const std::string& getPassword() const; + int getUserCount() const; + + void setUserCount(int count); + +private: + std::string name; + std::string ipAddress; + int port; + std::string password; + int userCount; +}; + +#endif // ROOMINFO_HPP diff --git a/others/C++/chat_room/include/StringUtils.hpp b/others/C++/chat_room/include/StringUtils.hpp new file mode 100644 index 0000000..04bacd0 --- /dev/null +++ b/others/C++/chat_room/include/StringUtils.hpp @@ -0,0 +1,15 @@ +#ifndef STRINGUTILS_HPP +#define STRINGUTILS_HPP + +#include +#include + +class StringUtils { +public: + static std::vector split(const std::string& str, char delimiter); + static std::string trim(const std::string& str); + static bool startsWith(const std::string& str, const std::string& prefix); + static bool endsWith(const std::string& str, const std::string& suffix); +}; + +#endif // STRINGUTILS_HPP diff --git a/others/C++/chat_room/include/User.hpp b/others/C++/chat_room/include/User.hpp new file mode 100644 index 0000000..6dac1fa --- /dev/null +++ b/others/C++/chat_room/include/User.hpp @@ -0,0 +1,22 @@ +#ifndef USER_HPP +#define USER_HPP + +#include +#include + +class User { +public: + User(const std::string& username, const std::string& ip); + + const std::string& getUsername() const; + const std::string& getIpAddress() const; + bool isActive() const; + void updateLastActive(); + +private: + std::string username; + std::string ipAddress; + std::chrono::system_clock::time_point lastActive; +}; + +#endif // USER_HPP diff --git a/others/C++/chat_room/include/UserManager.hpp b/others/C++/chat_room/include/UserManager.hpp new file mode 100644 index 0000000..45feeee --- /dev/null +++ b/others/C++/chat_room/include/UserManager.hpp @@ -0,0 +1,20 @@ +#ifndef USERMANAGER_HPP +#define USERMANAGER_HPP + +#include +#include +#include +#include "User.hpp" + +class UserManager { +public: + void addUser(std::shared_ptr user); + void removeUser(const std::string& username); + std::vector> getUsers() const; + std::shared_ptr findUser(const std::string& username) const; + +private: + std::vector> users; +}; + +#endif // USERMANAGER_HPP diff --git a/others/C++/chat_room/src/Application.cpp b/others/C++/chat_room/src/Application.cpp new file mode 100644 index 0000000..f0992f7 --- /dev/null +++ b/others/C++/chat_room/src/Application.cpp @@ -0,0 +1,46 @@ +#include "Application.hpp" +#include "MainWindow.hpp" +#include +#include + +Application::Application(int argc, char** argv) + : mainWindow(nullptr) { + try { + std::cout << "正在初始化应用程序..." << std::endl; + mainWindow = new MainWindow(400, 300, "局域网聊天室"); + std::cout << "主窗口创建成功" << std::endl; + } catch (const std::exception& e) { + std::cerr << "初始化失败: " << e.what() << std::endl; + } +} + +int Application::run() { + std::cout << "应用程序启动..." << std::endl; + + if (!mainWindow) { + std::cerr << "致命错误: 主窗口对象创建失败" << std::endl; + return 1; + } + + try { + std::cout << "初始化FLTK图形界面..." << std::endl; + Fl::scheme("gtk+"); + + std::cout << "显示主窗口..." << std::endl; + mainWindow->show(); + + std::cout << "窗口尺寸: " << mainWindow->w() << "x" << mainWindow->h() << std::endl; + std::cout << "进入主事件循环..." << std::endl; + + int ret = Fl::run(); + std::cout << "应用程序正常退出" << std::endl; + return ret; + } catch (const std::exception& e) { + std::cerr << "运行时错误: " << e.what() << std::endl; + return 1; + } +} + +Application::~Application() { + delete mainWindow; +} diff --git a/others/C++/chat_room/src/ChatRoom.cpp b/others/C++/chat_room/src/ChatRoom.cpp new file mode 100644 index 0000000..e474d75 --- /dev/null +++ b/others/C++/chat_room/src/ChatRoom.cpp @@ -0,0 +1,104 @@ +#include "ChatRoom.hpp" +#include "Message.hpp" +#include "MessageProtocol.hpp" +#include +#include +#include +#include + +ChatRoom::ChatRoom() : network(std::make_unique()) {} + +ChatRoom::~ChatRoom() {} + +bool ChatRoom::createRoom(int port, const std::string& password) { + roomPassword = password; + if (!network->startServer(port)) { + std::cerr << "Failed to create room on port " << port << std::endl; + return false; + } + return true; +} + +bool ChatRoom::joinRoom(const std::string& ip, int port, const std::string& password) { + if (!network->connectToServer(ip, port)) { + std::cerr << "Failed to connect to " << ip << ":" << port << std::endl; + return false; + } + + // 发送密码验证 + Message authMsg(nullptr, password, MessageType::AUTH); + network->sendMessage(MessageProtocol::encodeMessage(authMsg)); + + // 等待验证响应 + auto response = network->receiveMessages(); + if (response.empty() || response[0] != "AUTH_SUCCESS") { + return false; + } + + return true; +} + +std::string ChatRoom::getRoomPassword() const { + return roomPassword; +} + +std::string ChatRoom::getLocalIP() const { + char host[256]; + if (gethostname(host, sizeof(host)) == SOCKET_ERROR) { + return "127.0.0.1"; + } + + struct hostent* phe = gethostbyname(host); + if (phe == nullptr) { + return "127.0.0.1"; + } + + for (int i = 0; phe->h_addr_list[i] != nullptr; ++i) { + struct in_addr addr; + memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); + std::string ip = inet_ntoa(addr); + if (ip != "127.0.0.1") { + return ip; + } + } + + return "127.0.0.1"; +} + +void ChatRoom::sendChatMessage(const std::string& message) { + network->sendMessage(message); + messages.push_back(message); +} + +void ChatRoom::addUser(const std::string& username) { + auto user = std::make_shared(username, ""); + users.push_back(user); +} + +void ChatRoom::removeUser(const std::string& username) { + users.erase( + std::remove_if(users.begin(), users.end(), + [&username](const std::shared_ptr& user) { + return user->getUsername() == username; + }), + users.end()); +} + +const std::vector& ChatRoom::getMessages() const { + return messages; +} + +const std::vector>& ChatRoom::getUsers() const { + return users; +} + +std::string ChatRoom::getCurrentUsername() const { + if (!users.empty()) { + return users[0]->getUsername(); + } + return ""; +} + +NetworkManager* ChatRoom::getNetworkManager() const { + return network.get(); +} diff --git a/others/C++/chat_room/src/ChatWindow.cpp b/others/C++/chat_room/src/ChatWindow.cpp new file mode 100644 index 0000000..e43ead3 --- /dev/null +++ b/others/C++/chat_room/src/ChatWindow.cpp @@ -0,0 +1,50 @@ +#include "ChatWindow.hpp" +#include +#include + +ChatWindow::ChatWindow(int w, int h, const char* title, std::shared_ptr chatRoom) + : Fl_Window(w, h, title), chatRoom(chatRoom) { + + // 设置消息回调 + auto networkManager = chatRoom->getNetworkManager(); + networkManager->setMessageCallback([this](const std::string& msg) { + this->appendMessage(msg); + }); + + messageBuffer = new Fl_Text_Buffer(); + messageDisplay = new Fl_Text_Display(20, 20, w-220, h-70); + messageDisplay->buffer(messageBuffer); + + userList = new Fl_Browser(w-190, 20, 170, h-70); + userList->type(FL_MULTI_BROWSER); + + messageInput = new Fl_Input(20, h-40, w-100, 30); + sendButton = new Fl_Button(w-70, h-40, 60, 30, "发送"); + sendButton->callback(onSendMessageCallback, this); + + updateUserList(); +} + +void ChatWindow::appendMessage(const std::string& message) { + messageBuffer->append(message.c_str()); + messageBuffer->append("\n"); + messageDisplay->scroll(messageBuffer->count_lines(0, messageBuffer->length()), 0); +} + +void ChatWindow::updateUserList() { + userList->clear(); + for (const auto& user : chatRoom->getUsers()) { + userList->add(user->getUsername().c_str()); + } +} + +void ChatWindow::onSendMessageCallback(Fl_Widget* w, void* data) { + ChatWindow* win = static_cast(data); + std::string message = win->messageInput->value(); + + if (!message.empty()) { + win->chatRoom->sendChatMessage(message); + win->messageInput->value(""); + Fl::focus(win->messageInput); + } +} diff --git a/others/C++/chat_room/src/ConfigManager.cpp b/others/C++/chat_room/src/ConfigManager.cpp new file mode 100644 index 0000000..ee5db02 --- /dev/null +++ b/others/C++/chat_room/src/ConfigManager.cpp @@ -0,0 +1,38 @@ +#include "ConfigManager.hpp" +#include +#include + +ConfigManager& ConfigManager::getInstance() { + static ConfigManager instance; + return instance; +} + +void ConfigManager::loadConfig(const std::string& filename) { + std::ifstream file(filename); + std::string line; + + while (std::getline(file, line)) { + size_t pos = line.find('='); + if (pos != std::string::npos) { + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 1); + configMap[key] = value; + } + } +} + +void ConfigManager::saveConfig(const std::string& filename) { + std::ofstream file(filename); + + for (const auto& pair : configMap) { + file << pair.first << "=" << pair.second << "\n"; + } +} + +std::string ConfigManager::getValue(const std::string& key) { + return configMap[key]; +} + +void ConfigManager::setValue(const std::string& key, const std::string& value) { + configMap[key] = value; +} diff --git a/others/C++/chat_room/src/MainWindow.cpp b/others/C++/chat_room/src/MainWindow.cpp new file mode 100644 index 0000000..10cc53b --- /dev/null +++ b/others/C++/chat_room/src/MainWindow.cpp @@ -0,0 +1,140 @@ +#include "MainWindow.hpp" +#include "ChatWindow.hpp" +#include +#include +#include +#include + +MainWindow::MainWindow(int w, int h, const char* title) + : Fl_Window(w, h, title), chatRoom(std::make_unique()) { + + // 初始化窗口基础属性 + color(FL_WHITE); + begin(); + + try { + // 初始化界面组件 + titleBox = new Fl_Box(w/2-100, 20, 200, 30, "局域网聊天室"); + titleBox->box(FL_NO_BOX); + titleBox->labelfont(FL_BOLD); + titleBox->labelsize(20); + titleBox->labelcolor(FL_BLUE); + + createRoomBtn = new Fl_Button(w/2-100, 100, 200, 40, "创建房间"); + createRoomBtn->callback(onCreateRoomCallback, this); + + joinRoomBtn = new Fl_Button(w/2-100, 160, 200, 40, "加入房间"); + joinRoomBtn->callback(onJoinRoomCallback, this); + + // 初始化对话框指针 + createRoomDialog = nullptr; + joinRoomDialog = nullptr; + usernameDialog = nullptr; + + end(); + } catch (const std::exception& e) { + std::cerr << "窗口初始化失败: " << e.what() << std::endl; + throw; + } +} + +MainWindow::~MainWindow() { + if (createRoomDialog) delete createRoomDialog; + if (joinRoomDialog) delete joinRoomDialog; + if (usernameDialog) delete usernameDialog; +} + +void MainWindow::showCreateRoomDialog() { + if (createRoomDialog) return; + + createRoomDialog = new Fl_Window(300, 250, "创建房间"); + createRoomDialog->begin(); + + portInput = new Fl_Input(100, 30, 180, 30, "端口:"); + passwordInput = new Fl_Input(100, 70, 180, 30, "密码:"); + Fl_Button* confirmBtn = new Fl_Button(100, 120, 100, 30, "确认"); + confirmBtn->callback(onCreateRoomConfirmCallback, this); + + createRoomDialog->end(); + createRoomDialog->show(); +} + +void MainWindow::showJoinRoomDialog() { + if (joinRoomDialog) return; + + joinRoomDialog = new Fl_Window(300, 250, "加入房间"); + joinRoomDialog->begin(); + + ipInput = new Fl_Input(100, 30, 180, 30, "IP地址:"); + portInput = new Fl_Input(100, 70, 180, 30, "端口:"); + passwordInput = new Fl_Input(100, 110, 180, 30, "密码:"); + + Fl_Button* confirmBtn = new Fl_Button(100, 160, 100, 30, "确认"); + confirmBtn->callback(onJoinRoomConfirmCallback, this); + + joinRoomDialog->end(); + joinRoomDialog->show(); +} + +void MainWindow::showUsernameDialog() { + if (usernameDialog) return; + + usernameDialog = new Fl_Window(300, 150, "输入用户名"); + usernameDialog->begin(); + + usernameInput = new Fl_Input(100, 30, 180, 30, "用户名:"); + Fl_Button* confirmBtn = new Fl_Button(100, 80, 100, 30, "确认"); + confirmBtn->callback(onUsernameConfirmCallback, this); + + usernameDialog->end(); + usernameDialog->show(); +} + +void MainWindow::onCreateRoomCallback(Fl_Widget* w, void* data) { + MainWindow* win = static_cast(data); + win->showCreateRoomDialog(); +} + +void MainWindow::onJoinRoomCallback(Fl_Widget* w, void* data) { + MainWindow* win = static_cast(data); + win->showJoinRoomDialog(); +} + +void MainWindow::onCreateRoomConfirmCallback(Fl_Widget* w, void* data) { + MainWindow* win = static_cast(data); + int port = std::stoi(win->portInput->value()); + + std::string password = win->passwordInput->value(); + if (win->chatRoom->createRoom(port, password)) { + win->createRoomDialog->hide(); + win->showUsernameDialog(); + } +} + +void MainWindow::onJoinRoomConfirmCallback(Fl_Widget* w, void* data) { + MainWindow* win = static_cast(data); + std::string ip = win->ipInput->value(); + int port = std::stoi(win->portInput->value()); + + std::string password = win->passwordInput->value(); + if (win->chatRoom->joinRoom(ip, port, password)) { + win->joinRoomDialog->hide(); + win->showUsernameDialog(); + } +} + +void MainWindow::onUsernameConfirmCallback(Fl_Widget* w, void* data) { + MainWindow* win = static_cast(data); + std::string username = win->usernameInput->value(); + + win->chatRoom->addUser(username); + win->usernameDialog->hide(); + + // 创建并显示聊天窗口 + auto chatRoomPtr = std::shared_ptr(win->chatRoom.release()); + auto chatWindow = new ChatWindow(800, 600, "聊天室", chatRoomPtr); + chatWindow->show(); + + // 隐藏主窗口 + win->hide(); +} diff --git a/others/C++/chat_room/src/Message.cpp b/others/C++/chat_room/src/Message.cpp new file mode 100644 index 0000000..496a882 --- /dev/null +++ b/others/C++/chat_room/src/Message.cpp @@ -0,0 +1,24 @@ +#include "Message.hpp" + +Message::Message(const std::shared_ptr& sender, + const std::string& content, + MessageType type) + : sender(sender), content(content), type(type) { + timestamp = std::chrono::system_clock::now(); +} + +const std::shared_ptr& Message::getSender() const { + return sender; +} + +const std::string& Message::getContent() const { + return content; +} + +MessageType Message::getType() const { + return type; +} + +std::chrono::system_clock::time_point Message::getTimestamp() const { + return timestamp; +} diff --git a/others/C++/chat_room/src/MessageProtocol.cpp b/others/C++/chat_room/src/MessageProtocol.cpp new file mode 100644 index 0000000..27d032d --- /dev/null +++ b/others/C++/chat_room/src/MessageProtocol.cpp @@ -0,0 +1,46 @@ +#include "MessageProtocol.hpp" +#include +#include "StringUtils.hpp" + +std::string MessageProtocol::encodeMessage(const Message& message) { + std::ostringstream oss; + oss << static_cast(message.getType()) << "|" + << (message.getSender() ? message.getSender()->getUsername() : "") << "|" + << message.getContent(); + return oss.str(); +} + +Message MessageProtocol::decodeMessage(const std::string& data) { + auto parts = StringUtils::split(data, '|'); + if (parts.size() < 3) { + throw std::runtime_error("Invalid message format"); + } + + std::shared_ptr sender = nullptr; + if (!parts[1].empty()) { + sender = std::make_shared(parts[1], ""); + } + + MessageType type = static_cast(std::stoi(parts[0])); + std::string content = parts[2]; + + if (type == MessageType::USER_JOIN || type == MessageType::USER_LEAVE) { + content = ""; + } + + return Message(sender, content, type); +} + +bool MessageProtocol::validateMessage(const std::string& data) { + auto parts = StringUtils::split(data, '|'); + if (parts.size() < 3) { + return false; + } + + try { + int type = std::stoi(parts[0]); + return type >= 0 && type <= 5; // 检查消息类型是否有效(0-5对应MessageType枚举) + } catch (...) { + return false; + } +} diff --git a/others/C++/chat_room/src/NetworkEventHandler.cpp b/others/C++/chat_room/src/NetworkEventHandler.cpp new file mode 100644 index 0000000..3c6ec18 --- /dev/null +++ b/others/C++/chat_room/src/NetworkEventHandler.cpp @@ -0,0 +1,81 @@ +#include "NetworkEventHandler.hpp" +#include +#include +#include "MessageProtocol.hpp" + +NetworkEventHandler::NetworkEventHandler(std::shared_ptr chatRoom) + : chatRoom(chatRoom), running(false) {} + +NetworkEventHandler::~NetworkEventHandler() { + stop(); +} + +void NetworkEventHandler::start() { + if (running) return; + running = true; + heartbeatThread = std::thread(&NetworkEventHandler::heartbeatCheck, this); +} + +void NetworkEventHandler::stop() { + running = false; + if (heartbeatThread.joinable()) { + heartbeatThread.join(); + } +} + +void NetworkEventHandler::onConnected() { + auto users = chatRoom->getUsers(); + if (!users.empty()) { + auto user = users[0]; // 假设第一个用户是当前用户 + Message joinMsg(user, "", MessageType::USER_JOIN); + chatRoom->sendChatMessage(MessageProtocol::encodeMessage(joinMsg)); + } +} + +void NetworkEventHandler::onMessageReceived(const std::string& message) { + try { + if (!MessageProtocol::validateMessage(message)) { + return; + } + + auto msg = MessageProtocol::decodeMessage(message); + switch (msg.getType()) { + case MessageType::TEXT_MESSAGE: + chatRoom->sendChatMessage(msg.getSender()->getUsername() + ": " + msg.getContent()); + break; + case MessageType::USER_JOIN: + chatRoom->sendChatMessage(msg.getSender()->getUsername() + " 加入了聊天室"); + break; + case MessageType::USER_LEAVE: + chatRoom->sendChatMessage(msg.getSender()->getUsername() + " 离开了聊天室"); + break; + case MessageType::SYSTEM: + chatRoom->sendChatMessage("[系统] " + msg.getContent()); + break; + case MessageType::AUTH: + if (msg.getContent() == chatRoom->getRoomPassword()) { + chatRoom->sendChatMessage("AUTH_SUCCESS"); + } else { + chatRoom->sendChatMessage("AUTH_FAILED"); + } + break; + default: + break; + } + } catch (const std::exception& e) { + std::cerr << "消息处理错误: " << e.what() << std::endl; + } +} + +void NetworkEventHandler::onError(const std::string& error) { + chatRoom->sendChatMessage("[网络错误] " + error); +} + +void NetworkEventHandler::heartbeatCheck() { + while (running) { + std::this_thread::sleep_for(std::chrono::seconds(30)); + if (!chatRoom->getNetworkManager()->isConnected()) { + onError("连接已断开"); + } + } +} diff --git a/others/C++/chat_room/src/NetworkManager.cpp b/others/C++/chat_room/src/NetworkManager.cpp new file mode 100644 index 0000000..811930e --- /dev/null +++ b/others/C++/chat_room/src/NetworkManager.cpp @@ -0,0 +1,138 @@ +#include "NetworkManager.hpp" +#include +#include +#include +#include + +#pragma comment(lib, "ws2_32.lib") + +NetworkManager::NetworkManager() : serverSocket(INVALID_SOCKET), clientSocket(INVALID_SOCKET) { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + throw std::runtime_error("WSAStartup failed"); + } +} + +NetworkManager::~NetworkManager() { + if (serverSocket != INVALID_SOCKET) { + closesocket(serverSocket); + } + if (clientSocket != INVALID_SOCKET) { + closesocket(clientSocket); + } + WSACleanup(); +} + +bool NetworkManager::startServer(int port) { + serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (serverSocket == INVALID_SOCKET) { + return false; + } + + sockaddr_in serverAddr; + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网络接口 + serverAddr.sin_port = htons(port); + + // 设置SO_REUSEADDR选项 + int opt = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); + + if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { + std::cerr << "绑定端口失败: " << WSAGetLastError() << std::endl; + closesocket(serverSocket); + return false; + } + + if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) { + std::cerr << "监听失败: " << WSAGetLastError() << std::endl; + closesocket(serverSocket); + return false; + } + + // 启动接收线程 + acceptThread = std::thread([this]() { + while (true) { + sockaddr_in clientAddr; + int addrLen = sizeof(clientAddr); + SOCKET newClient = accept(serverSocket, (sockaddr*)&clientAddr, &addrLen); + if (newClient == INVALID_SOCKET) break; + + std::lock_guard lock(clientsMutex); + clients.push_back(newClient); + } + }); + + std::cout << "服务器已启动,监听端口: " << port << std::endl; + return true; +} + +bool NetworkManager::connectToServer(const std::string& ip, int port) { + clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (clientSocket == INVALID_SOCKET) { + return false; + } + + sockaddr_in serverAddr; + serverAddr.sin_family = AF_INET; + inet_pton(AF_INET, ip.c_str(), &serverAddr.sin_addr); + serverAddr.sin_port = htons(port); + + if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { + std::cerr << "连接服务器失败: " << WSAGetLastError() << std::endl; + closesocket(clientSocket); + return false; + } + + std::cout << "已连接到服务器: " << ip << ":" << port << std::endl; + + return true; +} + +void NetworkManager::sendMessage(const std::string& message) { + // 服务器模式:广播给所有客户端 + if (serverSocket != INVALID_SOCKET) { + std::lock_guard lock(clientsMutex); + for (auto client : clients) { + send(client, message.c_str(), message.size(), 0); + } + } + // 客户端模式:发送到服务器 + else if (clientSocket != INVALID_SOCKET) { + send(clientSocket, message.c_str(), message.size(), 0); + } + + // 本地回显 + if (messageCallback) { + messageCallback(message); + } +} + +std::vector NetworkManager::receiveMessages() { + std::vector messages; + if (clientSocket == INVALID_SOCKET) { + return messages; + } + + fd_set readSet; + FD_ZERO(&readSet); + FD_SET(clientSocket, &readSet); + + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + if (select(clientSocket + 1, &readSet, nullptr, nullptr, &timeout) > 0) { + char buffer[1024]; + int bytesReceived; + while ((bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0)) > 0) { + messages.emplace_back(buffer, bytesReceived); + } + } + + return messages; +} + +bool NetworkManager::isConnected() const { + return clientSocket != INVALID_SOCKET; +} diff --git a/others/C++/chat_room/src/RoomInfo.cpp b/others/C++/chat_room/src/RoomInfo.cpp new file mode 100644 index 0000000..8e53d98 --- /dev/null +++ b/others/C++/chat_room/src/RoomInfo.cpp @@ -0,0 +1,31 @@ +#include "RoomInfo.hpp" + +RoomInfo::RoomInfo(const std::string& name, + const std::string& ip, + int port, + const std::string& password) + : name(name), ipAddress(ip), port(port), password(password), userCount(0) {} + +const std::string& RoomInfo::getName() const { + return name; +} + +const std::string& RoomInfo::getIpAddress() const { + return ipAddress; +} + +int RoomInfo::getPort() const { + return port; +} + +const std::string& RoomInfo::getPassword() const { + return password; +} + +int RoomInfo::getUserCount() const { + return userCount; +} + +void RoomInfo::setUserCount(int count) { + userCount = count; +} diff --git a/others/C++/chat_room/src/StringUtils.cpp b/others/C++/chat_room/src/StringUtils.cpp new file mode 100644 index 0000000..18ccac5 --- /dev/null +++ b/others/C++/chat_room/src/StringUtils.cpp @@ -0,0 +1,34 @@ +#include "StringUtils.hpp" +#include +#include + +std::vector StringUtils::split(const std::string& str, char delimiter) { + std::vector tokens; + size_t start = 0; + size_t end = str.find(delimiter); + + while (end != std::string::npos) { + tokens.push_back(str.substr(start, end - start)); + start = end + 1; + end = str.find(delimiter, start); + } + + tokens.push_back(str.substr(start)); + return tokens; +} + +std::string StringUtils::trim(const std::string& str) { + auto start = std::find_if_not(str.begin(), str.end(), [](int c) { return std::isspace(c); }); + auto end = std::find_if_not(str.rbegin(), str.rend(), [](int c) { return std::isspace(c); }).base(); + return (start >= end) ? "" : std::string(start, end); +} + +bool StringUtils::startsWith(const std::string& str, const std::string& prefix) { + return str.size() >= prefix.size() && + str.compare(0, prefix.size(), prefix) == 0; +} + +bool StringUtils::endsWith(const std::string& str, const std::string& suffix) { + return str.size() >= suffix.size() && + str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; +} diff --git a/others/C++/chat_room/src/User.cpp b/others/C++/chat_room/src/User.cpp new file mode 100644 index 0000000..e72fe4f --- /dev/null +++ b/others/C++/chat_room/src/User.cpp @@ -0,0 +1,25 @@ +#include "User.hpp" +#include + +User::User(const std::string& username, const std::string& ip) + : username(username), ipAddress(ip) { + updateLastActive(); +} + +const std::string& User::getUsername() const { + return username; +} + +const std::string& User::getIpAddress() const { + return ipAddress; +} + +bool User::isActive() const { + auto now = std::chrono::system_clock::now(); + auto duration = std::chrono::duration_cast(now - lastActive); + return duration.count() < 5; // 5分钟内活跃 +} + +void User::updateLastActive() { + lastActive = std::chrono::system_clock::now(); +} diff --git a/others/C++/chat_room/src/UserManager.cpp b/others/C++/chat_room/src/UserManager.cpp new file mode 100644 index 0000000..438a0c3 --- /dev/null +++ b/others/C++/chat_room/src/UserManager.cpp @@ -0,0 +1,26 @@ +#include "UserManager.hpp" + +void UserManager::addUser(std::shared_ptr user) { + users.push_back(user); +} + +void UserManager::removeUser(const std::string& username) { + users.erase( + std::remove_if(users.begin(), users.end(), + [&username](const std::shared_ptr& user) { + return user->getUsername() == username; + }), + users.end()); +} + +std::vector> UserManager::getUsers() const { + return users; +} + +std::shared_ptr UserManager::findUser(const std::string& username) const { + auto it = std::find_if(users.begin(), users.end(), + [&username](const std::shared_ptr& user) { + return user->getUsername() == username; + }); + return it != users.end() ? *it : nullptr; +} diff --git a/others/C++/chat_room/src/main.cpp b/others/C++/chat_room/src/main.cpp new file mode 100644 index 0000000..b4de56d --- /dev/null +++ b/others/C++/chat_room/src/main.cpp @@ -0,0 +1,6 @@ +#include "Application.hpp" + +int main(int argc, char** argv) { + Application app(argc, argv); + return app.run(); +}