VC++基于服务器的点对点文件传输实例

一、系统概述

本系统实现了一个基于服务器的点对点文件传输平台,允许多个客户端通过中央服务器进行文件交换。系统采用C/S架构,支持文件上传、下载、列表查询等功能,适用于企业内网文件共享、分布式存储等场景。

二、系统架构

2.1 整体架构

上传/下载
上传/下载
上传/下载
文件存储
用户管理
客户端1
中央服务器
客户端2
客户端3
文件存储系统
数据库

2.2 核心组件

  1. 中央服务器:处理客户端连接、文件路由、权限管理
  2. 文件存储系统:存储上传的文件
  3. 数据库:存储用户信息、文件元数据
  4. 客户端程序:提供用户界面和文件操作功能

三、服务器端实现

3.1 服务器主程序 (FileServer.cpp)

cpp 复制代码
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <thread>
#include <mutex>
#include <direct.h>
#pragma comment(lib, "ws2_32.lib")

#define PORT 8888
#define BUFFER_SIZE 4096
#define MAX_CLIENTS 10

using namespace std;

struct ClientInfo {
    SOCKET socket;
    string username;
    bool authenticated;
};

struct FileInfo {
    string filename;
    string owner;
    long size;
    time_t uploadTime;
};

mutex mtx;
map<SOCKET, ClientInfo> clients;
map<string, FileInfo> fileDatabase;
string storagePath = "ServerStorage/";

void handleClient(SOCKET clientSocket);
void processCommand(SOCKET clientSocket, const string& command);
void sendFile(SOCKET clientSocket, const string& filename);
void receiveFile(SOCKET clientSocket, const string& filename, long fileSize);
void broadcastFileList(SOCKET excludeSocket = INVALID_SOCKET);
void logActivity(const string& message);

int main() {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        cerr << "WSAStartup failed." << endl;
        return 1;
    }

    // 创建存储目录
    _mkdir(storagePath.c_str());

    SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSocket == INVALID_SOCKET) {
        cerr << "Socket creation failed: " << WSAGetLastError() << endl;
        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) {
        cerr << "Bind failed: " << WSAGetLastError() << endl;
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
        cerr << "Listen failed: " << WSAGetLastError() << endl;
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    cout << "Server started on port " << PORT << endl;
    logActivity("Server started");

    while (true) {
        sockaddr_in clientAddr;
        int clientAddrSize = sizeof(clientAddr);
        SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrSize);
        if (clientSocket == INVALID_SOCKET) {
            cerr << "Accept failed: " << WSAGetLastError() << endl;
            continue;
        }

        char ipStr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(clientAddr.sin_addr), ipStr, INET_ADDRSTRLEN);
        cout << "New connection from " << ipStr << ":" << ntohs(clientAddr.sin_port) << endl;

        // 为新客户端创建线程
        thread clientThread(handleClient, clientSocket);
        clientThread.detach();
    }

    closesocket(serverSocket);
    WSACleanup();
    return 0;
}

void handleClient(SOCKET clientSocket) {
    char buffer[BUFFER_SIZE];
    int bytesReceived;

    // 添加客户端到列表
    {
        lock_guard<mutex> lock(mtx);
        clients[clientSocket] = {clientSocket, "", false};
    }

    // 发送欢迎消息
    string welcome = "Welcome to File Transfer Server\n";
    send(clientSocket, welcome.c_str(), welcome.size(), 0);

    while (true) {
        bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE, 0);
        if (bytesReceived <= 0) {
            break; // 连接关闭或出错
        }

        buffer[bytesReceived] = '\0';
        string command(buffer);
        processCommand(clientSocket, command);
    }

    // 客户端断开连接
    {
        lock_guard<mutex> lock(mtx);
        string username = clients[clientSocket].username;
        clients.erase(clientSocket);
        cout << "Client " << username << " disconnected" << endl;
        logActivity(username + " disconnected");
    }

    closesocket(clientSocket);
}

void processCommand(SOCKET clientSocket, const string& command) {
    lock_guard<mutex> lock(mtx);
    string cmd = command.substr(0, command.find(' '));
    string args = command.substr(command.find(' ') + 1);

    if (cmd == "LOGIN") {
        // 登录命令: LOGIN username password
        string username = args.substr(0, args.find(' '));
        string password = args.substr(args.find(' ') + 1);
        
        // 这里简化处理,实际应用中应验证密码
        clients[clientSocket].username = username;
        clients[clientSocket].authenticated = true;
        
        string response = "Login successful. Welcome " + username + "\n";
        send(clientSocket, response.c_str(), response.size(), 0);
        cout << "User " << username << " logged in" << endl;
        logActivity(username + " logged in");
        
        // 发送文件列表
        broadcastFileList(clientSocket);
    }
    else if (cmd == "LIST") {
        // 列出文件命令
        if (!clients[clientSocket].authenticated) {
            send(clientSocket, "Please login first\n", 24, 0);
            return;
        }
        
        string fileList = "Files available:\n";
        for (const auto& file : fileDatabase) {
            fileList += file.first + " (" + to_string(file.second.size) + " bytes) by " + file.second.owner + "\n";
        }
        send(clientSocket, fileList.c_str(), fileList.size(), 0);
    }
    else if (cmd == "UPLOAD") {
        // 上传文件命令: UPLOAD filename filesize
        if (!clients[clientSocket].authenticated) {
            send(clientSocket, "Please login first\n", 24, 0);
            return;
        }
        
        string filename = args.substr(0, args.find(' '));
        string sizeStr = args.substr(args.find(' ') + 1);
        long fileSize = stol(sizeStr);
        
        // 添加到文件数据库
        FileInfo info;
        info.filename = filename;
        info.owner = clients[clientSocket].username;
        info.size = fileSize;
        info.uploadTime = time(nullptr);
        fileDatabase[filename] = info;
        
        // 通知客户端准备接收
        string response = "Ready to receive " + filename + "\n";
        send(clientSocket, response.c_str(), response.size(), 0);
        
        // 接收文件
        receiveFile(clientSocket, filename, fileSize);
        
        // 广播更新后的文件列表
        broadcastFileList();
        logActivity(clients[clientSocket].username + " uploaded " + filename);
    }
    else if (cmd == "DOWNLOAD") {
        // 下载文件命令: DOWNLOAD filename
        if (!clients[clientSocket].authenticated) {
            send(clientSocket, "Please login first\n", 24, 0);
            return;
        }
        
        string filename = args;
        if (fileDatabase.find(filename) != fileDatabase.end()) {
            // 发送文件信息
            string fileInfo = "FILE " + filename + " " + to_string(fileDatabase[filename].size) + "\n";
            send(clientSocket, fileInfo.c_str(), fileInfo.size(), 0);
            
            // 发送文件内容
            sendFile(clientSocket, filename);
            logActivity(clients[clientSocket].username + " downloaded " + filename);
        } else {
            string response = "File not found\n";
            send(clientSocket, response.c_str(), response.size(), 0);
        }
    }
    else if (cmd == "DELETE") {
        // 删除文件命令: DELETE filename
        if (!clients[clientSocket].authenticated) {
            send(clientSocket, "Please login first\n", 24, 0);
            return;
        }
        
        string filename = args;
        if (fileDatabase.find(filename) != fileDatabase.end() && 
            fileDatabase[filename].owner == clients[clientSocket].username) {
            
            // 从数据库删除
            fileDatabase.erase(filename);
            
            // 删除物理文件
            string filePath = storagePath + filename;
            remove(filePath.c_str());
            
            string response = "File deleted successfully\n";
            send(clientSocket, response.c_str(), response.size(), 0);
            
            // 广播更新后的文件列表
            broadcastFileList();
            logActivity(clients[clientSocket].username + " deleted " + filename);
        } else {
            string response = "File not found or permission denied\n";
            send(clientSocket, response.c_str(), response.size(), 0);
        }
    }
    else {
        string response = "Unknown command\n";
        send(clientSocket, response.c_str(), response.size(), 0);
    }
}

void sendFile(SOCKET clientSocket, const string& filename) {
    string filePath = storagePath + filename;
    ifstream file(filePath, ios::binary | ios::ate);
    if (!file) {
        cerr << "Failed to open file: " << filePath << endl;
        return;
    }

    streamsize size = file.tellg();
    file.seekg(0, ios::beg);

    char buffer[BUFFER_SIZE];
    while (size > 0) {
        streamsize chunkSize = min(static_cast<streamsize>(BUFFER_SIZE), size);
        file.read(buffer, chunkSize);
        send(clientSocket, buffer, static_cast<int>(chunkSize), 0);
        size -= chunkSize;
    }

    file.close();
}

void receiveFile(SOCKET clientSocket, const string& filename, long fileSize) {
    string filePath = storagePath + filename;
    ofstream file(filePath, ios::binary);
    if (!file) {
        cerr << "Failed to create file: " << filePath << endl;
        return;
    }

    long remaining = fileSize;
    char buffer[BUFFER_SIZE];

    while (remaining > 0) {
        int chunkSize = min(BUFFER_SIZE, static_cast<int>(remaining));
        int bytesReceived = recv(clientSocket, buffer, chunkSize, 0);
        if (bytesReceived <= 0) {
            break; // 连接关闭或出错
        }
        
        file.write(buffer, bytesReceived);
        remaining -= bytesReceived;
    }

    file.close();
}

void broadcastFileList(SOCKET excludeSocket) {
    string fileList = "UPDATE_FILE_LIST\n";
    for (const auto& file : fileDatabase) {
        fileList += file.first + "|" + file.second.owner + "|" + 
                   to_string(file.second.size) + "|" + 
                   to_string(file.second.uploadTime) + "\n";
    }
    
    for (const auto& client : clients) {
        if (client.first != excludeSocket && client.second.authenticated) {
            send(client.first, fileList.c_str(), fileList.size(), 0);
        }
    }
}

void logActivity(const string& message) {
    ofstream logFile("server.log", ios::app);
    if (logFile) {
        time_t now = time(nullptr);
        char* dt = ctime(&now);
        logFile << "[" << dt << "] " << message << endl;
        logFile.close();
    }
}

3.2 服务器功能说明

  1. 用户认证:简单的用户名/密码登录(实际应用中应使用加密存储)
  2. 文件管理
    • 上传文件(UPLOAD命令)
    • 下载文件(DOWNLOAD命令)
    • 删除文件(DELETE命令)
    • 列出文件(LIST命令)
  3. 文件存储:服务器本地目录存储上传的文件
  4. 日志记录:记录所有操作和连接事件
  5. 广播通知:文件列表更新时通知所有客户端

四、客户端实现

4.1 客户端主程序 (FileClientDlg.cpp)

cpp 复制代码
#include <winsock2.h>
#include <ws2tcpip.h>
#include <afxwin.h>
#include <afxext.h>
#include <afxcmn.h>
#include <string>
#include <vector>
#include <fstream>
#include <thread>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "comctl32.lib")

#define PORT 8888
#define BUFFER_SIZE 4096

using namespace std;

class CFileClientDlg : public CDialogEx {
public:
    CFileClientDlg(CWnd* pParent = nullptr);
    enum { IDD = IDD_FILECLIENT_DIALOG };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()

private:
    // 控件变量
    CEdit m_editServerIP;
    CEdit m_editUsername;
    CEdit m_editPassword;
    CButton m_btnConnect;
    CButton m_btnDisconnect;
    CListBox m_listFiles;
    CEdit m_editLocalPath;
    CButton m_btnBrowse;
    CButton m_btnUpload;
    CButton m_btnDownload;
    CButton m_btnDelete;
    CProgressCtrl m_progress;
    CStatic m_status;

    // Socket相关
    SOCKET m_clientSocket;
    bool m_connected;
    string m_username;
    thread m_receiveThread;

    // 方法
    void ConnectToServer();
    void DisconnectFromServer();
    void SendCommand(const string& cmd);
    void ReceiveFileList();
    void UploadFile(const string& filePath);
    void DownloadFile(const string& filename);
    void DeleteFile(const string& filename);
    void UpdateUI(bool connected);
    void UpdateFileList(const string& list);
    void UpdateProgress(int progress);
    void SetStatus(const string& message);
    static DWORD WINAPI ReceiveThread(LPVOID lpParam);
    void StartReceiveThread();
    void StopReceiveThread();
};

BEGIN_MESSAGE_MAP(CFileClientDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON_CONNECT, &CFileClientDlg::OnBnClickedButtonConnect)
    ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, &CFileClientDlg::OnBnClickedButtonDisconnect)
    ON_BN_CLICKED(IDC_BUTTON_BROWSE, &CFileClientDlg::OnBnClickedButtonBrowse)
    ON_BN_CLICKED(IDC_BUTTON_UPLOAD, &CFileClientDlg::OnBnClickedButtonUpload)
    ON_BN_CLICKED(IDC_BUTTON_DOWNLOAD, &CFileClientDlg::OnBnClickedButtonDownload)
    ON_BN_CLICKED(IDC_BUTTON_DELETE, &CFileClientDlg::OnBnClickedButtonDelete)
    ON_LBN_DBLCLK(IDC_LIST_FILES, &CFileClientDlg::OnLbnDblclkListFiles)
END_MESSAGE_MAP()

CFileClientDlg::CFileClientDlg(CWnd* pParent) 
    : CDialogEx(IDD_FILECLIENT_DIALOG, pParent), m_clientSocket(INVALID_SOCKET), m_connected(false) {
}

void CFileClientDlg::DoDataExchange(CDataExchange* pDX) {
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_EDIT_SERVER_IP, m_editServerIP);
    DDX_Control(pDX, IDC_EDIT_USERNAME, m_editUsername);
    DDX_Control(pDX, IDC_EDIT_PASSWORD, m_editPassword);
    DDX_Control(pDX, IDC_BUTTON_CONNECT, m_btnConnect);
    DDX_Control(pDX, IDC_BUTTON_DISCONNECT, m_btnDisconnect);
    DDX_Control(pDX, IDC_LIST_FILES, m_listFiles);
    DDX_Control(pDX, IDC_EDIT_LOCAL_PATH, m_editLocalPath);
    DDX_Control(pDX, IDC_BUTTON_BROWSE, m_btnBrowse);
    DDX_Control(pDX, IDC_BUTTON_UPLOAD, m_btnUpload);
    DDX_Control(pDX, IDC_BUTTON_DOWNLOAD, m_btnDownload);
    DDX_Control(pDX, IDC_BUTTON_DELETE, m_btnDelete);
    DDX_Control(pDX, IDC_PROGRESS_FILE, m_progress);
    DDX_Control(pDX, IDC_STATIC_STATUS, m_status);
}

BOOL CFileClientDlg::OnInitDialog() {
    CDialogEx::OnInitDialog();
    
    // 初始化控件
    m_editServerIP.SetWindowText(_T("127.0.0.1"));
    m_editUsername.SetWindowText(_T("user1"));
    m_editPassword.SetWindowText(_T("pass1"));
    m_editLocalPath.SetWindowText(_T("C:\\test.txt"));
    m_btnDisconnect.EnableWindow(FALSE);
    m_btnUpload.EnableWindow(FALSE);
    m_btnDownload.EnableWindow(FALSE);
    m_btnDelete.EnableWindow(FALSE);
    m_progress.SetRange(0, 100);
    m_progress.SetPos(0);
    
    // 初始化Socket库
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        AfxMessageBox(_T("WSAStartup failed"));
        return FALSE;
    }
    
    return TRUE;
}

void CFileClientDlg::ConnectToServer() {
    CString serverIP, username, password;
    m_editServerIP.GetWindowText(serverIP);
    m_editUsername.GetWindowText(username);
    m_editPassword.GetWindowText(password);
    
    // 创建Socket
    m_clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_clientSocket == INVALID_SOCKET) {
        AfxMessageBox(_T("Socket creation failed"));
        return;
    }
    
    // 解析服务器地址
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(PORT);
    inet_pton(AF_INET, CT2A(serverIP), &serverAddr.sin_addr);
    
    // 连接服务器
    if (connect(m_clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        AfxMessageBox(_T("Connection failed"));
        closesocket(m_clientSocket);
        m_clientSocket = INVALID_SOCKET;
        return;
    }
    
    // 登录
    string loginCmd = "LOGIN " + string(CT2A(username)) + " " + string(CT2A(password)) + "\n";
    send(m_clientSocket, loginCmd.c_str(), loginCmd.size(), 0);
    
    // 启动接收线程
    StartReceiveThread();
    
    m_connected = true;
    m_username = CT2A(username);
    UpdateUI(true);
    SetStatus("Connected to server");
}

void CFileClientDlg::DisconnectFromServer() {
    if (m_connected) {
        StopReceiveThread();
        closesocket(m_clientSocket);
        m_clientSocket = INVALID_SOCKET;
        m_connected = false;
        UpdateUI(false);
        SetStatus("Disconnected from server");
    }
}

void CFileClientDlg::SendCommand(const string& cmd) {
    if (m_connected) {
        send(m_clientSocket, cmd.c_str(), cmd.size(), 0);
    }
}

void CFileClientDlg::ReceiveFileList() {
    SendCommand("LIST\n");
}

void CFileClientDlg::UploadFile(const string& filePath) {
    ifstream file(filePath, ios::binary | ios::ate);
    if (!file) {
        AfxMessageBox(_T("Failed to open file"));
        return;
    }
    
    streamsize size = file.tellg();
    file.seekg(0, ios::beg);
    
    string filename = filePath.substr(filePath.find_last_of("\\/") + 1);
    string cmd = "UPLOAD " + filename + " " + to_string(size) + "\n";
    SendCommand(cmd);
    
    // 发送文件内容
    char buffer[BUFFER_SIZE];
    while (size > 0) {
        streamsize chunkSize = min(static_cast<streamsize>(BUFFER_SIZE), size);
        file.read(buffer, chunkSize);
        send(m_clientSocket, buffer, static_cast<int>(chunkSize), 0);
        size -= chunkSize;
        
        // 更新进度
        int progress = static_cast<int>((1.0 - static_cast<double>(size)/file.tellg()) * 100);
        UpdateProgress(progress);
    }
    
    file.close();
    SetStatus("File uploaded: " + filename);
}

void CFileClientDlg::DownloadFile(const string& filename) {
    string cmd = "DOWNLOAD " + filename + "\n";
    SendCommand(cmd);
}

void CFileClientDlg::DeleteFile(const string& filename) {
    string cmd = "DELETE " + filename + "\n";
    SendCommand(cmd);
}

void CFileClientDlg::UpdateUI(bool connected) {
    m_btnConnect.EnableWindow(!connected);
    m_btnDisconnect.EnableWindow(connected);
    m_btnUpload.EnableWindow(connected);
    m_btnDownload.EnableWindow(connected);
    m_btnDelete.EnableWindow(connected);
    m_editServerIP.EnableWindow(!connected);
    m_editUsername.EnableWindow(!connected);
    m_editPassword.EnableWindow(!connected);
}

void CFileClientDlg::UpdateFileList(const string& list) {
    m_listFiles.ResetContent();
    
    if (list.find("UPDATE_FILE_LIST") != string::npos) {
        // 解析文件列表
        size_t pos = list.find('\n');
        if (pos != string::npos) {
            string data = list.substr(pos + 1);
            size_t start = 0;
            while (start < data.length()) {
                size_t end = data.find('\n', start);
                if (end == string::npos) break;
                
                string line = data.substr(start, end - start);
                m_listFiles.AddString(CA2T(line.c_str()));
                start = end + 1;
            }
        }
    } else {
        // 直接显示列表
        m_listFiles.AddString(CA2T(list.c_str()));
    }
}

void CFileClientDlg::UpdateProgress(int progress) {
    m_progress.SetPos(progress);
    CString status;
    status.Format(_T("Progress: %d%%"), progress);
    m_status.SetWindowText(status);
}

void CFileClientDlg::SetStatus(const string& message) {
    m_status.SetWindowText(CA2T(message.c_str()));
}

DWORD WINAPI CFileClientDlg::ReceiveThread(LPVOID lpParam) {
    CFileClientDlg* pThis = (CFileClientDlg*)lpParam;
    char buffer[BUFFER_SIZE];
    
    while (pThis->m_connected) {
        int bytesReceived = recv(pThis->m_clientSocket, buffer, BUFFER_SIZE, 0);
        if (bytesReceived <= 0) {
            pThis->DisconnectFromServer();
            break;
        }
        
        buffer[bytesReceived] = '\0';
        string data(buffer);
        
        if (data.find("FILE ") == 0) {
            // 文件传输开始
            size_t space1 = data.find(' ');
            size_t space2 = data.find(' ', space1 + 1);
            string filename = data.substr(space1 + 1, space2 - space1 - 1);
            long fileSize = stol(data.substr(space2 + 1));
            
            // 选择保存位置
            CFileDialog dlg(FALSE, NULL, CA2T(filename.c_str()), 
                           OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, 
                           _T("All Files (*.*)|*.*||"), pThis);
            if (dlg.DoModal() == IDOK) {
                CString savePath = dlg.GetPathName();
                ofstream file(CT2A(savePath), ios::binary);
                if (file) {
                    long remaining = fileSize;
                    while (remaining > 0) {
                        int chunkSize = min(BUFFER_SIZE, static_cast<int>(remaining));
                        int recvd = recv(pThis->m_clientSocket, buffer, chunkSize, 0);
                        if (recvd <= 0) break;
                        
                        file.write(buffer, recvd);
                        remaining -= recvd;
                        
                        // 更新进度
                        int progress = static_cast<int>((1.0 - static_cast<double>(remaining)/fileSize) * 100);
                        pThis->UpdateProgress(progress);
                    }
                    file.close();
                    pThis->SetStatus("File downloaded: " + CT2A(savePath));
                }
            }
        } else {
            // 普通消息
            pThis->UpdateFileList(data);
        }
    }
    
    return 0;
}

void CFileClientDlg::StartReceiveThread() {
    m_receiveThread = thread(ReceiveThread, this);
}

void CFileClientDlg::StopReceiveThread() {
    if (m_receiveThread.joinable()) {
        m_connected = false;
        m_receiveThread.join();
    }
}

// 事件处理函数
void CFileClientDlg::OnBnClickedButtonConnect() {
    ConnectToServer();
    ReceiveFileList();
}

void CFileClientDlg::OnBnClickedButtonDisconnect() {
    DisconnectFromServer();
}

void CFileClientDlg::OnBnClickedButtonBrowse() {
    CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, 
                   _T("All Files (*.*)|*.*||"), this);
    if (dlg.DoModal() == IDOK) {
        m_editLocalPath.SetWindowText(dlg.GetPathName());
    }
}

void CFileClientDlg::OnBnClickedButtonUpload() {
    CString filePath;
    m_editLocalPath.GetWindowText(filePath);
    UploadFile(CT2A(filePath));
}

void CFileClientDlg::OnBnClickedButtonDownload() {
    int sel = m_listFiles.GetCurSel();
    if (sel != LB_ERR) {
        CString filename;
        m_listFiles.GetText(sel, filename);
        DownloadFile(CT2A(filename));
    }
}

void CFileClientDlg::OnBnClickedButtonDelete() {
    int sel = m_listFiles.GetCurSel();
    if (sel != LB_ERR) {
        CString filename;
        m_listFiles.GetText(sel, filename);
        DeleteFile(CT2A(filename));
        ReceiveFileList(); // 刷新列表
    }
}

void CFileClientDlg::OnLbnDblclkListFiles() {
    OnBnClickedButtonDownload();
}

4.2 客户端功能说明

  1. 连接管理
    • 连接/断开服务器
    • 用户登录/登出
  2. 文件操作
    • 上传文件到服务器
    • 从服务器下载文件
    • 删除服务器上的文件
    • 查看文件列表
  3. 用户界面
    • 文件列表显示
    • 传输进度条
    • 状态信息显示
  4. 后台处理
    • 独立线程处理服务器消息
    • 文件传输进度更新
    • 自动刷新文件列表

参考代码 VC++基于服务器的点对点文件传输实例 www.youwenfan.com/contentcst/122450.html

五、系统使用说明

5.1 运行环境

  • Windows操作系统
  • Visual C++ 2010或更高版本
  • 支持C++11标准的编译器

5.2 安装步骤

  1. 编译服务器程序:

    复制代码
    cl FileServer.cpp ws2_32.lib
  2. 编译客户端程序:

    复制代码
    cl FileClientDlg.cpp user32.lib gdi32.lib comctl32.lib ws2_32.lib
  3. 创建服务器存储目录:

    复制代码
    mkdir ServerStorage

5.3 使用流程

  1. 启动服务器:

    复制代码
    FileServer.exe
  2. 启动客户端:

    复制代码
    FileClient.exe
  3. 在客户端界面:

    • 输入服务器IP地址(默认127.0.0.1)
    • 输入用户名和密码(默认user1/pass1)
    • 点击"Connect"连接服务器
  4. 文件操作:

    • 上传:选择本地文件,点击"Upload"
    • 下载:双击文件列表中的文件
    • 删除:选择文件,点击"Delete"
    • 刷新:点击"List"按钮

六、系统特点

6.1 技术特点

  1. 多线程架构:服务器为每个客户端创建独立线程
  2. 非阻塞UI:文件传输在后台线程进行
  3. 流式传输:支持大文件分块传输
  4. 实时更新:文件列表变更时自动通知客户端
  5. 进度反馈:文件传输过程中显示进度

6.2 安全特性

  1. 用户认证:简单的用户名/密码验证
  2. 权限控制:用户只能删除自己上传的文件
  3. 数据隔离:每个用户的文件相互隔离
  4. 传输加密:可扩展为SSL/TLS加密传输

七、扩展功能建议

7.1 增强安全性

cpp 复制代码
// 在服务器添加密码验证
bool VerifyCredentials(const string& username, const string& password) {
    // 实际应用中应从数据库验证
    map<string, string> credentials = {
        {"user1", "5f4dcc3b5aa765d61d8327deb882cf99"}, // pass1的MD5
        {"user2", "098f6bcd4621d373cade4e832627b4f6"}  // pass2的MD5
    };
    
    if (credentials.find(username) != credentials.end()) {
        return credentials[username] == md5(password);
    }
    return false;
}

7.2 添加文件搜索功能

cpp 复制代码
// 在服务器添加搜索命令
else if (cmd == "SEARCH") {
    string keyword = args;
    string results = "Search results for \"" + keyword + "\":\n";
    
    for (const auto& file : fileDatabase) {
        if (file.first.find(keyword) != string::npos) {
            results += file.first + " by " + file.second.owner + "\n";
        }
    }
    
    send(clientSocket, results.c_str(), results.size(), 0);
}

7.3 实现断点续传

cpp 复制代码
// 在文件传输中添加断点续传支持
void resumeFileTransfer(SOCKET socket, const string& filename, long startPos) {
    string filePath = storagePath + filename;
    ifstream file(filePath, ios::binary | ios::ate);
    
    if (file) {
        long currentSize = file.tellg();
        if (currentSize < startPos) {
            // 文件损坏,重新开始
            startPos = 0;
        }
    } else {
        startPos = 0;
    }
    
    // 发送续传请求
    string cmd = "RESUME " + filename + " " + to_string(startPos) + "\n";
    send(socket, cmd.c_str(), cmd.size(), 0);
    
    // 继续传输...
}

八、总结

本系统实现了一个基于服务器的点对点文件传输平台,具有以下特点:

  1. 完整的C/S架构

    • 服务器处理连接、认证、文件路由
    • 客户端提供用户友好的操作界面
    • 支持多客户端并发访问
  2. 核心功能实现

    • 用户认证与权限管理
    • 文件上传/下载/删除
    • 文件列表实时更新
    • 传输进度显示
  3. 技术亮点

    • 多线程并发处理
    • 流式文件传输
    • 非阻塞UI设计
    • 实时事件通知
  4. 可扩展性

    • 易于添加新功能(如文件搜索、断点续传)
    • 支持安全增强(SSL/TLS加密)
    • 可集成数据库系统

系统适用于企业内网文件共享、分布式存储、教育资源共享等多种场景,通过简单的扩展可满足更复杂的应用需求。

相关推荐
liu****2 小时前
第16届省赛蓝桥杯大赛C/C++大学B组(京津冀)
开发语言·数据结构·c++·算法·蓝桥杯
favour_you___2 小时前
epoll惊群问题与解决
服务器·网络·tcp/ip·epoll
咬_咬2 小时前
go语言学习(基本数据类型)
开发语言·学习·golang·数据类型
Y001112362 小时前
MySQL-进阶
开发语言·数据库·sql·mysql
Crazy________2 小时前
docker4.8
java·开发语言·eureka
山甫aa2 小时前
List 容器 -----C++的stl学习
开发语言·c++·学习
cch89182 小时前
Laravel 2.x:早期框架的奠基之路
java·开发语言
t198751282 小时前
光伏发电MPPT(最大功率点跟踪)MATLAB仿真程序
开发语言·matlab
CoderCodingNo2 小时前
【GESP】C++四、五级练习题 luogu-P1177 【模板】排序
数据结构·c++·算法