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.
26 juillet 2013 | Catégories: C, Linux / Unix | Tags: bind, errno, formatmessage, perror, socket, unix, Windows, wsagetlasterror | 1 commentaire


