Articles tagués “socket

Gestion des erreurs avec les sockets en C

Les sockets sont la base de la programmation d’applications réseau et il n’est pas rare de les manipuler en C. Il est assez facile de faire du code portable entre Windows et les autres systèmes d’exploitation courant (Linux, Mac OS X et autres Unix), en incluant les en-têtes qui vont bien à coup de directives pré-processeur et en pensant à démarrer la bibliothèque Winsock2 sous Windows. Vous pouvez lire ce tutoriel sur les sockets C, c’est avec lui que j’ai appris lors d’un projet en C à l’INSA.

Dans mon présent article, je souhaite mettre en avant un point qui n’est sûrement pas écrit assez gros dans le tutoriel : si les fonctions de manipulation des sockets sont les mêmes, la gestion des erreurs est différente ! En effet, les Unixoïdes positionnent correctement la variable magique errno et on peut donc utiliser perror() et strerror() pour avoir un message lisible décrivant l’erreur. Mais Windows ne positionne pas errno ! Le vilain ! Il faut alors utiliser la fonction WSAGetLastError() pour obtenir un code équivalent à errno et utiliser la fonction FormatMessage() pour obtenir un message lisible.

Voici un code simple (et imparfait, le retour de socket() n’est pas vérifié par exemple). Si l’adresse IP que vous renseignée n’est pas correcte pour votre ordinateur, alors la fonction bind() échouera. On pourra alors constater la différence entre Windows et les autres.

#include <errno.h></pre>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined (_WIN32)
#include <winsock2.h>
typedef int socklen_t;

#elif defined (__linux__)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;

#endif

#define PORT 23242

int main(void)
{
#if defined (_WIN32)
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2,2), &WSAData);
#endif

    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);

    SOCKADDR_IN sin;
    sin.sin_addr.s_addr = inet_addr("137.0.0.1");
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);

    errno = 0;

    int sock_err = bind(sock, (SOCKADDR*)&sin, sizeof(sin));

    if(sock_err == -1)
    {
        printf("Errno bind(): %d : %s\n", errno, strerror((errno)));
        perror("Perror bind()");

#if defined (_WIN32)
        printf("WSAGetLastError bind() : %d\n", WSAGetLastError());
        char *wsa_message = NULL;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                NULL,
                WSAGetLastError(),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPSTR)&wsa_message,
                0,
                NULL);
        printf("FormatMessage : %s\n", wsa_message);
        LocalFree(wsa_message);
#endif
    }

#if defined (_WIN32)
    WSACleanup();
#endif

    return EXIT_SUCCESS;
}

Résultat sous Windows Seven :

Errno bind(): 0 : No error
Perror bind(): No error
WSAGetLastError bind() : 10049
FormatMessage : L'adresse demandée n'est pas valide dans son contexte.

Résultat sous Mac OS X :

Errno bind(): 49 : Can't assign requested address
Perror bind(): Can't assign requested address

Gardez ce détail en tête pour éviter de vous retrouvez avec un code qui compile et s’exécute de la même manière mais avec perror("Bind()"); qui affiche des messages d’erreur qui n’ont aucun rien à avoir la choucroute comme Bind(): Result too Large ou encore Bind(): No error.


Concevoir un site comme celui-ci avec WordPress.com
Commencer