0

Sorry in advance, but please explain, how to use IOCP with WSASend - for example, to send a simple message to the server and receive a response from it.

I am doing this:

  1. Create completion port
  2. Create threads for the completion port
  3. I create a WSASocket, with the Overlaped flag.
  4. I establish a connection with a remote server using WSAConnect
  5. I bind the socket to the completion port.
  6. I send a message to the server - by calling the WSASend function.

Like this:

void My_func_for_Thread(HANDLE iocp)
{

    DWORD my_DWORD;
    PULONG_PTR   my_CompletionKey;

    WSAOVERLAPPED* my_WSAOVERLAPPED_1;


    int my_GetLastError;


    while (1)
    {

        BOOL my_BOOL_GetQueuedCompletionStatus = GetQueuedCompletionStatus(iocp, &my_DWORD, my_CompletionKey, &my_WSAOVERLAPPED_1, INFINITE);


        my_GetLastError = GetLastError();

   
            if (my_BOOL_GetQueuedCompletionStatus == FALSE)
            {
                std::cout << "GetQueuedCompletionStatus== FALSE" << std::endl;
            }

      }
}

int main()
    {
    
    //-------------------------------------------------------------------Create port IO-------------------------------------------------------------
    
        int Number_Threads = 4;  
    
        HANDLE My_handle_IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, Number_Threads);
    
    //------------------------------------------------------------------------------------------------------------------------------------------------
    
    
    
    //-------------------------------------------------------------------Create thread for IOCP-------------------------------------------------------
    
        std::vector<HANDLE>my_vector_Thread;
    
    
        for (int i = 0; i < Number_Threads; i++)
        {
            my_vector_Thread.push_back(CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&My_func_for_Thread, My_handle_IOCP, 0, 0));
        }
    
    
    //---------------------------------------------------------------------------------------------------------------------------------------------------
    
   
    
    //-------------------------------------------------------------------Initialization Winsock-----------------------------------------------------------
    
    WORD my_version_Winsock = MAKEWORD(2, 2); 
    
    WSADATA my_wsadata_struct; 
    
    int my_WSAStartup = WSAStartup(my_version_Winsock, &my_wsadata_struct);
    
    //------------------------------------------------------------------------------------------------------------------------------------------------------
    
    
    
    //-------------------------------------------------------------------Create WSASocket-----------------------------------------------------------------
    
    SOCKET my_WSASocketA = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
    
    //--------------------------------------------------------------------------------------------------------------------------------------------------------
    
    
    
    //-------------------------------------------------------------------
    
        std::string myString_IP = "XX.XXX.XX.XXX";
    
    //--------------------------------------------------------------------------
    
        sockaddr my_sockaddr;                 
    
        my_sockaddr = { 0 };                                            
        my_sockaddr.sa_family = 2;                                          // AF_INET.
        inet_pton(AF_INET, myString_IP.c_str(), &my_sockaddr.sa_data[2]);   
        my_sockaddr.sa_data[1] = 80;                                     //http port
    
        //--------------------------------------------------------------------------
    

    
        int status_WSAConnect = WSAConnect(my_WSASocketA, &my_sockaddr, sizeof(my_sockaddr), NULL, NULL, NULL, NULL);
    
    
    //-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
    
    
    
    //-------------------------------------------------------------------Bind socket and port--------------------------------------------------------
    
        My_handle_Create_IOCP = CreateIoCompletionPort((HANDLE)my_WSASocketA, My_handle_IOCP, NULL, 0);
    
    //-------------------------------------------------------------------------------------------------------------------------------------------------
    
    
    
    
    //-------------------------------------------------------------------Send message to serever with WSASend-----------------------------------
    
             WSABUF my_WSABUF;
    
             std::string request_text_string = "GET / HTTP/1.1\r\nHost: government.ru\r\nConnection: keep-alive\r\n\r\n";
    
    
             my_WSABUF.buf = &request_text_string[0];
             my_WSABUF.len = request_text_string.size();
    
    
             WSAOVERLAPPED my_WSAOVERLAPPED;
             my_WSAOVERLAPPED = { 0 };
    
    
    
             int my_WSASend =  WSASend(my_WSASocketA, &my_WSABUF, 1, NULL, 0, &my_WSAOVERLAPPED, NULL);
    
    
                     if (my_WSASend == SOCKET_ERROR)
             {
                       std::cout << "WSASend == SOCKET_ERROR:" std::endl;
                     }
    
                      if (my_WSASend == 0)
             {
                       std::cout << "WSASend == 0: no error" std::endl;
                     }
    
    
    //-----------------------------------------------------------------------------------------------------------------------------------------------------------
    
    
    
             Sleep(10000);
    
    
             //---------------------------------------------------------
             for (int i = 0; i < Number_Threads; i++)
             {
                 CloseHandle(my_vector_Thread[i]);
             }
             //---------------------------------------------------------
    
    }

But, GetQueuedCompletionStatus always return 998 - Invalid access to memory location. Which memory is the disabled access to? What am I doing wrong ?

Optimus1
  • 444
  • 2
  • 13
  • @Remy Lebeau, where do you see the error? Visual studio compiles the code without errors. – Optimus1 May 15 '21 at 17:50
  • that is because you are using a typecast to *force* the compiler to accept bad code. Remove the typecast and the compiler will complain about the mismatch – Remy Lebeau May 15 '21 at 18:07
  • `WSAConnect` not asynchronous api and must not be used in this case. use `AcceptEx`. instead use self thread pool- better use system thread pool and bind socket to it - via `BindIoCompletionCallback` or `CreateThreadpoolIo`. but really write such code from zero and correct handle all case - not simply task. better have/use some ready lib for asio and for sockets. and use it – RbMm May 15 '21 at 18:34

1 Answers1

1

There are several problems with your code.

Your My_func_for_Thread() function has the wrong signature for use with CreateThread(). The compiler is not complaining because you are using a typecast to silence the compiler from failing.

You are passing an uninitialized pointer to the lpCompletionKey parameter of GetQueuedCompletionStatus(). It expects a pointer to a valid ULONG_PTR variable for it to write to.

The WSAOVERLAPPED needs to remain active in memory until its final status is retrieved from the IOCP queue. But your thread is sleeping much longer than your main() is running. You should allocate the WSAOVERLAPPED dynamically, and then free it when its status is received.

Try something more like this instead:

DWORD WINAPI My_func_for_Thread(LPVOID lpParameter)
{
    HANDLE iocp = (HANDLE) lpParameter;
    DWORD my_DWORD;
    ULONG_PTR  my_CompletionKey;
    WSAOVERLAPPED* my_WSAOVERLAPPED_1;
    DWORD my_GetLastError;

    while (TRUE)
    {
        BOOL my_BOOL_GetQueuedCompletionStatus = GetQueuedCompletionStatus(iocp, &my_DWORD, &my_CompletionKey, (LPOVERLAPPED*) &my_WSAOVERLAPPED_1, INFINITE);

        my_GetLastError = GetLastError();

        if (my_BOOL_GetQueuedCompletionStatus)
        {
            delete my_WSAOVERLAPPED_1;
        }
        else
        {
            std::cout << "GetQueuedCompletionStatus == FALSE" << std::endl;
        }
    }

    return 0;
}

int main()
{
    ...

    for (int i = 0; i < Number_Threads; i++)
    {
        HANDLE hThread = CreateThread(NULL, 0, &My_func_for_Thread, My_handle_IOCP, 0, NULL);
        if (hThread)
            my_vector_Thread.push_back(hThread);
    }

    ...

    std::string request_text_string = "GET / HTTP/1.1\r\nHost: government.ru\r\nConnection: keep-alive\r\n\r\n";

    WSABUF my_WSABUF;
    my_WSABUF.buf = &request_text_string[0];
    my_WSABUF.len = request_text_string.size();

    WSAOVERLAPPED *my_WSAOVERLAPPED = new WSAOVERLAPPED;
    *my_WSAOVERLAPPED = { 0 };

    int my_WSASend =  WSASend(my_WSASocketA, &my_WSABUF, 1, NULL, 0, my_WSAOVERLAPPED, NULL);

    // wait for request to finish...
    // wait for response to arrive...
    // close socket...
    // wait for threads to terminate...

    ...

    for (size_t i = 0; i < my_vector_Thread.size(); i++)
    {
        CloseHandle(my_vector_Thread[i]);
    }

    return 0;
}       
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • error due to uninitialized lpCompletionKey ? How do I need to initialize it? What value? – Optimus1 May 15 '21 at 17:53
  • But this is the same code. :) Just why is memory allocated through the new WSAOVERLAPPED and and the parameter is passed to the function LPARAM for the place HANDLE ICOP. The rest of the code is the same, not working. – Optimus1 May 15 '21 at 18:23
  • @Optimus1 no, it is not the same code, look at the differences more carefully. And read the documentation for these API functions more carefully. – Remy Lebeau May 15 '21 at 18:27
  • Why did you change the function type? This is not a call back, this is my function, just run in separate threads. – Optimus1 May 15 '21 at 18:39
  • 1
    @Optimus1 as I told you, your original function had the wrong signature for `CreateThread()`. [Read the documentation](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms686736(v=vs.85)). And yes, it is a kind of callback. `CreateThread()` creates a thread, which then calls your function. – Remy Lebeau May 15 '21 at 18:42
  • Remy Lebeau, Now I understand, thnak you, but not a call back, but just a DWORD, that is, int. – Optimus1 May 15 '21 at 18:46
  • 1
    @Optimus1 The thread proc is a callback. if you don't understand what that is, read up about it. As for `CALLBACK`, in this case that refers to the *calling convention*, ie `__stdcall`. Same with `WINAPI`. – Remy Lebeau May 15 '21 at 19:29