2

I have a UDP socket application where I am working on the server side. To test the server side I put together a simple python client program that sends the message "hello world how are you". The server, should then receive the message, convert to uppercase and send back to the client. The problem lies here: I can observe while debugging that the server is receiving the message, applies the conversion, sends the response back and eventually waits for another message. However the python client is not receiving the message but wait's endlessly for the response from the server.

I found (an option) through the web that in order for the client to receive a response back it needs to bind to the server, which goes against what I have seen in a text book (The Linux Programming Interface). Nevertheless, I tried to bind the client to the server and the python program failed to connect at the binding line (don't know if I did it correctly). Python version is 2.7.5. The client program runs on RedHat and the server runs on a target module with Angstrom (it's cross compiled for a 32 bit processor).

Here is the code for the client:

import socket
import os

UDP_IP = "192.168.10.4"
UDP_PORT = 50005

#dir_path = os.path.dirname(os.path.realpath(__file__))

MESSAGE = "hello world how are you"

print "UDP target IP: ", UDP_IP
print "UDP target port: ", UDP_PORT
print "message: ", MESSAGE

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#sock.bind((UDP_IP, UDP_PORT))
print "Sending message..."
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
print "Message sent!"
print "Waiting for response..."
data = sock.recv(1024)

print "Received", repr(data)

And here is the code for the server:

void server_side(void)
{
    printf("Server start up.\n");

    struct sockaddr_in svaddr;
    struct sockaddr_in claddr;
    int sfd;
    int j;
    ssize_t numBytes;
    socklen_t len;
    char buf[BUF_SIZE];
    char claddrStr[INET_ADDRSTRLEN];

    //int output = open("test_output.txt", O_WRONLY|O_CREAT, 0664);

    printf("Creating new UDP socket...\n");

    sfd = socket(AF_INET, SOCK_DGRAM, 0);   /* Create Server Socket*/
    if (sfd == -1)
    {
        errExit("socket");
    }

    printf("Socket has been created!\n");

    memset(&svaddr, 0, sizeof(struct sockaddr_in));

    svaddr.sin_family = AF_INET;
    svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    svaddr.sin_port = htons(PORT_NUM);

    printf("Binding in process...\n");

    if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_in)) 
                == -1)
    {
        errExit("bind");
    }

    printf("Binded!\n");

    /* Receive messages, convert to upper case, and return to client.*/

    for(;;)
    {
        len = sizeof(struct sockaddr_in);
        numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
                (struct sockaddr *) &claddr, &len);
        if (numBytes == -1)
        {
            errExit("recvfrom");
        }
        if (inet_ntop(AF_INET, &claddr.sin_addr, claddrStr,
                INET_ADDRSTRLEN) == NULL)
        {
            printf("Couldn't convert client address to string.\n");
        }
        else
        {
            printf("Server received %ld bytes from (%s, %u).\n", (long)
                numBytes,
                    claddrStr, ntohs(claddr.sin_port));
        }

        claddr.sin_port = htons(PORT_NUM);

        for (j = 0; j< numBytes; j++)
        {
            buf[j] = toupper((unsigned char) buf[j]);
        }

        if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len)
               != numBytes)
        {
            fatal("sendto");
        }
    }
}

Again the problem is I am not receiving the response and printing the message back on the client terminal. I should receive the same message in all uppercase letters. I feel like I am missing a small detail. Thanks for the help!

  • I had already a similar problem and I solve it by sending the '\0' character at the end of the response to indicate the end of the message. – Eduardo Soares Jan 22 '19 at 16:05
  • 1
    @EduardoSoares shouldn't `sendto` attach a '\0' to the end of the message? Even so; I think `recv` in the python program should at least print what ever it receives. I'll try just in case this is the problem. – N. Dijkhoffz Jan 22 '19 at 18:53
  • I used to think that sendto already did this, but as I said I had a similar problem of a client waiting forever for the answer and I resolved this way. – Eduardo Soares Jan 22 '19 at 19:01
  • @EduardoSoares so two things. I see that the `buf` array actually ends in a `\0`. I add the `\0` any ways and it makes no difference. I did also see that the numBytes is off by one character which is the `\0`. During debugging I manually change the numBytes value to 24 instead of 23, but the client still does not receive a response. – N. Dijkhoffz Jan 22 '19 at 20:46

2 Answers2

2

Quick and dirty:

Remove this line from your C code:

claddr.sin_port = htons(PORT_NUM);

Now why:

When you send a message in your python script, your operating system will fill a UDP packet with the destination IP address and port you specified, the IP address of the machine you are using on the source IP field, and finally, a source port assigned "randomly" by the OS. Do notice, this is not the same port as the destination port, and even if it was the case, how would you know what program will receive the source message?(both would be hearing messages from the same port) Luckily, this is not possible.

Now, when your C code receives this packet, it will know who sent the message, and you have access to this information though the sockaddr struct filled by recvfrom. If you want to send some information back, you must send the packet with a destination port(as seen by the server) equal to the source port as seen by the client, which again, is not the same port that you are listening on the server. By doing claddr.sin_port = htons(PORT_NUM), you set overwrite the field that contained the source port of the client with the server port, and when you try to send this packet, 2 things may happen:

  • If the client ran from the same computer, the destination IP and source IP will be the same, and you've just set the destination port to be the port that the server is listening, so you will have a message loop.
  • If running on different computers, the packet will be received by the client computer, but there probably won't be any programs waiting for messages on that port, so it is discarded.

A half-baked analogy: you receive a letter from a friend, but when writing back to him, you change the number of his house with the number of your house... does not make much sense. Only difference is that this friend of yours moves a lot, and each letter may have a different number, but that is not important.

In theory, you must bind if you want to receive data back, in this case bind is an equivalent to listening to that port. This answer clarifies why it was not necessary in this case: https://stackoverflow.com/a/14243544/6253527

If you are on linux, you can see which port you OS assigned for your UDP socket using sudo ss -antup | grep python

  • Thanks for the detailed explanation. I see your point. I removed the line of code. However the client still does not receive the response. Should I then try to bind the client to the server then? Again, what you say makes sense but just removing the line and sending the response to the same port the message was received should be enough. No binding required. – N. Dijkhoffz Jan 22 '19 at 20:49
  • Just to clarify, there is no binding to the server. UDP is a connectionless protocol, you send messages and forget about them. When we talk about bind here, we are binding to a port in an interface in your computer, so the OS reports us about packets received there. Then the server would send responses to that address. About you problem, strange, I copied the code and tested it here before posting, it worked after changing IP and removing the line. Are you on linux? If so, do `sudo ss -antup | grep python` when the client is waiting for the response. Also, post the output of your server. – Arthur Moraes Do Lago Jan 23 '19 at 10:30
  • Also, tell me if you get any compilation warnings while compiling the C client, verify the ip address, and tell us more about your setup(OS? each program running in a different system? same system? anything else?) – Arthur Moraes Do Lago Jan 24 '19 at 10:42
  • I apologize for the delay. It has been a few packed weeks at work. I will attempt your suggestions again and gather any output as soon as possible. Thanks for the help and patience. – N. Dijkhoffz Jan 30 '19 at 14:15
  • This ended up working. It turned out to be a mistake on my part. Thanks for the help. – N. Dijkhoffz Feb 28 '19 at 13:42
1

N. Dijkhoffz, Would love to hear how you fixed it and perhaps post the correct code.

MountainLogic
  • 308
  • 2
  • 11