簡體   English   中英

C ++ win32異步套接字客戶端send()問題

[英]C++ win32 async socket client send() issue

我必須編寫一個WIN32,異步套接字客戶端應用程序,它連接到服務器。 我可以啟動程序,連接到服務器,接收並繼續接收數據,但是當我嘗試向服務器發送消息時,服務器上沒有任何反應。 我沒有任何套接字錯誤消息,send()函數返回正確的發送字節。 但服務器看起來好像沒有收到任何東西。 我的send()在FD_READ語句中調用,首先是if語句。 我試圖將其移動到其他語句(WM_CREATE,FD_WRITE,FD_CONNECT),但仍然是同一個問題。 我試圖找出什么是錯的,從一個mounth看很多論壇,但我沒有發現問題。 任何幫助將非常感激。 這是我的代碼示例(我跳過了非常有趣的部分):

#include <windows.h>
#include <fstream>
#include <sstream>
#include<process.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

#define IDC_BUTTON_CONNECT 101 // Button identifiers

#define IDC_EDIT_IP 102 // Edit / Text box identifiers
#define IDC_EDIT_PORT 103
#define IDC_EDIT_DEBUG 104
#define WM_SOCKET 105 // Socket messages structure identifier

HWND hWnd;
HWND hEditIp;
HWND hEditPort;
HWND hDebug;

HWND hButtonConnect;

HANDLE hReadMutex;

SOCKET Socket = NULL;
SOCKADDR_IN SockAddr;
char *ip = "";
char *port = "";
bool connectStatus = FALSE;
char readBuffer[5000];
char id[32];
char version[256];

LPSTR statusText = TEXT("Connecting...");

LRESULT CALLBACK WinProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);

// WinMain...

// appendTextToEdit function...

// incoming data processing thread (not used yet)...

LRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{   
    switch(msg)
    {
        case WM_CREATE:
        {
        // Get IP and port from file "config.ini"...        

        //Create windows for IP, Port and Debug...

        // Create "connect" button...


        WSADATA WsaDat; // Winsock initialization...
        int nResult=WSAStartup(MAKEWORD(2,2),&WsaDat);
        if(nResult!=0)
        {
            statusText=TEXT("Winsock initialization failed");                   
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        }

        Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // Creating socket...
        if(Socket==INVALID_SOCKET)
        {
            statusText=TEXT("Socket creation failed");                  
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        }

        nResult=WSAAsyncSelect(Socket,hWnd,WM_SOCKET,(FD_CLOSE|FD_READ|FD_WRITE|FD_CONNECT)); // Select AsyncSocket events...
        if(nResult)
        {
            statusText=TEXT("WSAAsyncSelect failed");                   
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        }           

        // Set up our socket address structure...
        SockAddr.sin_addr.s_addr=inet_addr(ip);
        SockAddr.sin_port=htons(atoi(port)); 
        SockAddr.sin_family=AF_INET;        

        connect(Socket,(LPSOCKADDR)(&SockAddr),sizeof(SockAddr)); // Connexion command              
    }
    break;

    case WM_PAINT:
    {
        // painting / updating the window...
    }
    break;

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
            case IDC_BUTTON_CONNECT:
            {                   
                if(connectStatus==FALSE) // Avoid to reconnect when already online...
                {
                    // Window repaint with updated text (statusText = global variable)
                    statusText=TEXT("Connecting...");                   
                    InvalidateRect(hWnd, NULL, TRUE);                   

                    // Retrieve edit box datas and store to variables and file...
                    // Saving data to "config.ini" file...

                    // Reinitializing adress structure with new host adress and port from edit boxes, and reconnect attempt...
                    SockAddr.sin_addr.s_addr=inet_addr(ip);
                    SockAddr.sin_port=htons(atoi(port));
                    SockAddr.sin_family=AF_INET;    

                    connect(Socket,(LPSOCKADDR)(&SockAddr),sizeof(SockAddr));
                }
                break;
            }
            break;          
        }
        break;          

    case WM_SOCKET:
    {           
        switch(WSAGETSELECTEVENT(lParam))
        {
            case FD_CONNECT:
            {
                connectStatus = TRUE;
                statusText=TEXT("Connected");               
            }

            case FD_READ:
            {
                appendTextToEdit(hDebug, "FD_READ event...\n");

                char *rawVariable = nullptr;                    
                char *next_token = nullptr;                         

                recv(Socket, readBuffer, sizeof(readBuffer), 0);            

                rawVariable = readBuffer;               

                if(strstr(rawVariable, "id=") != NULL) // Identifying message sent by the server (starts with id, version, ...) and store the values to variables (working fine)
                {   
                    char *label = strtok_s(rawVariable, "=", &next_token);
                    char *pId = strtok_s(NULL, "\n", &next_token);  
                    rawVariable = strtok_s(NULL, "", &next_token);
                    strcpy_s(id, pId);

                    char message[256] = "Qh57=1"; // Setting up message structure... (the server should do an action when it receives "Qh57=1\r\n" but it doesn't...) here is my problem...
                    appendTextToEdit(hDebug, "Message sent : ");
                    appendTextToEdit(hDebug, message);
                    char *endline = "\r\n";
                    strcat_s(message, endline); 

                    string messageString(message);          
                    int messageLength = ((messageString.length()) - 2); // Get outgoing string length (-2)                          
                    int sent = send(Socket, message , messageLength , 0);               

                    char buffer [33]; // Display number of bytes sent...
                    _itoa_s(sent,buffer,10);                    
                    appendTextToEdit(hDebug, " (");
                    appendTextToEdit(hDebug, buffer);
                    appendTextToEdit(hDebug, " bytes sent)\n");
                }

                if( strstr(rawVariable, "version") != NULL)
                {
                    char *label = strtok_s(rawVariable, "=", &next_token);      
                    char *pVersion = strtok_s(NULL, "\n", &next_token); 
                    rawVariable = strtok_s(NULL, "", &next_token);      
                    strcpy_s(version, pVersion);    
                }                                       

                appendTextToEdit(hDebug, "End of FD_READ\n");           
            }
            break;

            case FD_WRITE:
            {
                appendTextToEdit(hDebug, "FD_WRITE event...\n");

                appendTextToEdit(hDebug, "End of FD_WRITE\n");
            }
            break; 

            case FD_CLOSE:
            {   
                statusText = "Disconnected from server";
                connectStatus = FALSE;
                InvalidateRect(hWnd, NULL, TRUE);
//              SendMessage(hWnd, WM_DESTROY, NULL, NULL); // Final version...                  
            }
            break;
        }
        break; 
    }
    break;

    case WM_DESTROY:
    {
        closesocket(Socket);
        WSACleanup();
        PostQuitMessage(0);
        return 0;
    }
    break;
}   
return DefWindowProc(hWnd,msg,wParam,lParam);
}

問題是您正在向服務器發送不完整的消息,因此它不會執行任何操作。 根據您自己在代碼中的注釋:

the server should do an action when it receives "Qh57=1\r\n" but it doesn't...

它永遠不會收到\\r\\n ,因為你永遠不會發送它:

char message[256] = "Qh57=1";
...
char *endline = "\r\n";
strcat_s(message, endline); // <-- you do append a CRLF...

string messageString(message);          
int messageLength = ((messageString.length()) - 2); // <-- but you subtract the CRLF from the message length...
int sent = send(Socket, message , messageLength , 0); // <-- so the CRLF is not sent!

不要從消息長度中減去CRLF:

int messageLength = messageString.length();

順便說一下,使用std:string來計算消息長度是浪費內存。 您可以使用strlen()代替:

int messageLength = strlen(message);

流套接字連接對消息邊界一無所知。 它只傳輸字節,就像RS232連接一樣。 如果你仔細閱讀recv的文檔,你會發現一個成功的recv調用可以在一個字節和指定的緩沖區大小之間的任何地方“返回”(=復制到緩沖區) - 這取決於已經接收了多少數據。 recv s返回值將告訴您已復制了多少。 如果跟隨更多數據,則將通過后續的recv調用返回。 這意味着如果服務器發送id=3\\nversion=10.0.5\\nQh57=0\\nQs323=-999999\\n您的第一次recv調用可能會返回id=3\\nversion=10.0.5\\nQh ,第二次可能會返回57=0\\nQs323=-999999\\n 或者你可以用3個塊來獲得它。 或者4.或者甚至一次一個字節。

解決這個問題的最簡單的方法,不幸的是性能非常差,是重復調用recv ,緩沖區大小為一個字節。 直到你看到你收到了整條信息(在你的例子中,這將是在收到4 \\n字符之后)。

首選的方式是:

  1. 使用更大的緩沖區調用recv
  2. 追加許多字節recv返回到“消息組裝緩沖區”
  3. 如果“消息程序集緩沖區”包含至少一個完整消息:處理並刪除它。 重復此操作,直到處理完所有消息並將其刪除。
  4. 轉到(1)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM