Socket Programming: UDP Client-Server in C
Asked Answered
B

1

7

I'm trying to write a client server program using UDP, and wait-and-stop, but I haven't got to that part, I'm still trying to figure it out how the two processes (server and client) communicate, because on my client program, the user needs to enter the server name or IP address, and a port name, and then send an expression that the server should calculate. However, I dug some tutorials in the internet and after coding accordingly (or I thought so) I can't make the client communicate with the server. Below is my code, please enlighten me what I'm doing wrong, if it's the bind(), sendto(), recvfrom() or socket(), or all of them. I can't see what exactly is wrong. I know that the client-side shouldn't run on a infinite loop, but so far I want to make the programs communicate with each other, afterwards I'll polish my code. Thanks!

client-side code:

#include <stdio.h>      // Default System Calls
#include <stdlib.h>     // Needed for OS X
#include <string.h>     // Needed for Strlen
#include <sys/socket.h> // Needed for socket creating and binding
#include <netinet/in.h> // Needed to use struct sockaddr_in
#include <time.h>       // To control the timeout mechanism

#define EXPR_SIZE   1024
#define BUFLEN      512
#define TRUE        1
#define FALSE       0
#define SERVERLEN   1024

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

    long portNum;           // Since it's possible to input a value bigger
                            // than 65535 we'll be using long to
                            // avoid overflows
    char expr[EXPR_SIZE];
    char server[SERVERLEN];
    int fd;                 // file descriptor for the connected socket
    int buf[512];
    struct hostent *h;           // information of the host
    unsigned int addrLen;        // address length after getting the port number
    struct sockaddr_in myaddr;   // address of the client
    struct sockaddr_in servaddr; // server's address
    unsigned int exprLen;
    socklen_t slen = sizeof(servaddr);

    printf("Enter server name or IP address:");
    scanf("%s",server);
    printf("Enter port:");
    scanf("%ld",&portNum);
    if ((portNum < 0) || (portNum > 65535)) {
        printf("Invalid port number. Terminating.");
        return 0;
    }
    printf("Enter expression:");
    scanf("%s",expr);

    if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
        perror("cannot create socket");
        return 0;
    }

    memset((char *)&myaddr, 0, sizeof(myaddr));
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(0);

    if(bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0){
        perror("cannot bind");
        return 0;
    }

    /*
     // Discovering the port number the OS allocated
     addrLen = sizeof(myaddr);
     if(getsockname(fd, (struct sockaddr *)&myaddr, &addrLen) < 0){
     perror("cannot getsockname");
     return 0;
     }
     printf("local port number = %d\n", ntohs(myaddr.sin_port));
     */

    memset((char*)&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htonl(portNum);

    exprLen = sizeof(expr);


    while(TRUE){
        printf("Sending message to %s port %ld\n",server, portNum);
        if (sendto(fd, expr, strlen(expr), 0, (struct sockaddr *)&servaddr, slen) < 0) {
            perror("cannot sendto()");
        }
        printf("Success\n");

    }


    return 0;
}

Server-side code:

#include <stdio.h>      // Default System Calls
#include <stdlib.h>     // Needed for OS X
#include <string.h>     // Needed for Strlen
#include <sys/socket.h> // Needed for socket creating and binding
#include <netinet/in.h> // Needed to use struct sockaddr_in
#include <time.h>       // To control the timeout mechanism

#define EXPR_SIZE   1024
#define BUFLEN      512
#define TRUE        1
#define SERVERLEN   1024

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

    struct sockaddr_in myaddr;  // address of the server
    struct sockaddr_in claddr;  // address of the client
    char buf[BUFLEN];
    int fd;
    long recvlen;
    socklen_t clientlen;



    if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
        perror("cannot create socket");
        return 0;
    }

    memset((char *)&myaddr, 0, sizeof(myaddr));
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(0);

    if(bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0){
        perror("cannot bind");
        return 0;
    }
    clientlen = sizeof(claddr);

    while (TRUE) {
        recvlen = recvfrom(fd, buf, BUFLEN, 0, (struct sockaddr *)&claddr, &clientlen);
        if (recvlen < 0) {
            perror("cannot recvfrom()");
            return 0;
        }
        printf("Received %ld bytes\n",recvlen);
        buf[recvlen] = 0;
        printf("Received message: \"%s\"\n",buf);

    }

    return 0;
}

The server program doesn't output anything, while the client outputs until the process is interrupted:

Enter server name or IP address:127.0.0.1
Enter port:30
Enter expression:2+2
Sending message to 127.0.0.1 port 30
cannot sendto(): Can't assign requested address

I tried changing the server name to localhost, and other ports, but to no avail.

Brecciate answered 23/2, 2016 at 4:43 Comment(9)
"I can't make the client communicate with the server.". Could you be a bit specific than that? Does the program crash, does it produce no output, does it produce wrong output? etc. Also include any debugging findings that you have already gathered.Delgadillo
Going to edit the output in the questionBrecciate
Your client needs to send to the same port that the server has bound to.Delgadillo
for this line in client: scanf("%s",server); there is not max number of input characters modifier on the '%s' format specifier. Therefore the user can overrun the input buffer, resulting in undefined behaviour and can lead to a seg fault event. Suggest: scanf("%1023s",server);Pavlov
the compile of the client software results in several warning messages about unused parameters, unused variable, variables set but not used, etc. A good start on fixing all those warnings would be replace the current main() function signature with int main( void )Pavlov
when calling most system functions, for instance scanf(), always check the returned value (not the parameter value) to assure the operation was successful.Pavlov
the sendto() function returns the number of bytes sent. That number could be 0. So should compare the returned value with the strlen(expr) to assure the operation was successfulPavlov
@Pavlov Thanks, I ended up using getchar()! Is there a reason to be 1023 or any number is enough?Brecciate
the buffer size is 1024 bytes and with scanf() a '%s' format specifier will always append a NUL byte, so the max allowed input length is 1023 when the actual input buffer is 1023 bytes.Pavlov
G
15

When developing networking software (especially when using the BSD socket interface), it's important to keep things as simple as possible until you've established basic communication. Then you can incrementally add functionality, while making sure that you don't break anything along the way.

On the client side, keeping things simple means

  • Don't call bind in the client. The OS will choose an appropriate interface and assign a random port number, so there's no need to bind the socket.

  • Use a hard-coded server address (e.g. 127.0.0.1). Address 127.0.0.1 (0x7f000001) is the local host address, suitable for sending packets to a server on the same machine.

  • Use a hard-coded port number (e.g. 50037). Ephemeral port numbers should be greater than 0xC000 hex (49152 decimal).

  • Use a hard-coded message, e.g. "hello".

With that in mind, here's what the client software looks like

int main( void )
{
    int fd;
    if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        perror("socket failed");
        return 1;
    }

    struct sockaddr_in serveraddr;
    memset( &serveraddr, 0, sizeof(serveraddr) );
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons( 50037 );              
    serveraddr.sin_addr.s_addr = htonl( 0x7f000001 );  

    for ( int i = 0; i < 4; i++ ) {
        if (sendto( fd, "hello", 5, 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 ) {
            perror( "sendto failed" );
            break;
        }
        printf( "message sent\n" );
    }

    close( fd );
}

On the server side, keeping things simple means

  • Bind to INADDR_ANY, i.e. let the OS pick an appropriate interface.
  • Bind to a hard-coded port, e.g. 50037 (must be the same port the client uses).
  • Don't request the address information from recvfrom, i.e. pass NULL, 0 as the last two parameters.

With that in mind, here's what the server software looks like

int main( void )
{
    int fd;
    if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        perror( "socket failed" );
        return 1;
    }

    struct sockaddr_in serveraddr;
    memset( &serveraddr, 0, sizeof(serveraddr) );
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons( 50037 );
    serveraddr.sin_addr.s_addr = htonl( INADDR_ANY );

    if ( bind(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 ) {
        perror( "bind failed" );
        return 1;
    }

    char buffer[200];
    for ( int i = 0; i < 4; i++ ) {
        int length = recvfrom( fd, buffer, sizeof(buffer) - 1, 0, NULL, 0 );
        if ( length < 0 ) {
            perror( "recvfrom failed" );
            break;
        }
        buffer[length] = '\0';
        printf( "%d bytes: '%s'\n", length, buffer );
    }

    close( fd );
}
Gelderland answered 23/2, 2016 at 6:31 Comment(1)
In "toy" applications I doubt anyone would bat an eyelash, but in general: don't use ephemeral ports. If you google the topic you'll find cases where people have done so and had problems, or you can just take their advice: books.google.si/…Spang

© 2022 - 2024 — McMap. All rights reserved.