recvfrom() never returns

Started by
6 comments, last by Captain_Thunder 16 years, 8 months ago
I've just started learning how to do network programming (on Linux), and I'm trying to make a simple UDP client/server (both running on the same machine). While the server runs forever, the client sends a message, receives and prints a response, and then shuts down. However, the client never does any output, but just runs infinitely. I've used the debugger to determine that both the client and the server get stuck in an infinite loop on the calls to recvfrom(). I'm not really sure what the problem could be, so if anyone could help, I'd appreciate it. server.c:

#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>

char* handlemsg(char msg[]);

int main()
{
        int sock, newsock;
        char msg[255];
        struct sockaddr_in my_addr;
        struct sockaddr_in their_addr;
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock == -1)
        {
                printf("Error creating socket");
                exit(1);
        }
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = 5955;
        my_addr.sin_addr.s_addr = INADDR_ANY;
        int err = bind(sock, (struct sockaddr *)&my_addr, sizeof my_addr);
        if (err == -1)
        {
                printf("Error calling bind");
                exit(1);
        }
        listen(sock, 20);
        int sin_size = sizeof their_addr;
        for (;;)
        {
                recvfrom(sock, msg, 254, 0,
                        (struct sockaddr *)&their_addr,
                        sizeof(struct sockaddr));
                char* output = handlemsg(msg); /*Write*/
                sendto(sock, output, strlen(output), 0,
                        (struct sockaddr *)&their_addr,
                        sizeof(struct sockaddr));
        }
        return 0;
}

char* handlemsg(char msg[])
{
        if (msg[0] == 't')
        {
                return "ok";
        }
        else return "I got the message.";
}

client.c:

#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
        int sock;
        struct sockaddr_in their_addr;
        struct sockaddr_in addr;
        char output[255];
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock == -1)
        {
                printf("Error calling socket");
                exit(1);
        }
        their_addr.sin_family = AF_INET;
        their_addr.sin_port = htons(5955);
        their_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
        int err = sendto(sock, argv[1], strlen(argv[1]), 0,
                (struct sockaddr *)&their_addr,
                sizeof (struct sockaddr));
        if (err == -1)
        {
                printf("Error with sendto");
                exit(1);
        }
        recvfrom(sock, output, 254, 0, (struct sockaddr *)&addr,
                sizeof (struct sockaddr));
        for (int i = 0; i < 255; ++i)
                printf("%c", output);
        return 0;
}

Advertisement
recvfrom is a blocking call that stalls the program until information becomes available, so it looks like the data isn't getting to your server, make sure you allow UDP traffic on the port your program uses to ensure the packets aren't getting blocked from your program.

Generally if you don't want to stop on recv calls you use select on the socket first to ensure there's data ready to read before calling recv on it to allow your program to continue executing even when there's no data available.
I turned off NAT, but both programs still run indefinitely (I know that recvfrom is a blocking call, but the client is supposed to eventually shut down anyway), which leads me to believe that the problem is with my code. I have made a few changes (removed unnecessary call to listen() in the server, tried a few different addresses in the client), but it's still mostly the same.

I think I might be making a request to the wrong IP addresses in the client; is there any way I can print out the value of my_addr.sin_addr.s_addr in the server?
Disclaimer: I haven't done network programming in anything lower than Java


But I'm looking at your port you're assigning and wondering if it's correct, as in the client you change the byte ordering(with htons) but you don't on the server, is this causing them to send and receive on different ports? (maybe use wireshark to ensure it)
You can specify a socket to be non-blocking.

an example

	bool C_Socket::setBlocking(bool enableBlocking)	{		if(m_socket == INVALID_SOCKET || !m_ok)			return false;		DWORD nonBlock = enableBlocking ? 0 : 1;		return success(::ioctlsocket(m_socket, FIONBIO, &nonBlock));	}

Everything is better with Metal.

Quote:Original post by oliii
You can specify a socket to be non-blocking.


I know, but that's not what I'm trying to do. The server is supposed to run forever, and the client is supposed to send and receive a message. I run both of them at the same time; making the socket non-blocking would defeat the purpose.

Anyway, I tried having both port numbers go through htons(), and both without, and still nothing. I don't know why, but I think it's a problem with the IP address; when I tried using inet_addr() instead of INADDR_ANY in the server, bind() fails with errno set to 99 (cannot assign requested address).

I'm getting kind of desperate here, so I'd appreciate any help.
Looking at the initial code, the biggest problem is that you're not using htons() on the port number in the server.

Assuming you fix that, I'd like to know what OS you're running on. If it's Windows, then you should call WSAStartup() before any socket functions. Also, Windows is well known for having a built-in firewall which will prevent these kinds of servers from working by default.

If it's not Windows, you still need to fix the port number, and check for firewalls (MacOS X and various unixen can have that problem, too).

Binding to INADDR_ANY is the right thing to do, btw -- you shouldn't need to bind to a specific address.


Anyway, I tried your program, and the compiler complained about the following things:

1) You're returning to char * from a string constant. This is a warning.
2) You're passing an integer as the socklen pointer argument of recvfrom() in the server. This is an error.
3) You're having the same problem with recvfrom() socklen in the client.

After fixing the htons() part and fixing problems 2) and 3), your programs work on my Linux box. Note that you're printing 255 characters, not just the received characters, in the reply part. Also note that you're looking at the first character of the message in the server, without verifying that you received at least one character. A client could send zero bytes, which means you would read random stack trash (or, more likely, whatever the LAST client was sending).

Moral of the story: Always turn on as many compiler warnings as possible, and always heed them!
enum Bool { True, False, FileNotFound };
Thanks for all your help, hplus; it's working fine now. I was getting those warnings about the arguments to recvfrom, but I didn't bother to fix them. Lesson learned [grin]

This topic is closed to new replies.

Advertisement