1
votes

I've run into a problem with UDP sockets.

For this particular program, a single .c file needs to be both a client to another server (via TCP) and a server/client to itself (executed twice, running on separate servers). It will be running twice at the same time and since it needs to be able to do both the TCP connection (for one type of data) and the UDP connection (for a different type of data) at the same time it needed to be done with threads or forks, but I used threads.

The issue I'm having is the UDP sockets aren't receiving any datagrams from each other. There are no compilation errors and it runs fine, but there's no output other than general debugging print statements. It's getting stuck at the recvfrom commands.

The code below is separated into two parts (again, within the same .c file). The top portion is the server section, and the lower portion is the client section. This is all done WITHIN a thread. I tried creating the socket THEN calling the thread with the client code (the idea was the thread would communicate with the parent but it didn't matter), but it gets the same result. So, for now, the thread just handles the UDP connection and the parent deals with the TCP.

If you need any more explanation, please feel free to ask. This is for a school assignment so I can't give TOO much, but I'll say what I can.

Thank you!

QUICK EDIT: All this code below is doing is just sending hello to the server and back to the client. Further details are not needed.

Assume that argv->stuff is the struct that I passed to the thread and that the user provides the server, IP address, and port when executing.

//----- Server portion of code is below

int cli2_sockfd; 
char buffer_cli2[MAXLINE]; 
char *hello2 = "Hello from client 2"; 
struct sockaddr_in cli2_addr, client1_addr; 
int clis_portno = atoi(argv->port);
clis_portno = clis_portno + 1;

// Creating socket file descriptor 
if ( (cli2_sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 
    perror("socket creation failed"); 
    exit(EXIT_FAILURE); 
} 

memset(&cli2_addr, 0, sizeof(cli2_addr)); 
memset(&client1_addr, 0, sizeof(client1_addr)); 

// Filling server information 
cli2_addr.sin_family = AF_INET; // IPv4 
cli2_addr.sin_addr.s_addr = INADDR_ANY; 
cli2_addr.sin_port = htons(clis_portno); 

// Bind the socket with the server address 
if ( bind(cli2_sockfd, (const struct sockaddr *)&cli2_addr, 
        sizeof(cli2_addr)) < 0 ) 
{ 
    perror("bind failed"); 
    exit(EXIT_FAILURE); 
} 

while(1)
{
    int n2;
    socklen_t len2;
    if((n2 = recvfrom(cli2_sockfd, (char *)buffer_cli2, MAXLINE, 
                0, ( struct sockaddr *) &client1_addr, 
                &len2)) < 0)
    {
        perror("svr recvfrom");
        exit(EXIT_FAILURE);
    }

    buffer_cli2[n2] = '\0'; 
    printf("Client 1: %s\n", buffer_cli2); 

    if(sendto(cli2_sockfd, (const char *)hello2, strlen(hello2), 
        MSG_CONFIRM, (const struct sockaddr *) &client1_addr, 
            len2) < 0)
    {
        perror("svr sendto");
        exit(EXIT_FAILURE);
    }
    printf("Hello message sent.\n"); 
}


//----- The client portion of the code is below


int client1_sockfd; 
char buffer[MAXLINE]; 
char *hello1 = "Hello from client 1"; 
struct sockaddr_in   client2_addr; 
struct hostent *client_2;
clis_portno = atoi(argv->port);
clis_portno = clis_portno + 1;

// Creating socket file descriptor 
if ( (client1_sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 
    perror("socket creation failed"); 
    exit(EXIT_FAILURE); 
} 

memset(&client2_addr, 0, sizeof(client2_addr)); 

if((client_2 = gethostbyname(argv->name)) == NULL)
{
    perror("cli gethostbyname");
    exit(EXIT_FAILURE);
}

bzero((char *) &client2_addr, sizeof(client2_addr));

// Filling Client 2 information 
client2_addr.sin_family = AF_INET; 
bcopy((char *)client_2->h_addr, (char *)&client2_addr.sin_addr.s_addr, client_2->h_length);
client2_addr.sin_port = htons(clis_portno); 

while(1)
{
    int n1;
    socklen_t len1;

    if( sendto(client1_sockfd, (const char *)hello1, strlen(hello1), 
        0, (const struct sockaddr *) &client2_addr, 
            sizeof(client2_addr)) < 0)
    {
        perror("cli sendto");
        exit(EXIT_FAILURE);
    }

    printf("IN THREAD: Hello1 = %s\n", hello1); 

    if((n1 = recvfrom(client1_sockfd, (char *)buffer, MAXLINE, 
                MSG_WAITALL, (struct sockaddr *) &client2_addr, 
                &len1)) < 0)
    {
        perror("cli recvfrom");
        exit(EXIT_FAILURE);
    }

    buffer[n1] = '\0'; 
    printf("IN THREAD: Client 2 : %s\n", buffer); 
}
2
What is argv->name when you invoke gethostbyname? It should be localhost if you want to reliably send to your local socket. Trying to send to your public hostname's IP might incur a hairpin turn - which not all NAT's support. - selbie
@selbie argv->name is the name of the server to access. Let me try localhost and see what happens. - Nicholas N

2 Answers

0
votes

You are forgetting to initialize len2:

socklen_t len2;
if((n2 = recvfrom(cli2_sockfd, (char *)buffer_cli2, MAXLINE, 
            0, ( struct sockaddr *) &client1_addr, 
            &len2)) < 0)

Better:

socklen_t len2 = sizeof(client1_addr);
n2 = recvfrom(cli2_sockfd, (char *)buffer_cli2, MAXLINE, 
        0, ( struct sockaddr *) &client1_addr, 
        &len2));
if (n2 < 0)
{
   ….

Not sure if that's your only issue that's preventing packets from being received.

0
votes

I cleaned up your code a little and got it to work using port 9999 for server. And having the client connect to localhost. I cleaned up some of those memcpy statements around gethostbyname, some of your struct initialization calls, removed some of the exit calls that could occur for benign errors (including recvfrom errors when the server is offline). That MSG_WAITALL flag looked weird, so I removed that as well.

I got it working Cygwin without any special hacks. I have no doubt it would work in Linux (or any Unix).

You can see it working here in server mode:

enter image description here

And the corresponding window in client mode:

enter image description here

Code here:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <stdlib.h>
#include <memory.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <stdarg.h>

#define MAXLINE 260
#define MSG_CONFIRM "Confirm"

void server(unsigned short port)
{
    int cli2_sockfd = -1;
    char buffer_cli2[MAXLINE] = { 0 };
    char *hello2 = "Hello from client 2";
    struct sockaddr_in cli2_addr = { 0 }, client1_addr = { 0 };
    unsigned short clis_portno = port;

    // Creating socket file descriptor 
    if ((cli2_sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // Filling server information 
    cli2_addr.sin_family = AF_INET; // IPv4 
    cli2_addr.sin_addr.s_addr = INADDR_ANY;
    cli2_addr.sin_port = htons(clis_portno);

    // Bind the socket with the server address 
    if (bind(cli2_sockfd, (const struct sockaddr *)&cli2_addr,
        sizeof(cli2_addr)) < 0)
    {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        int n2;
        socklen_t len2 = sizeof(client1_addr);
        if ((n2 = recvfrom(cli2_sockfd, (char *)buffer_cli2, MAXLINE,
            0, (struct sockaddr *) &client1_addr,
            &len2)) < 0)
        {
            perror("svr recvfrom");
            exit(EXIT_FAILURE);
        }

        buffer_cli2[n2] = '\0';
        printf("Client 1: %s\n", buffer_cli2);

        if (sendto(cli2_sockfd, (const char *)hello2, strlen(hello2),
            0, (const struct sockaddr *) &client1_addr,
            len2) < 0)
        {
            perror("svr sendto");
        }
        printf("Hello message sent.\n");

    }
}

void client(const char* hostname, unsigned short port)
{
    int client1_sockfd;
    char buffer[MAXLINE];
    char *hello1 = "Hello from client 1";
    struct sockaddr_in   client2_addr = { 0 };
    struct hostent *client_2 = NULL;
    unsigned short clis_portno = port;

    // Creating socket file descriptor 
    if ((client1_sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    if ((client_2 = gethostbyname(hostname)) == NULL)
    {
        perror("cli gethostbyname");
        exit(EXIT_FAILURE);
    }

    // Filling Client 2 information 
    client2_addr.sin_family = AF_INET;
    client2_addr.sin_port = htons(clis_portno);
    memcpy(&client2_addr.sin_addr, client_2->h_addr, 4);

    while (1)
    {
        int n1;


        if (sendto(client1_sockfd, (const char *)hello1, strlen(hello1),
            0, (const struct sockaddr *) &client2_addr,
            sizeof(client2_addr)) < 0)
        {
            perror("cli sendto");
            exit(EXIT_FAILURE);
        }

        printf("IN THREAD: Hello1 = %s\n", hello1);

        socklen_t len1 = sizeof(client2_addr);
        if ((n1 = recvfrom(client1_sockfd, (char *)buffer, MAXLINE,
            0, (struct sockaddr *) &client2_addr,
            &len1)) < 0)
        {
            perror("cli recvfrom");
        }
        else
        {
            buffer[n1] = '\0';
            printf("IN THREAD: Client 2 : %s\n", buffer);
        }
        sleep(1);
    }
}




int main(int argc, char** argv)
{

    if ((argc > 1) && (strcmp(argv[1], "s") == 0))
    {
        printf("Running in server mode\n");
        server(9999);
    }
    else
    {
            printf("Running in client mode\n");
            client("localhost", 9999);
    }
    return 0;
}