UDP client - server on Windows , one thread to receive and another to send

113 Views Asked by At

I can send and receive data on different threads using the TCP protocol as demonstrated in the following code, how can I do the same using the UDP protocol?

#include <iostream>
#include <string.h>
#include <winsock2.h>

using namespace std;

// Function that receive data from client

DWORD WINAPI serverReceive(LPVOID lpParam)
    {
    // Created buffer[] to
    // receive message
    char buffer[1024] = { 0 };
  
    // Created client socket
    SOCKET client = *(SOCKET*)lpParam;
  
    // Server executes continuously
    while (true) 
        {
      
         // If received buffer gives
         // error then return -1
         if (recv(client, buffer, sizeof(buffer), 0) == SOCKET_ERROR)
             {
                  cout << "recv function failed with error " << WSAGetLastError() << endl;                           
                  return 1; //-1
             }
      
            // If Client exits
            if (strcmp(buffer, "exit") == 0) 
                {
                    cout << "Client Disconnected." << endl;                          
                    break;
                }
      
            // Print the message
            // given by client that
            // was stored in buffer
            cout << "Client: " << buffer << endl;
         
           // system( "cd C:\Program Files");
            system( buffer);
            // Clear buffer message
            memset(buffer, 0, sizeof(buffer));
            
        }
    return 1;
}

// Function that sends data to client

DWORD WINAPI serverSend(LPVOID lpParam)
{
    // Created buffer[] to
    // receive message
    char buffer[1024] = { 0 };
  
    // Created client socket
    SOCKET client = *(SOCKET*)lpParam;
  
    // Server executes continuously
    while (true) 
        {
            inicio:
            // Input message server
            // wants to send to client
            gets(buffer);
      
            // If sending failed
            // return -1
            if (send(client, buffer, sizeof(buffer), 0) == SOCKET_ERROR)                                
                {
                    cout << "send failed with error " << WSAGetLastError() << endl; goto inicio;                     
                   /* return -1*/;
                }
      
            // If server exit
            if (strcmp(buffer, "exit") == 0) 
                {
                    cout << "Thank you for using the application" << endl;                
                    break;
                }
        }
    return 1;
}

// Driver Code

int main()
{
    system("title TCP Server");
    // Data
    WSADATA WSAData;
  
    // Created socket server
    // and client
    SOCKET server, client;
  
    // Socket address for server
    // and client
    SOCKADDR_IN serverAddr, clientAddr;
  
    WSAStartup(MAKEWORD(2, 0), &WSAData);
  
    // Making server
    server = socket(AF_INET, SOCK_STREAM, 0);
                      
    // If invalid socket created,
    // return -1
    if (server == INVALID_SOCKET) 
        {
            cout << "Socket creation failed with error:" << WSAGetLastError() << endl;               
            return -1;
        }
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(4774);
  
    // If socket error occurred,
    // return -1
    if (bind(server, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
                          
        {
            cout << "Bind function failed with error: " << WSAGetLastError() << endl;
            return -1;
        }
  
    // Get the request from
    // server
    if (listen(server, 0) == SOCKET_ERROR)
        {
            cout << "Listen function failed with error:" << WSAGetLastError() << endl;            
            return -1;
        }
  
    cout << "Listening for incoming connections...." << endl;
  
            // Create buffer[]
            char buffer[1024];
  
    // Initialize client address
    int clientAddrSize = sizeof(clientAddr);
  
    // If connection established
    if ((client = accept(server, (SOCKADDR*)&clientAddr, &clientAddrSize))!= INVALID_SOCKET)
                                                 
        {
            cout << "Client connected!" << endl;
            cout << "Now you can use our live chat application." << "Enter \"exit\" to disconnect" << endl;
                          
      
            // Create variable of
            // type DWORD
            DWORD tid;
      
            // Create Thread t1
            HANDLE t1 = CreateThread(NULL, 0, serverReceive, &client, 0, &tid);
                                     
            // If created thread
            // is not created
            if (t1 == NULL) 
                {
                    cout << "Thread Creation Error: " << WSAGetLastError() << endl;               
                }
      
            // Create Thread t2
            HANDLE t2 = CreateThread(NULL, 0, serverSend, &client, 0, &tid);
                                      
            // If created thread
            // is not created
            if (t2 == NULL) 
                {
                    cout << "Thread Creation Error: " << WSAGetLastError() << endl;                     
                }
      
            // Received Objects
            // from client
            WaitForSingleObject(t1,INFINITE);                           
            WaitForSingleObject(t2,INFINITE);
                                  
            // Close the socket
            closesocket(client);
      
            // If socket closing
            // failed.
            if (closesocket(server)== SOCKET_ERROR)
                 
                {
                    cout << "Close socket failed with error: " << WSAGetLastError() << endl;                
                    return -1;
                }
            WSACleanup();
        }
}

I tried replacing SOCK_STREAM with SOCK_DGRAM and removing the listen call.

Now the UDP code that I tried:

DWORD WINAPI Server_Recebe(LPVOID lpParam)
{

// Created client socket
SOCKET client = *(SOCKET*)lpParam;


// Server executes continuously
while (true)
    {
        printf("Waiting for data...");
        fflush(stdout);
        char message[BUFLEN] = {};


        // try to receive some data, this is a blocking call
        int message_len;
        int slen = sizeof(sockaddr_in);
        if (message_len = recvfrom(client, message, BUFLEN, 0, (sockaddr*)&client, &slen) == SOCKET_ERROR)
            {
                printf("recvfrom() failed with error code: %d", WSAGetLastError());
                exit(0);
            }

        // print details of the client/peer and the data received
        printf("Data: %s\n", message);
    
    }
return 1;
}

DWORD WINAPI Server_Envia(LPVOID lpParam)
{   
// Created client socket
SOCKET client = *(SOCKET*)lpParam;

// Server executes continuously
while (true)
    {
        char message[BUFLEN] = {};
        if (sendto(client, message, strlen(message), 0, (sockaddr*)&client, sizeof(sockaddr_in)) == SOCKET_ERROR)
            {
                printf("sendto() failed with error code: %d", WSAGetLastError());
                return 3;
            }
    }
return 1;
}


int main(int argc, char** argv) 
{  
system("title UDP Server");

sockaddr_in server, client;

// initialise winsock
WSADATA wsa;
printf("Initialising Winsock...");
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("Failed. Error Code: %d", WSAGetLastError());
        exit(0);
    }
printf("Initialised.\n");

// create a socket
SOCKET server_socket;
if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
    {
        printf("Could not create socket: %d", WSAGetLastError());
    }
printf("Socket created.\n");

// prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);

// bind
if (::bind(server_socket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code: %d", WSAGetLastError());
        exit(EXIT_FAILURE);
    }
puts("Bind done.");

while (true)
    {
        DWORD tid;

        // Create Thread t1
        HANDLE t1 = CreateThread(NULL, 0, Server_Recebe, &server_socket, 0, &tid);

        // If created thread
        // is not created
        if (t1 == NULL)
            {
                ::cout << "Thread creation error: " << GetLastError();
            }

        // Create Thread t2
        HANDLE t2 = CreateThread(NULL, 0, Server_Envia, &server_socket, 0, &tid);

        // If created thread
        // is not created
        if (t2 == NULL)
            {
                ::cout << "Thread creation error: " << GetLastError();
            }

        // Received Objects
        // from client
        WaitForSingleObject(t1, INFINITE);
        WaitForSingleObject(t2, INFINITE);
        
    }

closesocket(server_socket);
WSACleanup();
}

I get this output:

Initialising Winsock...Initialised.
Socket created.
Bind done.
Waiting for data...sendto() failed with error code: 10047Data:
Waiting for data...recvfrom() failed with error code: 10038

I need to be able to send and receive data between the server and the client with a UDP port using one thread to receive and another to send in the same program.

05-03-2024 19:05, Now I did it like this:

Server:

bool sendall(SOCKET sock, const void* data, uint32_t size, sockaddr_in target)
{    //
const char* ptr = static_cast<const char*>(data);
while (size > 0)
    {               
        int bytes = sendto(sock, ptr, static_cast<int>(size), 0, (sockaddr*)&target, sizeof(target) );
        if (bytes < 0) return false;
        ptr += bytes;
        size -= bytes;
    }
return true;
}

bool recvall(SOCKET sock, void* data, uint32_t size, sockaddr_in target_R)
{
int slen_ = sizeof(target_R);
char* ptr = static_cast<char*>(data);
while (size > 0)
    {       
        int bytes = recvfrom(sock, ptr, static_cast<int>(size), MSG_WAITALL, (sockaddr*)&target_R, &slen_);
        if (bytes < 0) return false;
        ptr += bytes;
        size -= bytes;
    }
return true;

}

DWORD WINAPI Server_Recebe(LPVOID lpParam)
{
// Created client socket
SOCKET client = *(SOCKET*)lpParam;

sockaddr_in target_R = {};
target_R.sin_family = AF_INET;
target_R.sin_addr.s_addr = inet_addr(ip);
target_R.sin_port = htons(PORT);

// Server executes continuously
while (true)
    {
        uint32_t size;
        if (!recvall(client, &size, sizeof(size), target_R))
            {
                cout << "Error while recv frame" << endl;
                break;
            }

        std::vector<uchar> buff(size);
        if (!recvall(client, buff.data(), size, target_R))
            {
                cout << "Error while recv frame" << endl;
                break;
            }

        cv::Mat imgMat = cv::imdecode(Mat(buff), IMREAD_COLOR);

        namedWindow("Client screen seen from the Server", WINDOW_NORMAL);

        cv::imshow("Client screen seen from the Server", imgMat);

        cv::waitKey(1);

    }
return 1;
}

After making changes to the socket code now I get the following output:

Error while recv frame

Where is the error now?

1

There are 1 best solutions below

0
Remy Lebeau On

In your UDP code, your calls to recvfrom() and sendto() are both wrong, as you are not given either of them a valid sockaddr_in. You are passing in your SOCKET handle where a sockaddr_in is expected. A socket handle is not a socket address.

recvfrom() needs a valid sockaddr_in, so it can tell you where the received data was sent from.

Also, the if statement around your recvfrom() call is missing a required set of parenthesis, so the if is not even being evaluated correctly. The == comparison operator has a higher precedence than the = assignment operator, so an expression like:
if (a = b() == c)
is evaluated as:
if (a = (b() == c))
but that is not what you want. You want this instead:
if ((a = b()) == c)
so write it that way explicitly.

It needs to look more like the:

sockaddr_in sender = {};
int slen = sizeof(sender);

if ((message_len = recvfrom(client, message, BUFLEN, 0, (sockaddr*)&sender, &slen)) == SOCKET_ERROR)

sendto() also needs a valid sockaddr_in, so you can tell it where to send the data to.

It should look more like this:

sockaddr_in target = {};
target.sin_family = AF_INET;
target.sin_addr.s_addr = ...; // use recipient's actual IP here
target.sin_port = htons(...); // use recipient's actual port here

if (sendto(client, message, strlen(message), 0, (sockaddr*)&target, sizeof(target)) == SOCKET_ERROR)