2401_86455051 2024-12-30 11:29 采纳率: 0%
浏览 3

window系统c语言的TCP协议的多线程通信且要求客户端有界面

#include "pch.h"
#include "framework.h"
#include "Client.h"
#include <winsock2.h>
#include <string>
#include <windows.h>
#include <commctrl.h>
#include <ws2tcpip.h>
#include <ctime>
#include <sstream>
#include <iostream> 

#pragma comment(lib, "ws2_32")

// 定义资源标识符(如果没有使用资源文件)
#ifndef STATUSCLASSNAME
#define STATUSCLASSNAME "msctls_statusbar32"
#endif

#ifndef SBARS_SIZEGRIP
#define SBARS_SIZEGRIP 0x0004
#endif

#define IDC_EDIT_SEND 1001
#define IDC_EDIT_RECEIVE 1002
#define IDC_BUTTON_SEND 1003
#define IDC_CONNECT 1004

#define MAX_LOADSTRING 100

// 全局变量
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名
HWND hStatusBar;                                // 状态栏句柄
HWND hEditSend;                                 // 消息输入框句柄
HWND hEditReceive;                              // 消息显示框柄
SOCKET clientSocket;                            // 客户端套接字
struct sockaddr_in serverAddr;
bool isConnected = false;                       // 连接状态

// 函数声明
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
int WINAPI WinMain( _In_ HINSTANCE hInstance,   _In_opt_ HINSTANCE hPrevInstance,  _In_ LPSTR lpCmdLine,_In_ int nCmdShow );
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
DWORD WINAPI ReceiveThread(LPVOID lpParam);
void UpdateStatusBar(const std::string& status);
void ConnectToServer(HWND hWnd);
void SendMessageToServer(const std::string& message);
std::string GetCurrentTimeString(const std::string& message);

// 全局互斥锁
CRITICAL_SECTION mutex;

// 获取当前时间并格式化为字符串,接受message参数
std::string GetCurrentTimeString(const std::string& message) {
    char buffer[9];
    time_t now = time(0);
    tm ltm;
    localtime_s(&ltm, &now);
    strftime(buffer, sizeof(buffer), "%H:%M:%S", &ltm);
    std::string timeStamp = std::string(buffer);
    std::stringstream ss;
    ss << "[" << timeStamp << "] " << message;
    return ss.str();
}

DWORD WINAPI ReceiveThread(LPVOID lpParam) {
    char buffer[1024];
    int recvLen;
    HWND hWnd = *(static_cast<HWND*>(lpParam));

    while (isConnected) {
        // 加强窗口句柄有效性检查
        DWORD dwStyle = GetWindowLong(hEditReceive, GWL_STYLE);
        DWORD dwExStyle = GetWindowLong(hEditReceive, GWL_EXSTYLE);
        if (!IsWindow(hEditReceive) || !IsWindowVisible(hEditReceive) || !IsWindowEnabled(hEditReceive) || GetParent(hEditReceive) != hWnd || (dwStyle & ES_READONLY) != ES_READONLY || (dwExStyle & WS_EX_CLIENTEDGE) != WS_EX_CLIENTEDGE) {
            MessageBox(NULL, L"hEditReceive is in an invalid state", L"Error", MB_OK | MB_ICONERROR);
            // 输出更多窗口状态信息到调试控制台
            std::cout << "hEditReceive state: style=" << dwStyle << ", exStyle=" << dwExStyle << std::endl;
            return 0;
        }
        if (!IsWindow(hWnd) || !IsWindowVisible(hWnd) || !IsWindowEnabled(hWnd)) {
            MessageBox(NULL, L"hWnd is invalid", L"Error", MB_OK | MB_ICONERROR);
            return 0;
        }
        
        // 接收消息(加锁)
        EnterCriticalSection(&mutex);
        recvLen = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
        LeaveCriticalSection(&mutex);

        if (recvLen > 0) {
            buffer[recvLen] = '\0';
            std::string message(buffer);
            std::string formattedMessage = GetCurrentTimeString(message);
            std::wstring wideFormattedMessage(formattedMessage.begin(), formattedMessage.end());

            // 获取编辑框当前的内容长度(加锁)
            EnterCriticalSection(&mutex);
            int len = GetWindowTextLengthW(hEditReceive);
            if (len == -1) {  // 处理获取长度失败的情况
                int error = GetLastError();
                std::wstring errorMsg = L"获取编辑框文本长度失败,错误码: " + std::to_wstring(error);
                MessageBox(hWnd, errorMsg.c_str(), L"错误", MB_OK | MB_ICONERROR);
                LeaveCriticalSection(&mutex);
                continue;
            }
            LeaveCriticalSection(&mutex);

            // 获取编辑框当前的内容(加锁)
            EnterCriticalSection(&mutex);
            std::wstring wideMessage(len + 1 + wideFormattedMessage.length(), L'\0');
            if (GetWindowTextW(hEditReceive, &wideMessage[0], len + 1) == 0) {  // 处理获取文本失败的情况
                int error = GetLastError();
                std::wstring errorMsg = L"获取编辑框文本失败,错误码: " + std::to_wstring(error);
                MessageBox(hWnd, errorMsg.c_str(), L"错误", MB_OK | MB_ICONERROR);
                LeaveCriticalSection(&mutex);
                continue;
            }
            LeaveCriticalSection(&mutex);

            wideMessage += wideFormattedMessage + L"\n";

            // 设置编辑框的内容(加锁)
            EnterCriticalSection(&mutex);
            if (SetWindowTextW(hEditReceive, wideMessage.c_str()) == 0) {  // 处理设置文本失败的情况
                int error = GetLastError();
                std::wstring errorMsg = L"设置编辑框文本失败,错误码: " + std::to_wstring(error);
                MessageBox(hWnd, errorMsg.c_str(), L"错误", MB_OK | MB_ICONERROR);
            }
            LeaveCriticalSection(&mutex);
        }
        else if (recvLen == 0) {
            // 服务器关闭连接
            isConnected = false;
            UpdateStatusBar("服务器已关闭连接");
            MessageBox(hWnd, L"服务器已关闭连接", L"提示", MB_OK | MB_ICONINFORMATION);
            // 关闭套接字(加锁)
            EnterCriticalSection(&mutex);
            closesocket(clientSocket);
            LeaveCriticalSection(&mutex);
            break;
        }
        else {
            // 接收消息出错
            int errorCode = WSAGetLastError();
            std::wstring errorText = L"接收消息出错,错误码: " + std::to_wstring(errorCode);
            MessageBox(hWnd, errorText.c_str(), L"错误", MB_OK | MB_ICONERROR);

            if (errorCode == WSAECONNRESET) {
                // 如果连接被重置,尝试重新连接服务器
                isConnected = false;
                // 关闭套接字(加锁)
                EnterCriticalSection(&mutex);
                closesocket(clientSocket);
                LeaveCriticalSection(&mutex);
                ConnectToServer(hWnd);
            }
            else {
                isConnected = false;
                // 关闭套接字(加锁)
                EnterCriticalSection(&mutex);
                closesocket(clientSocket);
                LeaveCriticalSection(&mutex);
            }
            break;
        }
    }
    return 0;
}

// 更新状态栏显示
void UpdateStatusBar(const std::string& status) {
    int statusBarParts[] = { 0, 100 };
    std::wstring wideStatus(status.begin(), status.end());
    SendMessage(hStatusBar, SB_SETPARTS, sizeof(statusBarParts) / sizeof(int), (LPARAM)statusBarParts);
    if (SendMessage(hStatusBar, SB_SETTEXT, 0, (LPARAM)wideStatus.c_str()) == 0) {  // 处理设置状态栏文本失败的情况
        int error = GetLastError();
        std::wstring errorMsg = L"设置状态栏文本失败,错误码: " + std::to_wstring(error);
        MessageBox(NULL, errorMsg.c_str(), L"错误", MB_OK | MB_ICONERROR);
    }
}

// 连接到服务器
void ConnectToServer(HWND hWnd) {
    WSADATA wsaData;
    SOCKET serverSocket;
    struct sockaddr_in serverAddr;
    int result;
    int retryCount = 3;  // 设置重试次数

    // 初始化Winsock
    result = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (result != 0) {
        MessageBox(NULL, L"WSAStartup failed.", L"Error", MB_OK);
        return;
    }

    // 创建套接字(加锁)
    EnterCriticalSection(&mutex);
    serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSocket == INVALID_SOCKET) {
        MessageBox(NULL, L"Error at socket(): ", L"Socket Initialization Failed", MB_OK);
        WSACleanup();
        LeaveCriticalSection(&mutex);
        return;
    }
    std::cout << "Socket created successfully. Socket descriptor: " << serverSocket << std::endl;  // 输出套接字描述符

    // 设置服务器地址
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(8888); // 端口号
    inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);      // 服务器IP

    // 显示连接等待提示框
    MessageBox(NULL, L"正在连接服务器,请稍候...", L"连接中", MB_OK | MB_ICONINFORMATION);

    // 连接到服务器(重试机制)
    while (retryCount > 0) {
        result = connect(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
        if (result == SOCKET_ERROR) {
            int errorCode = WSAGetLastError();
            if (errorCode == WSAECONNREFUSED && retryCount > 1) {
                // 如果连接被拒绝,等待一段时间后重试
                Sleep(1000);
                retryCount--;
                continue;
            }
            MessageBox(NULL, L"Unable to connect to server.", L"Connection Error", MB_OK);
            closesocket(serverSocket);
            WSACleanup();
            LeaveCriticalSection(&mutex);
            return;
        }
        break;
    }
    std::cout << "Connected to server successfully." << std::endl;  // 输出连接成功信息

    clientSocket = serverSocket; // 保存套接字
    isConnected = true; // 更新连接状态
    UpdateStatusBar("Connected to server");

    // 启动接收线程(加锁)
    std::cout << "Thread parameter address (hWnd): " << std::hex << (void*)hWnd << std::endl;
    HANDLE hThread = CreateThread(NULL, 0, ReceiveThread, (LPVOID)hWnd, 0, NULL);
    if (hThread == NULL) {
        MessageBox(NULL, L"Unable to create receive thread.", L"Thread Creation Failed", MB_OK);
        closesocket(clientSocket);
        WSACleanup();
        isConnected = false;
        LeaveCriticalSection(&mutex);
        return;
    }
    CloseHandle(hThread);

    // 解锁
    LeaveCriticalSection(&mutex);
}

// 向服务器发送消息
void SendMessageToServer(const std::string& message) {
    if (isConnected) {
        int sendResult = send(clientSocket, message.c_str(), (int)message.length(), 0);
        if (sendResult == SOCKET_ERROR) {
            int errorCode = WSAGetLastError();
            std::wstring errorText = L"Send failed with error code: " + std::to_wstring(errorCode);
            MessageBox(NULL, errorText.c_str(), L"Send Error", MB_OK | MB_ICONERROR);
            isConnected = false; // 发送失败,更新连接状态
            // 关闭套接字(加锁)
            EnterCriticalSection(&mutex);
            closesocket(clientSocket);
            LeaveCriticalSection(&mutex);
        }
        else {
            // 发送成功,可以在状态栏显示发送成功的信息
            UpdateStatusBar("Message sent successfully");
        }
    }
    else {
        MessageBox(NULL, L"Not connected to the server.", L"Send Error", MB_OK | MB_ICONWARNING);
    }
}

// 注册窗口类
ATOM MyRegisterClass(HINSTANCE hInstance) {
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CLIENT));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_CLIENT);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

// 保存实例句柄并创建主窗口
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
    hInst = hInstance;
    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, 500, 300, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd) {
        return FALSE;
    }

    hStatusBar = CreateWindow(STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
        0, 0, 0, 0, hWnd, (HMENU)IDC_CONNECT, hInst, nullptr);

    hEditSend = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL,
        10, 10, 200, 50, hWnd, (HMENU)IDC_EDIT_SEND, hInst, nullptr);

    hEditReceive = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL,
        10, 70, 200, 180, hWnd, (HMENU)IDC_EDIT_RECEIVE, hInst, nullptr);

    CreateWindowW(L"BUTTON", L"发送", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
        10, 260, 80, 20, hWnd, (HMENU)IDC_BUTTON_SEND, hInst, nullptr);

    CreateWindowW(L"BUTTON", L"连接", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
        100, 260, 80, 20, hWnd, (HMENU)IDC_CONNECT, hInst, nullptr);

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

// 应用程序的入口点,负责初始化应用程序、处理消息循环和程序退出。
// hInstance: 当前实例句柄。
// hPrevInstance: 前一个实例句柄(在32位Windows中通常为NULL)。
// szCmdLine: 命令行参数字符串。
// iCmdShow: 窗口的显示状态。
// 返回值: 程序退出时的状态码。

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_CLIENT, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 初始化互斥锁
    InitializeCriticalSection(&mutex);

    // 执行应用程序初始化
    if (!InitInstance(hInstance, nCmdShow)) {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLIENT));

    MSG msg;

    // 主消息循环
    while (GetMessage(&msg, nullptr, 0, 0)) {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    // 销毁互斥锁
    DeleteCriticalSection(&mutex);

    return (int)msg.wParam;
}

// About对话框回调函数
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}


// WndProc函数定义
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_COMMAND: {
        int wmId = LOWORD(wParam);
        // 分析菜单选择
        switch (wmId) {
        case IDC_CONNECT:
            if (isConnected) {
                // 断开连接逻辑
                // 关闭套接字(加锁)
                EnterCriticalSection(&mutex);
                closesocket(clientSocket);
                LeaveCriticalSection(&mutex);
                isConnected = false;
                UpdateStatusBar("未连接");
            }
            else {
                ConnectToServer(hWnd);
            }
            break;
        case IDC_BUTTON_SEND: {
            if (isConnected) {
                int len = GetWindowTextLengthW(hEditSend);
                if (len == 0) {
                    MessageBox(hWnd, L"请输入消息内容", L"提示", MB_OK | MB_ICONINFORMATION);
                    break;
                }
                std::wstring wideMessage(len + 1, L'\0');
                GetWindowTextW(hEditSend, const_cast<LPWSTR>(wideMessage.data()), len + 1);
                std::string message(wideMessage.begin(), wideMessage.end());
                SendMessageToServer(message);
            }
            else {
                MessageBox(hWnd, L"请先连接服务器", L"提示", MB_OK | MB_ICONINFORMATION);
            }
        }
                            break;
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
                   break;
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 在此处添加使用hdc的任何绘图代码...
        EndPaint(hWnd, &ps);
    }
                 break;
    case WM_DESTROY: {
        if (isConnected) {
            // 关闭套接字(加锁)
            EnterCriticalSection(&mutex);
            closesocket(clientSocket);
            LeaveCriticalSection(&mutex);
        }
        // 释放状态栏资源
        DestroyWindow(hStatusBar);
        // 释放消息输入框资源
        DestroyWindow(hEditSend);
        // 释放消息显示框资源
        DestroyWindow(hEditReceive);

        // 如果接收线程正在运行,等待其结束
        if (isConnected) {
            HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, *((DWORD*)&hWnd));
            if (hThread != NULL) {
                WaitForSingleObject(hThread, INFINITE);
                CloseHandle(hThread);
            }
        }

        // 销毁互斥锁
        DeleteCriticalSection(&mutex);

        // 发送退出消息
        PostQuitMessage(0);
        break;
    }
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

在DWORD WINAPI ReceiveThread(LPVOID lpParam)函数代码HWND hWnd = *(static_cast<HWND*>(lpParam));处出现0x00007FF715BE9E11 处(位于 Client.exe 中)引发的异常: 0xC0000005: 读取位置 0x0000000000090812 时发生访问冲突。请问,如何修改

  • 写回答

2条回答 默认 最新

  • 码农阿豪@新空间 新星创作者: 前端开发技术领域 2024-12-30 12:03
    关注
    让阿豪来帮你解答,本回答参考chatgpt3.5编写提供,如果还有疑问可以评论或留言
    The code provided seems to be a part of a C++ client application that communicates with a server using sockets. It includes necessary headers, defines constants, global variables, function declarations, and some utility functions. To answer your question, it seems like you want to know how to properly handle the window messages in the `WndProc` function. The `WndProc` function is a callback function that processes messages sent to a window. Here is a structured answer to your question with an example:
    1. Declare the WndProc function with the correct signature:
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    
    1. Define the WndProc function and handle different window messages:
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
        switch (message) {
            case WM_CREATE: 
                // Handle window creation message
                break;
            
            case WM_COMMAND:
                // Handle command messages from menu or control
                break;
            case WM_DESTROY:
                // Handle window destruction message
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    1. Register the WndProc function with the window class during initialization:
    ATOM MyRegisterClass(HINSTANCE hInstance) {
        // Register window class
        WNDCLASS wc = { 0 };
        wc.lpfnWndProc = WndProc;
        // Other window class settings
        RegisterClass(&wc);
    }
    
    1. In the WinMain function, create the main window using the registered window class:
    HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    
    1. Ensure you have a message loop to process messages:
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    

    By following these steps and handling different window messages appropriately in the WndProc function, you can create a responsive and functional Windows application. Let me know if you need more information or specific examples for handling certain messages.

    评论

报告相同问题?

问题事件

  • 创建了问题 12月30日