Thread: UDP Sockets (linux)

  1. #1
    Registered User
    Join Date
    Nov 2011
    Posts
    20

    UDP Sockets (linux)

    Hey all,

    I'm developing a program that uses UDP sockets, the main program code and its other interfaces are fine, and work 100% as intended. However, this is my first foray into working with UDP sockets, and i have a question or two.

    As UDP is a connectionless protocol, if i create a socket, send a packet to a server, immediately store the socket object(created from a class) in a vector, would i be able to write a loop, which would keep checking for data until it received what was requested then close and delete the socket? (im not entirely sure if UDP sockets can send replies, and that the server would reply to the same socket).

    If the above is possible, would i be able to use the non-blocking and select() commands like a standard sock_stream socket to check for when data has arrived? so that i dont have to "pause" the entire program to wait for a response?

    Thats all i need for now. Thanks for your help

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Well you should be using sendto() and recvfrom() for UDP.

    Plus you can still use select() with the descriptor (on the receive end) to wait for a message, then call recvfrom() to fetch it. This is basically the same as what you would do with TCP and recv().
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Nov 2011
    Posts
    20
    I'll be honest, the server im planning to connect to, is a valve source server, so i wont have access to the server sockets. The program im writing is essentially a client. im just wondering if i can still use the select commands for checking if data is available on it.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    select() on Unix/Linux doesn't care.
    If you've got a descriptor, you can use it in select() calls.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Nov 2011
    Posts
    20
    Thanks, ive managed to get this functional now. Now my next step is to understand the UDP packets properly. Do you, or anyone else have any useful links for someone new to start learning about UDP packets, their contents/what they consist of etc. Any information at this point is helpful. I already have access to the structure of the packets being sent to me. I'm just unsure of how to use this information to construct my own packet, or parse any packets it sends me.

    Also.. based on the UDP recvfrom. like this:

    result = recvfrom(sockfd, buf1, 1024, 0, (struct sockaddr *) &udp_fromaddr, &length);

    is there anyway i could return both udp_fromaddr and length, and if there is, how would i call the RecvUDP function the recvfrom is in?

    ie using a struct.

    Code:
    struct UDPData
    {
        struct sockaddr_in udp_fromaddr;
        unsigned int length;
    };
    Code:
    return udp_fromaddr, length;
    Code:
    UDPData temp = RecvUDP();
    ^^ Just tried the above and got errors, can anyone help me out with returning the two sets of data?
    Thanks again!
    Last edited by avidkaos999; 01-09-2012 at 05:42 PM.

  6. #6
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Quote Originally Posted by avidkaos999 View Post
    is there anyway i could return both udp_fromaddr and length, and if there is, how would i call the RecvUDP function the recvfrom is in?

    ie using a struct.

    Code:
    struct UDPData
    {
        struct sockaddr_in udp_fromaddr;
        unsigned int length;
    };
    Code:
    return udp_fromaddr, length;
    Code:
    UDPData temp = RecvUDP();
    ^^ Just tried the above and got errors, can anyone help me out with returning the two sets of data?
    Thanks again!
    you can just construct a UDPData object and return it like anything else. no need to try to use a comma to return multiple values, which isn't allowed anyway. python allows that, but not C++.

    Code:
    UDPData myUdpData = { udp_fromaddr, length };
    return myUdpData;

  7. #7
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    or you can just do the same as recvfrom function - return the length and pass the addr by pointer as an out parameter
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  8. #8
    Registered User
    Join Date
    Nov 2011
    Posts
    20
    Thanks for that Elkvis, worked beautifully. Now returning the data that i require .

    Time to start googling for more information regarding constructing and parsing UDP data packets, any links appreciated to anything useful, even sample source

  9. #9
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Quote Originally Posted by avidkaos999 View Post
    Time to start googling for more information regarding constructing and parsing UDP data packets, any links appreciated to anything useful, even sample source
    what do you mean "parsing UDP data"?
    recvfrom already brings you the content of the packet stripping all the protocol specific headers... You get the buffer constructed by the application on the other side - it is application specific. So parsing of the data will depend on the application you are communicating with
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  10. #10
    Registered User
    Join Date
    Nov 2011
    Posts
    20
    Ah didnt realise it stripped the headers. Alright well, the packet im currently receiving is; https://developer.valvesoftware.com/...urce_servers_2. The A2S_INFO response. The UDP socket is receiving the packet, ive just no idea where to start with parsing the buffer contents. Can't see to find much information with my google search terms. Just cant see how you can create a struct to store the information from the buffer. Did i mention im still a noob at c++?

  11. #11
    Registered User
    Join Date
    Nov 2011
    Posts
    20
    Been tinkering. with a for loop..

    Code:
    for (i = 5, i <= sizeof(buffer), i++)
    {
        cout << "Char: " << buffer[i] << "\n";
    }
    it does print the data within it, char by char, which includes some bytes (which look weird with cout). However, just using cout to display buffer, doesnt output anything?
    Am i missing something?

  12. #12
    Registered User
    Join Date
    Nov 2011
    Posts
    20
    Excuse the code below, its copied and pasted from my main program so most of these headers could probably be snipped etc.

    Code:
    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <vector>
    #include <string>
    #include <sys/time.h>
    #include <arpa/inet.h>
    
    using namespace std;
    
    #define A2S_INFO "\xFF\xFF\xFF\xFF\x54Source Engine Query"
    #define A2S_INFO_LENGTH 25
    
    int main(int argc, char *argv[])
    {
        int sockfd, a, flags;
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        flags = fcntl(sockfd, F_GETFL);
        a = fcntl(sockfd, F_SETFL,O_NONBLOCK);
        if (sockfd)
        {
            struct hostent *udptoaddr;
            struct sockaddr_in udp_toaddr;
            struct sockaddr_in udp_fromaddr;
            unsigned int length;
    
            string address;
            int port;
    
            address = "109.70.149.41";
            port = 27000;
    
            udp_toaddr.sin_family = AF_INET;
            udptoaddr = gethostbyname(address.c_str());
            if (udptoaddr == 0)
            {
                cout << "Host Error\n";
            } else {
                // Success!
            }
            bcopy((char *)udptoaddr->h_addr, (char *)&udp_toaddr.sin_addr, udptoaddr->h_length);
            udp_toaddr.sin_port = htons(port);
                
            struct timeval waitd;
            fd_set sendfds;
            FD_ZERO(&sendfds);
    
            waitd.tv_sec = 2;
            waitd.tv_usec = 0;
    
            int err;
            FD_SET(sockfd, &sendfds);
            length=sizeof(struct sockaddr_in);
            err = select(sockfd+1, NULL, &sendfds, (fd_set*)0, &waitd);
            if (FD_ISSET(sockfd, &sendfds)) {
                // Ready so send data
                char sendbuf[80];
                int result;
                result = sendto(sockfd, A2S_INFO, A2S_INFO_LENGTH, 0, (const struct sockaddr *)&udp_toaddr, length );
                if (result < 0) {
                    cout << "SEND RESULT (" << errno << "): " << strerror(errno) << "\n";
                }
                FD_CLR(sockfd, &sendfds);
                // Now Perform another check and loop till data received.
                bool data;
                data = false;
    
                while (!data) {
                    struct timeval waitd;
                    fd_set readfds;
                    FD_ZERO(&readfds);
                    waitd.tv_sec = 2;
                    waitd.tv_usec = 0;
                    int err;
        
                    FD_SET(sockfd, &readfds);
                    err = select(sockfd+1, &readfds, NULL, (fd_set*)0, &waitd);
                    if (FD_ISSET(sockfd, &readfds)) {
                        // Data Waiting
                        int result;
                        char buf1[1024];
                        result = recvfrom(sockfd, buf1, 1024, 0, (struct sockaddr *) &udp_fromaddr, &length);
                        cout << "Bytes Received: " << result << "\n";
        
                        string newbuf;
                        newbuf = buf1;
                        cout << "cout full buffer: " << newbuf << "\n";
    
                        cout << "Press any key to display char by char buffer\n";
                        cin.get();
    
                        int loop;
                        for (loop = 0; loop < result; loop++)
                        {
                            cout << buf1[loop] << "\n";
                        }
                        cout << "End of Char by Char Display\n";
                        // Received Packet. Send it back for parsing. but HOW?
        
                        FD_CLR(sockfd, &readfds);
                
                        data = true;
                    } else        
                    {
                        // No DATA
                        bool data;
                        data = false;
                    }
            }
            } else
            {
                return -1;
            }
    
        } else {
            return -1;
            // Socket Error
        }
    };
    This is the code so far in my testing cpp source file. As you can see, this version isnt using structs and such and relies on the basics to reduce problems with my other coding (if any). Try it out, Program will receive a UDP response from a Left4Dead2 server, display the output, then quit. This is to show you what im referring to, and the buffer that i need to parse and contain into a struct. Preferably with the struct being:

    Code:
    struct A2SReplyPacket{
        unsigned char Type;
        unsigned char Version;
        char ServerName[256];
        char Map[32];
        char GameDirectory[32];
        char GameDescription[256];
        short AppID;
        unsigned char Players;
        unsigned char MaxPlayers;
        unsigned char Bots;
        unsigned char Dedicated;
        unsigned char OS;
        unsigned char Password;
        unsigned char Secure;
        char GameVersion[32];
    
    };
    If anyone could provide assistance id be extremely grateful. Starting to tear my hair out now ;(

  13. #13
    Registered User
    Join Date
    Nov 2011
    Posts
    20
    Still not got anywhere with this at all. Tried using a for loop checking each char that get returned for NULL. Only problem with that is only the strings are ended with the NULL character. the integers/bytes are not. Same applies to using strtok too

    Anyone else able to shed some light on how i should go about parsing the buffer?

  14. #14
    Registered User
    Join Date
    Nov 2008
    Posts
    127
    It looks like you already have the structure of the buffer. So just cast your buffer to a pointer of that struct and access the values you are interested in. You will most likely have to ntohs the AppID, but you can access everything else directly.

  15. #15
    Registered User
    Join Date
    Nov 2011
    Posts
    20
    And now to sound like a noob again, but how would i accomplish that?

    ReplyPacket test;
    test = buf1;

    and different variations of it return conversion errors. Would the problem lie within the fact that im storing the recvfrom in a buffer thats declared like buf1[1024] and not a char pointer? If that makes sense?

    Declaring buf1 as a pointer before the recvfrom and out of the while loop, "char *buf1;" and changing the following, results in a segfault:

    Code:
    result = recvfrom(sockfd, &buf1, 1024, 0, (struct sockaddr *) &udp_fromaddr, &length);
                        //result = recv(sockfd, &buf1, 1024,0);
                        cout << "Error (" << errno << "): " << strerror(errno) << "\n";
                        cout << "Result: " << result << "\n - Why do i segfault now?\n";
                        cout << "Data: " << buf1 << "\n";
    Returned the code back to the previous post and attempted this:

    Code:
    ReplyPacket *temp;
    temp = (ReplyPacket *) buf1;
    cout << "Struct" << temp->hostname << "\n";
    However this doesnt copy the contents of buf1 into the appropriate struct members. Perhaps it would help more if someone would tell me what i should be reading up on here? type casting?
    Last edited by avidkaos999; 01-11-2012 at 03:56 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. UDP Sockets in C under Linux
    By goodsamaritan in forum C Programming
    Replies: 6
    Last Post: 03-23-2011, 06:02 AM
  2. Linux Sockets in C
    By Scythed in forum C Programming
    Replies: 8
    Last Post: 11-07-2010, 10:23 AM
  3. sockets in linux question
    By matrixon in forum Networking/Device Communication
    Replies: 6
    Last Post: 08-27-2006, 01:48 PM
  4. Segfault with C sockets on Linux
    By arti_valekar in forum C Programming
    Replies: 3
    Last Post: 02-16-2005, 02:25 AM
  5. Need help with Linux sockets
    By junbin in forum Linux Programming
    Replies: 1
    Last Post: 07-21-2002, 12:42 PM