From 92e571305c418c11e7b95e7da78511f076bbf600 Mon Sep 17 00:00:00 2001 From: Cormac <1350363682@qq.com> Date: Sat, 2 Nov 2024 20:18:26 +0800 Subject: [PATCH] 1.2 --- Server.cpp | 56 +++++++++++++---------------- TFTP.vcxproj | 8 +++-- UI.cpp | 44 ++++++++++++++++------- UI.h | 1 + api.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++- api.h | 8 ++++- shared.h | 4 ++- 7 files changed, 171 insertions(+), 49 deletions(-) diff --git a/Server.cpp b/Server.cpp index e771725..21d45a7 100644 --- a/Server.cpp +++ b/Server.cpp @@ -1,8 +1,9 @@ #define _CRT_SECURE_NO_WARNINGS +#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 #include #include #include -#include +#include // 字符串流 #include // Windows套接字库 #include // Windows套接字库 #include // 时间库 @@ -79,7 +80,7 @@ class TFTPHeader { char data[BUFFER_SIZE - 4]; // 数据 TFTPHeader(uint16_t op = 0, uint16_t blockOrError = 0) : opcode(op), blockOrErrorCode(blockOrError) { - memset(data, 0, sizeof(data)); + memset(data, 0, sizeof(data)); } void setData(const std::string& str) { @@ -138,18 +139,16 @@ class TFTPServer { return; } - serverAddr.sin_family = AF_INET; - serverAddr.sin_port = htons(PORT); - serverAddr.sin_addr.s_addr = INADDR_ANY; - - if (bind(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { - logMessage("绑定失败,请检查"+ std::to_string(PORT) + "端口是否已被占用", sockaddr_in{}, 1); + // 绑定监听套接字 + std::string BindResult = BindSocketToInterface(&sock, interfaces[selectedInterfaceIdx].second.find(":") != std::string::npos, interfaces[selectedInterfaceIdx].second , PORT); // 绑定到特定的网络接口,端口为指定值 + if (BindResult != "OK") { + logMessage("监听套接字绑定失败:"+ BindResult, sockaddr_in{}, 1); closesocket(sock); WSACleanup(); return; } - logMessage("TFTP服务器启动,正在监听"+ std::to_string(PORT) +"端口...", sockaddr_in{}, 2); + logMessage("服务器启动成功,正在监听"+ interfaces[selectedInterfaceIdx].second + " : " + std::to_string(PORT), sockaddr_in{}, 2); ServerRootDirectory = mainFrame->server_dir->GetPath().ToStdString(); @@ -180,10 +179,11 @@ class TFTPServer { logMessage("服务器已关闭", sockaddr_in{}, 2); if (is_console_mode) { is_console_mode = false; - running = false; } + running = false; closesocket(sock); WSACleanup(); + g_mainFrame->Server_Switch->SetSelection(1); } // 监听套接字运行时 @@ -299,15 +299,12 @@ class TFTPServer { u_long blockmode = 1; ioctlsocket(dataSock, FIONBIO, &blockmode); - // 绑定传输套接字到动态分配的端口 - sockaddr_in dataAddr = {}; - dataAddr.sin_family = AF_INET; - dataAddr.sin_port = 0; // 动态分配,端口设置为0 - dataAddr.sin_addr.s_addr = INADDR_ANY; - if (bind(dataSock, (sockaddr*)&dataAddr, sizeof(dataAddr)) == SOCKET_ERROR) { - logMessage("绑定数据传输套接字失败", clientAddr, 1); - closesocket(dataSock); - currentThreadCount--; + // 绑定传输套接字 + std::string BindResult = BindSocketToInterface(&dataSock, interfaces[selectedInterfaceIdx].second.find(":") != std::string::npos , interfaces[selectedInterfaceIdx].second , 0);// 绑定到特定的网络接口,端口随机 + if (BindResult != "OK") { + logMessage("RQQ传输套接字绑定失败:" + BindResult, sockaddr_in{}, 1); + closesocket(sock); + WSACleanup(); return; } @@ -520,15 +517,12 @@ class TFTPServer { u_long blockmode = 1; ioctlsocket(dataSock, FIONBIO, &blockmode); - // 绑定到动态分配的端口 - sockaddr_in dataAddr = {}; - dataAddr.sin_family = AF_INET; - dataAddr.sin_port = 0; // 动态分配端口 - dataAddr.sin_addr.s_addr = INADDR_ANY; - if (bind(dataSock, (sockaddr*)&dataAddr, sizeof(dataAddr)) == SOCKET_ERROR) { - logMessage("绑定数据传输套接字失败", clientAddr, 1); - closesocket(dataSock); - currentThreadCount--; + // 绑定传输套接字 + std::string BindResult = BindSocketToInterface(&dataSock, interfaces[selectedInterfaceIdx].second.find(":") != std::string::npos, interfaces[selectedInterfaceIdx].second, 0);// 绑定到特定的网络接口,端口随机 + if (BindResult != "OK") { + logMessage("WRQ传输套接字绑定失败:" + BindResult, sockaddr_in{}, 1); + closesocket(sock); + WSACleanup(); return; } @@ -541,17 +535,17 @@ class TFTPServer { // 文件写操作 + std::ifstream existingFile(filePath); std::ofstream file(filePath, mode == "netascii" ? std::ios::out : std::ios::binary); - if (!file.is_open()) { + if (!file.is_open() || existingFile) { // 检查文件是否已存在 - std::ifstream existingFile(filePath); if (existingFile) { sendError(clientAddr, 6, "File already exists"); logMessage("客户端尝试写入一个已存在的文件:" + filename, clientAddr, 1); } else { sendError(clientAddr, 2, "Cannot create file"); - logMessage("服务器无法创建文件,请使用管理员权限启动服务器", clientAddr, 1); + logMessage("无法写入文件,请检查权限或者线程冲突", clientAddr, 1); } closesocket(dataSock); // 关闭传输套接字 currentThreadCount--; diff --git a/TFTP.vcxproj b/TFTP.vcxproj index 9787d74..151a5ef 100644 --- a/TFTP.vcxproj +++ b/TFTP.vcxproj @@ -87,6 +87,10 @@ + + C:\env\boost_1_86_0\stage\lib;$(IncludePath) + C:\env\boost_1_86_0\stage\lib;$(LibraryPath) + Level3 @@ -140,14 +144,14 @@ NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true stdcpp17 - $(WXWIN)\include;$(WXWIN)\include\msvc + C:\env\boost_1_86_0;$(WXWIN)\include;$(WXWIN)\include\msvc Windows true true true - $(WXWIN)\lib\vc_x64_lib;%(AdditionalLibraryDirectories) + C:\env\boost_1_86_0\stage\lib;$(WXWIN)\lib\vc_x64_lib;%(AdditionalLibraryDirectories) RequireAdministrator diff --git a/UI.cpp b/UI.cpp index cc85b34..be047e7 100644 --- a/UI.cpp +++ b/UI.cpp @@ -31,14 +31,16 @@ bool b_error_log_switch = error_log_switch; wxString s_error_log_dir = wxString::FromUTF8(error_log_dir.c_str()); bool is_console_mode = false; wxString s_PORT = wxString::Format(wxT("%d"), PORT); -//wxString s_TIMEOUT = wxString::Format(wxT("%d"), TIMEOUT); // 1.1更新 +//wxString s_TIMEOUT = wxString::Format(wxT("%d"), TIMEOUT); // 暂不可用 +int selectedInterfaceIdx = -1; int TableRows = 16; bool Remember_Minimize = false; +std::vector> interfaces = GetNetworkInterfaces(); /////////////////////////////////////////////////////////////////////////// enum { - PU_RESTORE = 10001, - PU_EXIT, + PU_RESTORE = 10001, // 恢复 + PU_EXIT // 退出 }; /////////////////////////////////////////////////////////////////////////// main::main(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) @@ -81,7 +83,7 @@ main::main(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint check_update = new wxButton(this, wxID_ANY, _("检查更新"), wxDefaultPosition, wxDefaultSize, 0); buttons->Add(check_update, 0, wxALL | wxEXPAND, 5); - version = new wxStaticText(this, wxID_ANY, _("当前版本:1.1"), wxDefaultPosition, wxDefaultSize, 0); + version = new wxStaticText(this, wxID_ANY, _("当前版本:1.2"), wxDefaultPosition, wxDefaultSize, 0); version->Wrap(-1); version->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)); @@ -106,7 +108,6 @@ main::main(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint server_dir = new wxDirPickerCtrl(this, wxID_ANY, wxT("./File"), _("服务器目录"), wxDefaultPosition, wxDefaultSize, wxDIRP_DEFAULT_STYLE); gSizer7->Add(server_dir, 0, wxALL | wxEXPAND, 5); - grid_server_dir->Add(gSizer7, 1, wxEXPAND, 5); wxGridSizer* grid_sever_ip; @@ -116,16 +117,22 @@ main::main(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint text_server_ip->Wrap(-1); grid_sever_ip->Add(text_server_ip, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - wxString choice_server_ipChoices[] = { _("本地环回地址(127.0.0.1)"), _("本机公网ip") }; - int choice_server_ipNChoices = sizeof(choice_server_ipChoices) / sizeof(wxString); - choice_server_ip = new wxChoice(this, wxID_ANY, wxPoint(-1, -1), wxDefaultSize, choice_server_ipNChoices, choice_server_ipChoices, 0); + selectedInterfaceIdx = 0; + if (interfaces.empty()) { + console_log->AppendText("读取网卡失败,使用默认接口"); + } + interfaces.insert(interfaces.begin(),std::make_pair(std::string("Default"), std::string("All"))); + + wxArrayString choices; + for (const auto& iface : interfaces) { + choices.Add(wxString::FromUTF8(iface.first + " - " + iface.second)); + } + choice_server_ip = new wxChoice(this, wxID_ANY, wxPoint(-1, -1), wxDefaultSize, choices, 0); choice_server_ip->SetSelection(0); grid_sever_ip->Add(choice_server_ip, 0, wxALL | wxEXPAND, 5); - grid_server_dir->Add(grid_sever_ip, 1, wxEXPAND, 5); - main_grid->Add(grid_server_dir, 1, wxEXPAND, 5); wxGridSizer* console_log_grid; @@ -134,7 +141,6 @@ main::main(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint console_log = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY); console_log_grid->Add(console_log, 0, wxALL | wxEXPAND, 5); - main_grid->Add(console_log_grid, 1, wxEXPAND, 5); wxGridSizer* information_grid; @@ -201,6 +207,7 @@ main::main(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint version->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(main::OnVersionClick), NULL, this); version->Bind(wxEVT_ENTER_WINDOW, &main::OnMouseEnter, this); version->Bind(wxEVT_LEAVE_WINDOW, &main::OnMouseLeave, this); + choice_server_ip->Connect(wxEVT_CHOICE, wxCommandEventHandler(main::OnServerIPChanged), NULL, this); this->Bind(wxEVT_CLOSE_WINDOW, &main::OnClose, this); // 关闭窗口事件 loadSettings("settings.conf"); // 读取配置文件 } @@ -280,7 +287,7 @@ void main::OnCheckUpdateButtonClick(wxCommandEvent& event) wxMessageBox(_("版本检查程序Version.exe不存在"), _("错误"), wxOK | wxICON_ERROR); return; } - std::string version = "1.1"; // 当前版本号 + std::string version = "1.2"; // 当前版本号 std::string command = "Version.exe " + version; system(command.c_str()); @@ -390,7 +397,7 @@ void main::TableStatus(int row, bool success) void main::OnVersionClick(wxMouseEvent& event) { - wxMessageBox(_("1.1更新:\n- 修复了异常情况不能正常重传的bug\n- 传输使用非阻塞模式和Select方法,提升了性能\n- 表格现在可以动态更新行数\n- 优化计时器逻辑\n更新计划:\n- 动态调整超时时间,并且可以手动设置\n- 减少冗余ACK的发送"), _("更新信息"), wxOK | wxICON_INFORMATION, this); + wxMessageBox(_("1.2更新:\n- 添加选择网络接口功能\n- 优化套接字绑定失败错误信息\n更新计划:\n- 动态调整超时时间,并且可以手动设置\n- 减少冗余ACK的发送"), _("更新信息"), wxOK | wxICON_INFORMATION, this); } void main::OnMouseEnter(wxMouseEvent& event) @@ -448,6 +455,17 @@ void main::OnClose(wxCloseEvent& event) // 如果用户选择取消,则不执行任何操作 } +void main::OnServerIPChanged(wxCommandEvent& event) +{ + if (running) { + wxMessageBox("服务器启动时禁止更改此选项,请先关闭服务器。", "非法操作", wxICON_ERROR); + choice_server_ip->SetSelection(selectedInterfaceIdx); + } + else { + selectedInterfaceIdx = choice_server_ip->GetSelection(); // 获取选择的网卡 + } +} + main::~main() { diff --git a/UI.h b/UI.h index 20e2cac..202cef8 100644 --- a/UI.h +++ b/UI.h @@ -60,6 +60,7 @@ class main : public wxFrame void OnMouseEnter(wxMouseEvent& event); void OnMouseLeave(wxMouseEvent& event); void loadSettings(const std::string& configFilePath); + void OnServerIPChanged(wxCommandEvent& event); // 维护表格信息 void AddClientInfo(const std::string& ip, const std::string& file, const std::string& startTime); diff --git a/api.cpp b/api.cpp index e72468c..a77514b 100644 --- a/api.cpp +++ b/api.cpp @@ -1,8 +1,20 @@ #define _CRT_SECURE_NO_WARNINGS #include "api.h" -#include #include +#include // Windows套接字库 #include +#include +#include +#include +#include +#include "shared.h" +#include +#include +#include +#include + +#pragma comment(lib, "iphlpapi.lib") +#pragma comment(lib, "ws2_32.lib") Timer::Timer() : isRunning(false) {} @@ -81,3 +93,88 @@ void CloseConsoleWindow() { FreeConsole(); } + +std::string ConvertWStringToString(const std::wstring& wstr) { + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), nullptr, 0, nullptr, nullptr); + std::string str(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &str[0], size_needed, nullptr, nullptr); + return str; +} + +// 获取网卡信息 +std::vector> GetNetworkInterfaces() { + std::vector> interfaces; + ULONG bufferSize = 15000; + PIP_ADAPTER_ADDRESSES addresses = (IP_ADAPTER_ADDRESSES*)malloc(bufferSize); + + if (GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, addresses, &bufferSize) == ERROR_BUFFER_OVERFLOW) { + free(addresses); + addresses = (IP_ADAPTER_ADDRESSES*)malloc(bufferSize); + } + + if (GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, addresses, &bufferSize) == NO_ERROR) { + for (PIP_ADAPTER_ADDRESSES addr = addresses; addr != nullptr; addr = addr->Next) { + std::string name = ConvertWStringToString(addr->FriendlyName); + for (PIP_ADAPTER_UNICAST_ADDRESS ua = addr->FirstUnicastAddress; ua != nullptr; ua = ua->Next) { + char ip[INET6_ADDRSTRLEN]; + if (getnameinfo(ua->Address.lpSockaddr, ua->Address.iSockaddrLength, ip, sizeof(ip), nullptr, 0, NI_NUMERICHOST) == 0) { + interfaces.push_back(std::make_pair(name,ip)); + } + } + } + } + free(addresses); + return interfaces; +} + +std::string GetLastErrorAsString() { + // 获取错误代码 + DWORD errorMessageID = WSAGetLastError(); + if (errorMessageID == 0) { + return "端口被占用"; + } + + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, nullptr); + + std::string message(messageBuffer, size); + + // 释放缓冲区 + LocalFree(messageBuffer); + + return message; +} + +// 绑定套接字到特定的网络接口 +std::string BindSocketToInterface(SOCKET* sock, bool isIPV6, const std::string& ipAddress, int port) { + struct sockaddr_in service; + + // 设置套接字地址和端口 + if (isIPV6) { + return "IPV6 is not supported"; + } + else { + service.sin_family = AF_INET; + if (ipAddress == std::string("All"))service.sin_addr.s_addr = INADDR_ANY; // 所有接口 + else if (InetPtonA(AF_INET, ipAddress.c_str(), &service.sin_addr) != 1) { + return "Invalid IPV4 address format : " + ipAddress; + } + service.sin_port = htons(port); + } + + // 绑定套接字到特定的网络接口 + if (bind(*sock, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) { + closesocket(*sock); + WSACleanup(); + return GetLastErrorAsString(); + } + return "OK"; +} + + + + + + diff --git a/api.h b/api.h index 0ab0813..32e9972 100644 --- a/api.h +++ b/api.h @@ -1,7 +1,10 @@ #pragma once #include #include +#include #include +#include +#include // Windows套接字库 // 计时器 class Timer { @@ -28,5 +31,8 @@ std::string path_join(const std::string& path1, const std::string& path2); void OpenConsoleWindow(); void CloseConsoleWindow(); +// 获取网卡信息 +std::vector> GetNetworkInterfaces(); - +// 绑定套接字到特定的网络接口 +std::string BindSocketToInterface(SOCKET* sock, bool isIPV6, const std::string& ipAddress,int PORT); diff --git a/shared.h b/shared.h index 2f27ad6..f5ddc26 100644 --- a/shared.h +++ b/shared.h @@ -13,7 +13,9 @@ extern bool is_console_mode; // 是否为控制台模式 extern int PORT; // 服务器端口 //extern int TIMEOUT; // 超时时间 +extern std::vector> interfaces; // 网卡信息 +extern int selectedInterfaceIdx; // 选择的网卡 -extern void CloseConsoleWindow(); +extern void CloseConsoleWindow(); // 关闭控制台窗口