user2104647
user2104647

Reputation: 31

UDP Socket Programming in C: 2 Clients and 1 Server

Im very new to network programming. I have a UDP client/server that sends a message to a server in lower or uppercase. The server receives the message and relays it back with switches cases. I cant figure out how I would instead of relaying it back to the first client, sending it to client2. heres my code.

Server:

/*
Simple udp server

*/
#include<stdio.h>   //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>
#include<ctype.h>

#define BUFLEN 512  //Max length of buffer
#define PORT 8888   //The port on which to listen for incoming data

void die(char *s)
{
    perror(s);
exit(1);
}

int main(void)
{
struct sockaddr_in si_me, si_other, si_other2;

int s, i, slen = sizeof(si_other) , recv_len;
char buf[BUFLEN];

//create a UDP socket
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
    die("socket");
}

// zero out the structure
memset((char *) &si_me, 0, sizeof(si_me));

si_me.sin_family = AF_INET;
si_me.sin_port = htons(PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);

//bind socket to port
if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
{
    die("bind");
}

//keep listening for data
while(1)
{
    printf("Waiting for data...");
    fflush(stdout);

    //try to receive some data, this is a blocking call
    if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1)   // read datagram from server socket
    {
        die("recvfrom()");
    }

    //print details of the client/peer and the data received
    printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port));         printf("Data: %s\n" , buf);

    //now reply to server socket/the client with the same data
    if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1)
    {
        die("sendto()");
    }



}

close(s);
return 0;
}

The Client:

/*
Simple udp client

*/
#include<stdio.h>   //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>
#include<ctype.h>

#define SERVER "192.x.x.x"
#define BUFLEN 512  //Max length of buffer
#define PORT 8888   //The port on which to send data

void die(char *s)
{
perror(s);
exit(1);
}

int main(void)
{
struct sockaddr_in si_other;
int s, s2, i, slen=sizeof(si_other);
char buf[BUFLEN];
char message[BUFLEN];

if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)        // create a client socket
{
    die("socket");
}

memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);

if (inet_aton(SERVER , &si_other.sin_addr) == 0)            // Create datagram with server IP and port.
{
    fprintf(stderr, "inet_aton() failed\n");
    exit(1);
}

while(1)
{
    printf("Enter message : ");
    gets(message);


    int a;
    char message2[BUFLEN];
    for(a=0;a<=BUFLEN-1;a++)
      {
        if(message[a] >= 97 && message[a] <= 122)
           message2[a] = toupper(message[a]);
        else
           message2[a] = tolower(message[a]);

      }


    if (sendto(s, message2, strlen(message2) , 0 , (struct sockaddr *) &si_other, slen)==-1)
    {
        die("sendto()");
    }


    //receive a reply and print it
    //clear the buffer by filling null, it might have previously received data
    memset(buf,'\0', BUFLEN);
    //try to receive some data, this is a blocking call
    if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1)        // read datagram from client socket
    {
        die("recvfrom()");
    }

    puts(buf);
}

close(s);
return 0;
}

Upvotes: 3

Views: 33647

Answers (1)

little_birdie
little_birdie

Reputation: 5857

Since this has 21K views with no explicit answer, and it is basic understanding of coding for UDP.. I will give it some love.

As mentioned in the comments already: In your server code, you receive a message from the client using:

recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen))

The result of this function is that the message data will be written into buf and the ip address and port number of the socket that sent the message will be filled into si_other (which must be of type struct sockaddr_in).

So then, when you send a response using:

sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen)

Since si_other, which you are passing as the destination address for sendto, contains the ip/port of the last message you got, the response will always go back to the sender of the last message you got. In many server applications this is a pretty common scenario: you get a request from somewhere, you send a response back to the same place.

If you want the message to go someplace else, other than the process that sent you the request, then you need to create a different struct sockaddr_in variable which contains the ip address and port of where you would like the message to go.

And in your client code, you already have the code that does that, eg (cleaned up a bit):

struct sockaddr_in si_client2;

memset((char *) &si_client2, 0, sizeof(si_client2));
si_client2.sin_family = AF_INET;
si_client2.sin_port = htons(CLIENT2_PORT);
if(inet_aton(CLIENT2_HOST, &si_client2.sin_addr) == 0)
    perror("inet_aton");

So now, if you use si_client2 in your sendto(), the packet will go to that client.

Because it's UDP, delivery is not guaranteed. If there is a process listening for UDP at that ip address, on that port number, then (if no network error occurs) it will get the message. If not, nothing will happen.. your message vanishes into the void.

Keep in mind that if client 1 and client 2 are both running on the same machine, they will need to use different port numbers, because every destination (whether in a client or server role) must have a unique combination of IP and PORT.

Now, in real life applications, it is rare that a server will ever know the IP and PORT of its clients ahead of time.. usually clients will not use fixed port numbers but instead use "ephemeral ports".. port numbers which the operating system assigns at run-time. Whereas, the client will often be configured with the IP and port of the server.

So, in most cases, you would have some code in the server that keeps a list of client addresses.. perhaps a simple messaging service would keep a list of the last 100 clients it got messages from... But how this is actually done would be dictated by the needs of the application. For a simple exercise like this one, you can just hard code the addresses as I said...

The bottom line is that, to send a UDP packet to a specific destination, the sender must know the IP and PORT of that specific destination. And the only way to know that is either to have some configuration data, or for someone (such as the destination) to send a packet ahead of time letting you know of its existence. Just keep in mind that with a UDP socket you can get a message from anywhere, and you'll be given the ip/port along with the message. If you need to send a message, you need to know the ip/port of where you want to send it. It's your applications problem to figure out how it's going to get that information and where to store that information for later use.

Upvotes: 9

Related Questions