1
votes

I have a really simple exercise to do: make a socket where when a client is connected, the server display its information (address and port number).

I wrote this:

client.c

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

int main(int argc, char *argv[])
{
    // check arguments
    if (argc != 3)
    {
        fprintf(stderr, "Erreur argument\n");
        exit(EXIT_FAILURE);
    }
    char *nom_machine = argv[1];
    int port = atoi(argv[2]);

    int sock;
    struct sockaddr_in addr;

    // création socket
    sock = socket(AF_INET, SOCK_STREAM, 0);

    // remplissage struct de l'adresse
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port); // code les octets dans l'ordre réseau
    inet_aton(nom_machine, &addr.sin_addr);

    // connnexion à 1.2.3.4:4242
    connect(sock, (struct sockaddr *)&addr, sizeof addr);

    // display informations
    fprintf (stderr,
            "---------\nAdresse: %s\nPort: %hd\n---------\n",
            inet_ntoa(addr.sin_addr),
            ntohs(addr.sin_port));


    // envoie message
    int n = 7;
    int r = write(sock, &n, sizeof(int));
    if (r != sizeof(int)) {
        fprintf(stderr, "Echec de l'envoi de n\n");
        exit(EXIT_FAILURE);
    }

    // arrêt
    close(sock);

    exit(EXIT_SUCCESS);
}

Server.c

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

int main(int argc, char *argv[])
{   
    if (argc != 2)
    {
        fprintf(stderr, "Erreur argument\n");
        exit(EXIT_FAILURE);
    }
    int port = atoi(argv[1]);

    int sock;
    struct sockaddr_in addr;

    // création socket
    sock = socket(AF_INET, SOCK_STREAM, 0);

    // remplissage struct de l'adresse
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port); // code les octets dans l'ordre réseau
    addr.sin_addr.s_addr = INADDR_ANY;

    // création connnexion
    bind(sock, (struct sockaddr *)&addr, sizeof addr);

    // écoute
    listen(sock, 2);

    // attend une connexion entrante
    struct sockaddr_in client_addr;
    socklen_t size;
    int lect = accept(sock, (struct sockaddr *)&client_addr, &size); // (2) structure entring connection

    // display client informations
    fprintf (stderr,
            "---------\nAdresse: %s\nPort: %d\n---------\n",
            inet_ntoa(client_addr.sin_addr),
            ntohs(client_addr.sin_port));

    // lecture message
    int n;
    int r = read(lect, &n, sizeof(int));
    if (r <= 0) {
                fprintf(stderr, "Le serveur n'a pas recu le message\n");
                close(sock);
                exit(EXIT_FAILURE);
    }
    // Affichage
    fprintf(stderr, "%d\n", n);

    // arrêt
    close(sock);

    exit(EXIT_SUCCESS);
}

Unfortunately, when I test my code, the client's address and port number given by the server seems to be totally random (except that sometimes it gives the right address, but I never saw the correct port number).

Here's an example:

client console

thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ gcc -g -Wall -Wextra -Og -o client client.c
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./client 127.0.0.1 3333
---------
Adresse: 127.0.0.1
Port: 3333
---------
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./client 127.0.0.1 3333
---------
Adresse: 127.0.0.1
Port: 3333
---------

server console

thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ gcc -g -Wall -Wextra -Og -o serveur serveur.c
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./serveur 3333
---------
Adresse: 6.127.0.0
Port: 16578
---------
Le serveur n'a pas recu le message
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./serveur 3333
---------
Adresse: 127.0.0.1
Port: 55098
---------
7

If someone can help to figure my errors out. Thanks :)

1
There's no error. Look up the term "ephemeral port"Shawn
Ok but how it can explain that sometimes the port is negative and that the client address changed?Eccsx
It can't go negative. It's an unsigned short. 0-65535. You're not printing it correctly.user207421

1 Answers

1
votes

You are missing this point from the accept(2) manpage:

The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.

This means, that you have to do

socklen_t size = sizeof client_addr;

Otherwise the returned address is truncated if the value in size (which is indeterminate) is too small, which means, that the contents may remain uninitialized (completely or partially). This leads to the strange values you observe.

Another issue is, that the bind() may fail with Address already in use when the port was recently used (TIME_WAIT state). In this case, the port is bound automatically from a certain, implementation defined range. To avoid this, you can set the option SO_REUSEADDR before the bind():

int one = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));

Furthermore, it is very advisable to check all return values for errors; you would have noticed bind() failing then.