..                     ,.-..                     ,.., 
      |  |                  .`     `.                  |    |                                  
      |''|                  |`'---'`|                  |`'-`|                                  
      |<<|                  | _____ |             _,,..|[}{]|.,,,                              
    _,|<<|,,                | \ D / |         ,-``     |[}{]|    ``',                          
  .`  `..'  `.              |  |A|  |         |-,,     ``--''    _,-'|                         
  | `''--''` |              |  |T|  |         |   ``'''------''``    |                         
  |  [ O3 ]  |              |  |A|  |         |     [ Mindkind ]     |                         
  `.   ~~   .'              | /   \ |         `-,,    ] 1010 [   _,-'                          
    `''--''`      _,,,,,,...||  C  ||.....,,,,,,_ ``'''------''```                             
      |<<|.-''```` _,,,,,...|[  O  ]|...,,,,,,_  ```''-|[}{]|                                  
    ,```   .,-''```         |[  R  ]|          ``''-., |[}{]|,                                 
   |      `.        _,,,,...||  E  ||...,,,,,         '|[}{]| |                                
   |`-,_   `'-.,,_,`********| \   / |********`._,,.-'` |[}{]|'|                                
   |    `''-.,,,_ `````'''''---------'''''`````  _,,,.-|[}{]| |                                
   |`-,_         `````''''''---------'''''''`````      |[}{]|'|                                
   |    `''[                                          ]|[}{]| |                                
   |`-,_   [          NSocket - Socket Layer          ]|[}{]|'|                                
   |    `''[                                         ] |[}{]| |                                
    `-,_        [                              ]  _,,..|[}{]|.,,,                              
      |<;`'-.,,,_                             ,-``     |[}{]|    ``',                          
    ,`     ;     ```;;;;;;;;---------;;;;;;;;;|-,,     ``--''    _,-'|                         
   |`-,_   `'-.,,_,`********|[ !?! ]|********`|   ``'''------''``    |                         
   |`-,_`''-.,,,_ `````'''''---------'''''````|     ]   Ninja  [     |                         
   |`-,_`''-.,,, `````''''''---------'''''''```-,,               _,-'                          
    `-,_`''-.,,,[                                ]``'''------''``                              
      |<`|'-.,,,_[                              ]_,,,.-|[}{]|                                  
      |<<|       `````''''''---------'''''''`````      |[}{]|                                  
      |<<|                  |]Aka: [|                  |[}{]|                                  
      |<<|             ___,,| _____ |....--------------|[}{]|,,,,,,,,,,__                      
      |<<|,,.--'''`````   __| \ D / |....--------------|[}{]|,,,,,,,,_   `````'''--..,,_       
  _.-'``       ,,.--'`````  |  |A|  |                  |[}{]|         `````''-.,,       ``'-., 
 ;           -`             |  |T|  |,.....----------..|[}{]|,,,_                `'           ;
 |`'-.,,       `''-..,,,_.-`|  |A|  |******************|[}{]|****````-_,,,,,.--'`       _,,-'`|
 |      ```''--..,,,,,_   ```````''''''''--------------''''''''```````   __,,,,..--''```      |
 |                     ````````''''''''''--------------''''''''''````````                     |
 |                                                                                            |


                                                                          Ninja
                                                                     Avril 2006


Résumé
    "Ninja Production Present" un autre texte sympa ou batard, selon le
    lecteur, pour votre bénéfice personnel ou qui sera le "petit quelque chose"
    qui vous manquait pour vous tirez devant un train. Sans plus tarder, comme
    vous l'aurez surement remarqué, je vous ai préparé une implémentation,
    TRÈS non complète, d'une couche d'abstraction pour les sockets sous windows
    ET linux, w00t.

Tables des matières
    1. Introduction
    2. Pourquoi
    3. Portabilité
       3.1 Windows vs Linux
       3.2 IPv4 vs IPv6
    4. Comment
       4.1 Fonctions de base
       4.2 Autres fonctions
    5. Code
    6. Conclusion

1. Introduction

    La définition simple et rapide d'un socket, selon moi no flame s.v.p., est
    la suivante: une extrémité d'une liaison bidirectionnelle entre deux
    programmes fonctionnant sur le réseau. Un socket est lié à un port de sorte
    que la couche TCP, ou n'importe quelle couche "Transport" du modèle OSI,
    puisse identifier à quelle application seront destinées les données reçues.

2. Pourquoi

    Par lâcheté? Pour la réutilisation de code? <Entrez votre raison ici>? Les
    sockets peuvent être un vrai "pain in the ass": Vérifier si c'est bien un
    IP, version doted ou long, sinon faire la requête DNS pour obtenir le IP
    associé au nom de l'hôte; vérifier si le port est valide; initialisation
    du winsock (sous windows duh!); envoyer toutes les données d'un buffeur
    quelconque; manipuler les sockets non bloquant avec un système de polling
    [select()]; etc. Alors, pourquoi ne pas faire une fois pour toutes un
    ensemble de fonctions qui vont faciliter ces tâches en réduisant le code à
    produire lors de futures projets.

3. Portabilité

    3.1 Windows vs Linux

        Pour ceux qui ont eu l'occasion de jouer avec les sockets sur Linux ET
        sur Windows, la manipulation des sockets est très similaire à quelques
        détails près. L'ensemble des fonctions de base comme socket(), listen(),
        recv(), send(), accept(), etc. sont présente sur les deux plateformes et
        utilise les mêmes paramètres. Mais il y à quelques petites différences du
        genre un socket sous linux est de type INT mais sous windows c'est un
        UNSIGNED INT. Sous windows on doit absolument initialisé winsock avant
        pouvoir manipuler un socket. De plus, certaines fonctions sous linux ne
        se retrouvent pas sous windows ou ne prennent simplement pas les mêmes
        arguments.

        Il est donc désagréable, pour quelqu'un qui tente de créer une
        application portable sur ces deux plateformes, d'écrire une application
        qui utilise les sockets. Il serait donc plus pratique de se créer une
        librairie de fonctions manipulant les sockets qui serait indépendante de
        la plateforme sous laquelle elle est compilée. Dans ce cours texte, nous
        allons nous concentrer uniquement sur linux et windows.

    3.2 IPv4 vs IPv6

        Malgré le fait que IPv6 ne soit pas grandement répandu, il se peut
        qu'un jour nous aurons besoin de coder une application qui utilisera
        IPv6. Comme IPv6 passe certains paramètres qui diffèrent de ce que IPv4
        utilise généralement et certaines fonctions ne sont compatibles qu'avec
        IPv4. Ces différences augmentent l'effort et le temps qu'un progammeur
        doit investir lors du développement d'une application utilisant les
        sockets. Nous allons nous concentrer seulement sur le proticol IPv4
        puisqu'il s'agit simplement d'un texte explicatif/éducatif et il sera
        plus facile de lire et comprendre le code en réduisant le nombre de
        #ifdef, #ifndef, etc.

4. Comment

    4.1 Fonctions de base

        Il existe plusieurs fonctions pour la manipulation des sockets,
        certaines sont essentielles, d'autre moins. Certaines sont
        majoritairement utilisées comme support pour d'autres fonctions plus
        primordiales. Voici donc la liste des fonctions de base que nous allons
        tenter de porter:

        socket()  - crée un socket
        bind()    - attache un socket a une adresse et/ou un port
        connect() - ouvre une connexion sur un hôte distant
        listen()  - écoute pour des connexions entrantes
        accept()  - autorise une connexion entrante
        read()    - lire les données reçues de l'hôte
        write()   - envoie des données à l'hôte
        close()   - ferme un socket

    4.2 Autres fonctions

        Toutes autres fonctions ne seront pas portées même si certaines seront
        utilisées dans les wrapes que nous allons créer. Le but de ce texte
        n'étant pas de fournir une librairie complète, mais bien de produire
        un template sur lequel nous pourrons travailler et, idéalement,
        améliorer.

        Nous allons néanmoins créer quelques fonctions qui nous seront utiles
        pour la manipulation des sockets. Elles serviront, entre autres, à
        valider une adresse ip en format dotted (192.158.0.1), d'initialiser
        l'environnement (majoritairement sous windows), de récupérer les erreurs
        socket, etc.

5. Code

    Nous voici donc au moment attendu, l'implémentation de notre librairie. Le
    code sera divisé par fichier. Les délimiteurs suivants marque le début et la
    fin du code d'un fichier:

    =---------------------------BEGIN file.x --------------------------------=
    =---------------------------END file.x ----------------------------------=



    C'est un départ!

    =---------------------------BEGIN nsocket.h -----------------------------=

/* * fichier: nsocket.h * auteur : ninja * date : 13 May 2006 * desc : * Déclaration des structutures et fonctions permettant une * manipulation plus facile des sockets. * */ #ifndef NSOCKET_H_INCLUDE #define NSOCKET_H_INCLUDE #if defined (_WIN32) || defined (WIN32) #include <winsock2.h> #else #include <sys/types.h> #include <sys/socket.h> #define SOCKET int /* va nous permettre d'utiliser SOCKET comme type sous linux. Sous win, SOCKET == unsigned int */ #define INVALID_SOCKET -1 #endif #define NSOCKLISTENMAX 50 /* longueur max pour la file des connexionsen attente*/ #define NSOCK_TCP 0x001 /* socket de type TCP. Defaut */ #define NSOCK_UDP 0x002 /* socket de type UDP. Defaut à TCP */ #define NSOCK_NONBLOCK 0x004 /* socket non-bloquant. Defaut à bloquant */ #define NSOCK_BIND 0x008 /* binder le socket. Defaut pas de bind() */ #define NSOCK_CLIENT 0x010 /* Defaut */ #define NSOCK_SERVER 0x020 /* socket en mode écoute */ #define NSOCK_CONNECTING 0x040 /* socket essaie de se connecter à un hôte */ #define NSOCK_CLOSED 0x100 /* socket fermé */ #define NSOCK_DEFAULT 0x011 /* NSOCK_TCP & NSOCK_CLIENT */ /* Permet de contrôler si nsocket doit afficher les erreur lui-même ou non */ extern int NSOCKET_DO_VERBOSE; typedef struct nsocket_options_s nsocket_options_t, *pnsocket_options_t; typedef struct nsocket_s nsocket_t, *pnsocket_t; /* cette structure contiens toutes les infos de base niveau pour la manipulation des socekts */ struct nsocket_options_s { SOCKET sockfd; /* socket file descriptor */ int pf; /* protocol family - defaut: PF_INET */ int type; /* socket type - defaut: SOCK_STREAM */ int protocol; /* defaut: (IPPROTO_TCP) */ int af; /* address family - defaut: AF_INET */ struct sockaddr my_addr; struct sockaddr peer_addr; int flags; /* holds NSOCK_ flags */ }; /* contien les infos de haut niveau comme les ips en version dotted et les ports en Host Bits Order */ struct nsocket_s { char* peer_ip; /* remote ip */ unsigned int peer_port; /* remote port */ char* my_ip; /* my ip */ unsigned int my_port; /* my port */ struct nsocket_options_s opts; }; /* fonctions pour la manipulation des sockets. voir nsocket.c pour plus de détails sur leurs fonctionnalités */ int nsocket_init(); int nsocket_shutdown(); int nsocket_isvalideip(const char *ip); int nsocket_get_error(pnsocket_t s); int nsocket_set_nonblock(pnsocket_t s, unsigned long value); pnsocket_t nsocket_create(int flags); int nsocket_bind(pnsocket_t s, const char *my_ip, int my_port); int nsocket_connect(pnsocket_t s, const char *peer_ip, int peer_port); int nsocket_listen(pnsocket_t s); pnsocket_t nsocket_accept(pnsocket_t s); int nsocket_read(pnsocket_t s, char* data, int size); int nsocket_write(pnsocket_t s, const char* data, int size); int nsocket_close(pnsocket_t s); int nsocket_free(pnsocket_t s); int nsocket_opts_init(pnsocket_options_t opts); int nsocket_opts_free(pnsocket_options_t opts); void nsocket_perror(pnsocket_t s, const char *fname, const char *cname); #endif /* not define NSOCKET_H_INCLUDE */
=---------------------------END nsocket.h -------------------------------= =---------------------------BEGIN nsocket.c -----------------------------=
/* * fichier: nsocket.c * auteur : ninja * date : 13 May 2006 * desc : * Implémentation des fonctions permettant une manipulation plus facile * des sockets. * */ #if defined (_WIN32) || defined (WIN32) #include <winsock2.h> #else #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define _GNU_SOURCE /* needed if we want to use strdup */ #endif #include <string.h> /* malloc(), free()[WIN32], _strdup()[WIN32] */ #include <stdlib.h> /* free() */ #include <stdio.h> /* printf(), stdout */ #include "nsocket.h" #if defined (_WIN32) || defined (WIN32) #define strdup _strdup static WSADATA wsaData; /* si ça ne fonctionne pas */ /*static WSAData wsaData; /* alors essayez ceci */ #endif /* Permet de contrôler si nsocket doit afficher les erreurs lui-même ou non. Par défaut, nsocket affiche un message d'erreur */ int NSOCKET_DO_VERBOSE = 1; /* nsocket_init() * 0: w00t * -1: operation fail * * Sous windows nous devons absolument initialiser l'environnement Winsock * sinon tout appel aux fonctions de manipulation de socket va échouer. */ int nsocket_init() { #if defined (_WIN32) || defined (WIN32) WORD wVersionRequested; // version winsock demandé wVersionRequested = MAKEWORD(2, 2); if(WSAStartup(wVersionRequested, &wsaData) != 0) return -1; /* aucune DLL winsock valide n'a été trouvé */ /* Vérifier si la DLL winsock support bien la version 2.2. */ /* Notez que même si la DLL support une version suppérieur à 2.2, tout en */ /* supportant la 2.2, la valeur retourner dans wVersion sera 2.2 puisque */ /* c'est ce que nous avons demandé. */ if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { WSACleanup(); return -1; /* aucune DLL winsock valide n'a été trouvé */ } #endif return 0; } /* nsocket_shutdown() * 0 : w00t * non zero: operation fail * * Comme nous devons initialiser l'environnement Winsock avant l'utilisation * de socket, nous devons aussi libérer/nettoyer l'environnement après * utilisation. */ int nsocket_shutdown() { #if defined (_WIN32) || defined (WIN32) // on nettoie l'environnement winsock return WSACleanup(); #else return 0; #endif } /* nsocket_isvalideip() * -1: invalide dotted address * 0: valide dotted address * * Valide si l'ip passé en paramètre est bien un ip en format dotted * ex.: 127.0.0.1 */ int nsocket_isvalideip(const char* ip) { #if defined (_WIN32) || defined (WIN32) struct sockaddr addr; int size; /* initialise la mémoire a zéro */ memset(&addr,0,sizeof(addr)); addr.sa_family = AF_INET; /* si on ne spécifie pas, WSAStringToAddress échoue avec WSAEINVAL (10022) */ size = sizeof(addr); if (WSAStringToAddress((LPTSTR)ip,AF_INET,NULL,&addr,&size) == 0) return 0; #else struct in_addr iaddr4; /* nous utilisons inet_pton même si il ne s'agit pas d'un IPv4 puisque cette fonction supporte IPv6 et IPv4. De plus elle est fortement recommandée au lieu de inet_aton */ if (inet_pton(AF_INET,ip,&iaddr4) > 0) return 0; #endif /* ip invalide */ return -1; } /* nsocket_get_error() * retourne le code d'erreur du socket */ int nsocket_get_error(pnsocket_t s) { int size, err; if(s == NULL) return 0; size = sizeof(int); err = 0; getsockopt(s->opts.sockfd, SOL_SOCKET, SO_ERROR, (char*)&err, &size); return(err); } /* nsocket_set_nonblock() * 0 : w00t * non zero: operation fail * * Nous permet de mettre, ou non, le socket en mode non bloquant */ int nsocket_set_nonblock(pnsocket_t s, unsigned long value) { if(s == NULL) return 0; #if defined (_WIN32) || defined (WIN32) return ioctlsocket(s->opts.sockfd, FIONBIO, &value); #else int oldflags = fcntl(s->opts.sockfd, F_GETFL, 0); if (oldflags == -1) return -1; if (value != 0) oldflags |= O_NONBLOCK; else oldflags &= ~O_NONBLOCK; return fcntl(s->opts.sockfd, F_SETFL, oldflags); #endif } /* nsocket_create * pnsocket: pointer sur le nouveau socket * NULL: Erreur */ pnsocket_t nsocket_create(int flags) { pnsocket_t s; int i; /* Sanitarisation (w00t nouveau terme) des flags ...*/ if(flags & NSOCK_UDP && flags & NSOCK_TCP) flags &= ~NSOCK_UDP; if(flags & NSOCK_SERVER && flags & NSOCK_CLIENT) flags &= ~NSOCK_SERVER; flags &= ~NSOCK_CONNECTING; flags |= NSOCK_CLOSED; /* alloue la mémoire pour le nouveau socket */ s = (pnsocket_t)malloc(sizeof(nsocket_t)); /* Initialisation des données membres... */ s->peer_ip = NULL; s->peer_port = 0; s->my_ip = NULL; s->my_port = 0; /* Initialisation des options... */ nsocket_opts_init(&s->opts); /* Copie des flags demandé... */ s->opts.flags = flags; /* Est-ce que le socket doit être UDP ou TCP */ if(flags & NSOCK_UDP) { s->opts.type = SOCK_DGRAM; s->opts.protocol = IPPROTO_UDP; } else { s->opts.type = SOCK_STREAM; s->opts.protocol = IPPROTO_TCP; } /* Création du socket */ if ((s->opts.sockfd = socket(s->opts.af, s->opts.type, s->opts.protocol)) == INVALID_SOCKET) { nsocket_perror(s, "nsocket_create()", "socket"); return NULL; } /* Est-ce que nous voulons un socket non bloquant? */ if (flags & NSOCK_NONBLOCK) i=1; else i=0; if(nsocket_set_nonblock(s, i) !=0) { nsocket_perror(s, "nsocket_create()", "nsocket_set_nonblock"); return NULL; } return(s); } /* nsocket_bind * -1 : une erreur c'est produite * 0 : nsocket invalide ou le bit NSOCK_BIND n'est pas présent * 1 : w00t */ int nsocket_bind(pnsocket_t s, const char *my_ip, int my_port) { #if defined (_WIN32) || defined (WIN32) struct sockaddr addr; int addr_len; #else struct in_addr addr4; #endif int yes; if(s == NULL) return 0; if (!(s->opts.flags & NSOCK_BIND || s->opts.flags & NSOCK_SERVER)) return 0; /* Vérifie si le ip est valide */ if(nsocket_isvalideip(my_ip) != 0) { nsocket_perror(s, "nsocket_bind()", "nsocket_isvalideip"); return -1; } /* Vérifie si le port de l'hôte est valide */ if (my_port < 0 || my_port > 65535) { nsocket_perror(s,"nsocket_bind()", "port invalide"); return -1; } /* Libère l'espace mémoire si déjà alloué... */ if(s->my_ip) free(s->my_ip); /* Archivage du ip et du port... */ s->my_ip = strdup(my_ip); s->my_port = my_port; /* On veut utiliser le port même si il est pris par une autre application. Surtout utile pour un socket en mode listen */ yes=1; setsockopt(s->opts.sockfd,SOL_SOCKET,SO_REUSEADDR, (char*)&yes,sizeof(int)); /* !!! ATTENTION !!! Le code qui suit est EXTRÊMEMENT laid, et est juste un big hack pour contourner certains problèmes d'assignation et de typecasting. Vous avez été prévenu... <hack type="laid" size="gros"> */ ((struct sockaddr_in*)&s->opts.my_addr)->sin_family = AF_INET; ((struct sockaddr_in*)&s->opts.my_addr)->sin_port = htons((u_short)my_port); #if defined (_WIN32) || defined (WIN32) memset(&addr,0,sizeof(addr)); ((struct sockaddr_in*)&addr)->sin_family = AF_INET; addr_len = sizeof(addr); if (WSAStringToAddress(s->my_ip,s->opts.af,NULL,&addr,&addr_len) != 0) return -1; /* ne devrait jamais se produire */ ((struct sockaddr_in*)&s->opts.my_addr)->sin_addr = ((struct sockaddr_in*)&addr)->sin_addr; #else if (inet_pton(AF_INET,s->my_ip,&addr4) <= 0) return -1; /* ne devrait jamais se produire */ ((struct sockaddr_in*)&s->opts.my_addr)->sin_addr = addr4; #endif /* </hack> */ if (bind(s->opts.sockfd, &s->opts.my_addr, sizeof(s->opts.my_addr)) == -1) { nsocket_perror(s, "nsocket_bind()", "bind"); return -1; } return 1; } /* nsocket_connect * -1: une erreur est survenue lors de la tentative/processus de connexion * 0: erreur, nsocket invalide ou nsocket en mode SERVER (listen) * 1: w00t */ int nsocket_connect(pnsocket_t s, const char *peer_ip, int peer_port) { #if defined (_WIN32) || defined (WIN32) struct sockaddr addr; int addr_len; #else struct in_addr addr4; #endif if(s == NULL) return 0; if (!(s->opts.flags & NSOCK_CLIENT)) return 0; /* Vérifie si le ip de l'hôte est valide */ if(nsocket_isvalideip(peer_ip) != 0) { nsocket_perror(s, "nsocket_connect()", "nsocket_isvalideip"); return -1; } /* Vérifie si le port de l'hôte est valide */ if (peer_port < 0 || peer_port > 65535) { nsocket_perror(s,"nsocket_connect()", "port invalide"); return -1; } /* Libère l'espace mémoire si déjà alloué... */ if(s->peer_ip) free(s->peer_ip); /* Archivage du ip et du port... */ s->peer_ip = strdup(peer_ip); s->peer_port = peer_port; /* !!! ATTENTION !!! Le code qui suit est EXTRÊMEMENT laid, et est juste un big hack pour contourner certains problèmes d'assignation et de typecasting. Vous avez été prévenu... <hack type="laid" size="gros"> */ ((struct sockaddr_in*)&s->opts.peer_addr)->sin_family = AF_INET; ((struct sockaddr_in*)&s->opts.peer_addr)->sin_port = htons((u_short)peer_port); #if defined (_WIN32) || defined (WIN32) memset(&addr,0,sizeof(addr)); ((struct sockaddr_in*)&addr)->sin_family = AF_INET; addr_len = sizeof(addr); if (WSAStringToAddress(s->peer_ip,s->opts.af,NULL,&addr,&addr_len) != 0) return -1; /* ne devrait jamais se produire */ ((struct sockaddr_in*)&s->opts.peer_addr)->sin_addr = ((struct sockaddr_in*)&addr)->sin_addr; #else if (inet_pton(AF_INET,s->peer_ip,&addr4) <= 0) return -1; /* ne devrait jamais se produire */ ((struct sockaddr_in*)&s->opts.peer_addr)->sin_addr = addr4; #endif /* </hack> */ s->opts.flags |= NSOCK_CONNECTING; /* connexion en cours */ if (connect(s->opts.sockfd, &s->opts.peer_addr, sizeof(s->opts.peer_addr)) == -1) { /* On ne peut attendre que connect() retourne une error si nous avons configurer le socket en mode non bloquant puisque connect() va toujours retourner une erreur: EINPROGRESS (linux) ou WSAEWOULBLOCK (win32) */ if (s->opts.flags & NSOCK_NONBLOCK && #if defined (_WIN32) || defined (WIN32) WSAGetLastError() == WSAEWOULDBLOCK #else errno == EINPROGRESS #endif ) { s->opts.flags &= ~NSOCK_CLOSED; /* pas vraiment l'endroit idéal pour le faire mais ça doit être fait */ return 1; } nsocket_perror(s, "nsocket_connect()", "connect"); return -1; } /* si nous somme ici, c'est que le socket en en mode bloquant donc connect() à réussie */ s->opts.flags &= ~NSOCK_CONNECTING; s->opts.flags &= ~NSOCK_CLOSED; return 1; } /* nsocket_listen() * -1 : une erreur c'est produite * 0 : nsocket invalide où le bit NSOCK_SERVER n'est pas présent * 1 : w00t */ int nsocket_listen(pnsocket_t s) { if(s == NULL) return 0; if (!(s->opts.flags & NSOCK_SERVER)) return 0; if (listen(s->opts.sockfd, NSOCKLISTENMAX) == -1) { nsocket_perror(s, "nsocket_listen()", "listen"); return -1; } s->opts.flags &= ~NSOCK_CLOSED; /* l'appel à listen() a réussie, donc le socket n'est plus dans l'état 'fermé' */ return 1; } /* nsocket_accept * pnsocket: pointer sur le nouveau socket * NULL: Erreur */ pnsocket_t nsocket_accept(pnsocket_t s) { pnsocket_t new_sock; int sin_size, flags; if(s == NULL) return NULL; flags = NSOCK_DEFAULT; if (s->opts.flags & NSOCK_UDP) return NULL; /* accept ne peut être appelé sur des socket UPD */ if (s->opts.flags & NSOCK_NONBLOCK) flags |= NSOCK_NONBLOCK; if((new_sock = nsocket_create(flags)) == NULL) return NULL; sin_size = sizeof(struct sockaddr); if ((new_sock->opts.sockfd = accept(s->opts.sockfd, &new_sock->opts.peer_addr, &sin_size)) == -1) { nsocket_perror(s,"nsocket_accept()","accept"); return NULL; } new_sock->opts.flags &= ~NSOCK_CLOSED; /* l'appel à accept() a réussie, donc le socket n'est plus dans l'état 'fermé' */ return new_sock; } /* nsocket_read * -1: une erreur c'est produite * 0: connexion fermé par l'hôte * >0: nombre d'octects reçus */ int nsocket_read(pnsocket_t s, char* data, int size) { int nbytes; if(s == NULL) return -1; if (!data || size <= 0) /* si mémoire non allouée ou taille invalide */ return 0; if ((nbytes=recv(s->opts.sockfd, data, size, 0)) == -1) { /* si notre socket est en mode non bloquant qu'il n'y a aucune données dans la file d'attente du socket, recv retourne WSEWOULDBLOCK (win) ou EAGAIN (linux) */ if ((s->opts.flags & NSOCK_NONBLOCK) && #if defined (_WIN32) || defined (WIN32) WSAGetLastError() == WSAEWOULDBLOCK #else errno == EAGAIN #endif ) return -1; /* on retourne -1 mai ce n'est pas une erreur critique il faut donc vérifier WSAGetLastError ou errno pour s'assurer qu'il s'agit bien de WSAEWOULDBLOCK ou EAGAIN Je sais ce n'est pas très pratique, parce que VOUS devez faire la vérification, mais je vais penser à quelque chose pour arranger ça plus tard */ nsocket_perror(s, "nsocket_read()", "recv"); #if defined (_WIN32) || defined (WIN32) if (WSAGetLastError() == WSAECONNRESET) /* connection reset Je n'ai pas chercher pour l'équivalent linux encore */ nsocket_close(s); #endif return(-1); } if (nbytes == 0) { /* connection closed by peer */ nsocket_perror(s, "nsocket_read()", "recv [conn. closed]"); nsocket_close(s); return(0); } return(nbytes); } /* nsocket_write * -1: une erreur c'est produite * n: nombre d'otects envoyés */ int nsocket_write(pnsocket_t s, const char* data, int size) { int n; if(s == NULL) return -1; if (!data || size <= 0) /* si aucune donnée ou taille invalide */ return 0; if((n = send(s->opts.sockfd, data, size, 0)) == -1) { nsocket_perror(s, "sock_got_write()", "send"); return(-1); } return(n); } /* nsocket_close() * -1: une erreur s?est produite * 0: w00t */ int nsocket_close(pnsocket_t s) { if(s == NULL) return 0; if(s->opts.sockfd == INVALID_SOCKET) return 0; if(s->opts.flags & NSOCK_CLOSED) return 0; #if defined (_WIN32) || defined (WIN32) if (shutdown(s->opts.sockfd, SD_BOTH) == -1) { #else if (shutdown(s->opts.sockfd, SHUT_RDWR) == -1) { #endif nsocket_perror(s, "nsocket_close()", "shutdown"); return(-1); } #if defined (_WIN32) || defined (WIN32) if (closesocket(s->opts.sockfd) == -1) { #else if (close(s->opts.sockfd) == -1) { #endif nsocket_perror(s, "nsocket_close()", "close"); return(-1); } nsocket_opts_free(&s->opts); return(0); } /* nsocket_free * -1: pointer invalide * 0: w00t * * Libère un socket créer via nsocket_create ou nsocket_accept */ int nsocket_free(pnsocket_t s) { if(!s) return(-1); if(s->peer_ip) free(s->peer_ip); s->peer_ip = NULL; s->peer_port = 0; if(s->my_ip) free(s->my_ip); s->my_ip = NULL; s->my_port = 0; free(s); s = NULL; return(0); } /* nsocket_opts_init * -1: nsocket_options_t invalide * 0: w00t */ int nsocket_opts_init(pnsocket_options_t opts) { if (!opts) return -1; opts->sockfd = INVALID_SOCKET; opts->pf = PF_INET; opts->type = SOCK_STREAM; opts->protocol = IPPROTO_TCP; opts->af = AF_INET; memset(&(opts->my_addr), '\0', sizeof(opts->my_addr)); memset(&(opts->peer_addr), '\0', sizeof(opts->peer_addr)); opts->flags = NSOCK_CLOSED; return 0; } /* nsocket_opts_free * -1: nsocket_options_t invalide * 0: w00t */ int nsocket_opts_free(pnsocket_options_t opts) { if (!opts) return -1; memset(opts, '\0', sizeof(nsocket_options_t)); return 0; } /* nsocket_perror() * print errno, socket error code and on win32 platform, WSAGetLastError() */ void nsocket_perror(pnsocket_t s, const char *fname, const char *cname) { if(s == NULL) return; if (NSOCKET_DO_VERBOSE) #if defined (_WIN32) || defined (WIN32) printf("\n\t[E] %s: %s - errno: %d SO_ERROR: %d WSAGetLastError: %d\n", fname,cname,errno,nsocket_get_error(s),WSAGetLastError()); #else printf("\n\t[E] %s: %s - errno: %d SO_ERROR: %d\n",fname,cname,errno, nsocket_get_error(s)); #endif }
=---------------------------END nsocket.c -------------------------------= =---------------------------BEGIN test.c --------------------------------=
#include <stdio.h> #include <string.h> #include "nsocket.h" int main(void) { pnsocket_t s; char buf[1024]; int nbytes; /* initialisation */ if(nsocket_init() ==-1) return(-1); /* création du socket */ if ((s = nsocket_create(NSOCK_DEFAULT)) == NULL) return(-1); /* on lance le connect */ if (nsocket_connect(s,"72.14.203.104",80) == -1) { nsocket_perror(s,"main()","nsocket_connect()"); nsocket_close(s); nsocket_free(s); return(-1); } strcpy(buf, "GET / HTTP/1.0\n\n"); if (nsocket_write(s,buf,strlen(buf)) == -1) { nsocket_close(s); nsocket_free(s); return(-1); } if ((nbytes = nsocket_read(s,buf,1024)) == -1) { nsocket_close(s); nsocket_free(s); return(-1); } fwrite("[Read]\n",sizeof(char),7,stdout); if (nbytes) fwrite(buf,sizeof(char),nbytes,stdout); /* on ferme le socket */ nsocket_close(s); nsocket_free(s); /* clean up */ nsocket_shutdown(); return(0); }
=---------------------------END test.c ----------------------------------= 6. Conclusion C'est la partie que je déteste probablement le plus, alors je vais faire ça "sweet and short " comme diraient nos copains anglophones. Si vous trouvez cet article intéressant, utile ou que vous l'avez détesté; faites-moi-le savoir. Bien sûr, si vous n'avez pas aimé dites-moi au moins pourquoi, de façon constructive et non pas juste un chargement d'insulte. Croyez-moi, je sais à qui parler pour en avoir. Sur ce, à une prochaine fois. Oh! Juste comme ça, en passant. Ce code est loin d'être parfait. Je l'ai testé rapidement sur windows et linux mais il y a surement de l'optimisation à faire et peut-être un ou deux bugs. Mais le but de ce texte n'est que de vous donner un exemple ou une base pour un éventuel re-write.