Unix Socket - Guide rapide

Les sockets permettent la communication entre deux processus différents sur des machines identiques ou différentes. Pour être plus précis, c'est un moyen de parler à d'autres ordinateurs en utilisant des descripteurs de fichiers Unix standard. Sous Unix, chaque action d'E / S se fait en écrivant ou en lisant un descripteur de fichier. Un descripteur de fichier est juste un entier associé à un fichier ouvert et il peut s'agir d'une connexion réseau, d'un fichier texte, d'un terminal ou autre.

Pour un programmeur, une socket ressemble et se comporte comme un descripteur de fichier de bas niveau. C'est parce que les commandes telles que read () et write () fonctionnent avec les sockets de la même manière qu'elles le font avec les fichiers et les tubes.

Les sockets ont été introduits pour la première fois dans 2.1BSD et ensuite raffinés dans leur forme actuelle avec 4.2BSD. La fonction sockets est désormais disponible avec la plupart des versions actuelles du système UNIX.

Où Socket est-il utilisé?

Un socket Unix est utilisé dans un cadre d'application client-serveur. Un serveur est un processus qui exécute certaines fonctions à la demande d'un client. La plupart des protocoles de niveau application comme FTP, SMTP et POP3 utilisent des sockets pour établir une connexion entre le client et le serveur, puis pour échanger des données.

Types de socket

Il existe quatre types de prises disponibles pour les utilisateurs. Les deux premiers sont les plus couramment utilisés et les deux derniers sont rarement utilisés.

Les processus sont supposés communiquer uniquement entre les sockets du même type, mais il n'y a aucune restriction qui empêche la communication entre les sockets de types différents.

  • Stream Sockets- La livraison dans un environnement en réseau est garantie. Si vous envoyez via le socket de flux trois éléments "A, B, C", ils arriveront dans le même ordre - "A, B, C". Ces sockets utilisent TCP (Transmission Control Protocol) pour la transmission de données. Si la livraison est impossible, l'expéditeur reçoit un indicateur d'erreur. Les enregistrements de données n'ont pas de limites.

  • Datagram Sockets- La livraison dans un environnement en réseau n'est pas garantie. Ils sont sans connexion car vous n'avez pas besoin d'avoir une connexion ouverte comme dans Stream Sockets - vous créez un paquet avec les informations de destination et l'envoyez. Ils utilisent UDP (User Datagram Protocol).

  • Raw Sockets- Ils permettent aux utilisateurs d'accéder aux protocoles de communication sous-jacents, qui prennent en charge les abstractions de socket. Ces sockets sont normalement orientés datagramme, bien que leurs caractéristiques exactes dépendent de l'interface fournie par le protocole. Les sockets bruts ne sont pas destinés à l'utilisateur général; ils ont été fournis principalement à ceux qui souhaitent développer de nouveaux protocoles de communication ou pour accéder à certaines des fonctionnalités les plus cryptiques d'un protocole existant.

  • Sequenced Packet Sockets- Ils sont similaires à une socket de flux, à l'exception du fait que les limites d'enregistrement sont préservées. Cette interface est fournie uniquement dans le cadre de l'abstraction de socket NS (Network Systems) et est très importante dans les applications NS les plus sérieuses. Les sockets de paquets séquencés permettent à l'utilisateur de manipuler les en-têtes SPP (Sequence Packet Protocol) ou Internet Datagram Protocol (IDP) sur un paquet ou un groupe de paquets, soit en écrivant un en-tête prototype avec toutes les spécifiant un en-tête par défaut à utiliser avec toutes les données sortantes, et permet à l'utilisateur de recevoir les en-têtes sur les paquets entrants.

Quelle est la prochaine?

Les prochains chapitres sont destinés à renforcer vos bases et à préparer une base avant de pouvoir écrire des programmes Serveur et Client à l'aide de socket . Si vous voulez directement voir comment écrire un programme client et serveur, vous pouvez le faire, mais ce n'est pas recommandé. Il est fortement recommandé de procéder étape par étape et de compléter ces quelques premiers chapitres pour faire votre base avant de passer à la programmation.

Avant de passer aux choses réelles, parlons un peu des adresses réseau - l'adresse IP.

L'adresse IP d'hôte, ou plus communément l'adresse IP, est utilisée pour identifier les hôtes connectés à Internet. IP signifie Internet Protocol et fait référence à la couche Internet de l'architecture réseau globale de l'Internet.

Une adresse IP est une quantité de 32 bits interprétée comme quatre nombres ou octets de 8 bits. Chaque adresse IP identifie de manière unique le réseau utilisateur participant, l'hôte sur le réseau et la classe du réseau utilisateur.

Une adresse IP est généralement écrite dans une notation décimale à points de la forme N1.N2.N3.N4, où chaque Ni est un nombre décimal compris entre 0 et 255 décimal (00 à FF hexadécimal).

Classes d'adresses

Les adresses IP sont gérées et créées par l' Internet Assigned Numbers Authority (IANA). Il existe cinq classes d'adresses différentes. Vous pouvez déterminer dans quelle classe appartient une adresse IP en examinant les quatre premiers bits de l'adresse IP.

  • Class A les adresses commencent par 0xxx, ou 1 to 126 décimal.

  • Class B les adresses commencent par 10xx, ou 128 to 191 décimal.

  • Class C les adresses commencent par 110x, ou 192 to 223 décimal.

  • Class D les adresses commencent par 1110, ou 224 to 239 décimal.

  • Class E les adresses commencent par 1111, ou 240 to 254 décimal.

Adresses commençant par 01111111, ou 127 décimal, sont réservés pour le bouclage et pour les tests internes sur une machine locale [Vous pouvez tester ceci: vous devriez toujours pouvoir envoyer un ping 127.0.0.1, qui pointe vers vous]; Les adresses de classe D sont réservées à la multidiffusion; Les adresses de classe E sont réservées pour une utilisation future. Ils ne doivent pas être utilisés pour les adresses d'hôte.

Exemple

Class Leftmost bits Start address Finish address
UNE 0xxx 0.0.0.0 127.255.255.255
B 10xx 128.0.0.0 191.255.255.255
C 110x 192.0.0.0 223.255.255.255
1110 224.0.0.0 239.255.255.255
E 1111 240.0.0.0 255.255.255.255

Sous-réseau

Le sous-réseau ou le sous-réseau signifie essentiellement de dériver un réseau. Cela peut être fait pour diverses raisons telles que le réseau dans une organisation, l'utilisation de différents supports physiques (tels que Ethernet, FDDI, WAN, etc.), la préservation de l'espace d'adressage et la sécurité. La raison la plus courante est de contrôler le trafic réseau.

L'idée de base du sous-réseau est de partitionner la partie identifiant d'hôte de l'adresse IP en deux parties -

  • Une adresse de sous-réseau dans l'adresse réseau elle-même; et
  • Une adresse d'hôte sur le sous-réseau.

Par exemple, un format d'adresse de classe B courant est N1.N2.SH, où N1.N2 identifie le réseau de classe B, le champ S 8 bits identifie le sous-réseau et le champ H 8 bits identifie l'hôte sur le sous-réseau.

Les noms d'hôtes en termes de nombres sont difficiles à retenir et sont donc appelés par des noms ordinaires tels que Takshila ou Nalanda. Nous écrivons des applications logicielles pour connaître l'adresse IP pointée correspondant à un nom donné.

Le processus de recherche d'une adresse IP pointillée en fonction du nom d'hôte alphanumérique donné est appelé hostname resolution.

Une résolution de nom d'hôte est effectuée par un logiciel spécial résidant sur des systèmes haute capacité. Ces systèmes sont appelés DNS (Domain Name Systems), qui conservent le mappage des adresses IP et des noms ordinaires correspondants.

Le fichier / etc / hosts

La correspondance entre les noms d'hôtes et les adresses IP est conservée dans un fichier appelé hôtes . Sur la plupart des systèmes, ce fichier se trouve dans/etc annuaire.

Les entrées de ce fichier ressemblent à ce qui suit -

# This represents a comments in /etc/hosts file.
127.0.0.1       localhost
192.217.44.207  nalanda metro
153.110.31.18   netserve
153.110.31.19   mainserver centeral
153.110.31.20   samsonite
64.202.167.10   ns3.secureserver.net
64.202.167.97   ns4.secureserver.net
66.249.89.104   www.google.com
68.178.157.132  services.amrood.com

Notez que plus d'un nom peut être associé à une adresse IP donnée. Ce fichier est utilisé lors de la conversion de l'adresse IP en nom d'hôte et vice versa.

Vous n'auriez pas accès pour modifier ce fichier, donc si vous souhaitez mettre un nom d'hôte avec une adresse IP, vous devez disposer de l'autorisation root.

La plupart des applications Net utilisent l'architecture Client-Serveur, qui fait référence à deux processus ou deux applications qui communiquent entre eux pour échanger des informations. L'un des deux processus agit comme un processus client et un autre processus agit comme un serveur.

Processus client

C'est le processus, qui fait généralement une demande d'informations. Après avoir obtenu la réponse, ce processus peut se terminer ou effectuer un autre traitement.

Example, Internet Browser fonctionne comme une application cliente, qui envoie une demande au serveur Web pour obtenir une page Web HTML.

Processus serveur

C'est le processus qui prend une demande des clients. Après avoir reçu une demande du client, ce processus effectuera le traitement requis, collectera les informations demandées et les enverra au client demandeur. Une fois terminé, il devient prêt à servir un autre client. Les processus du serveur sont toujours alertes et prêts à répondre aux demandes entrantes.

Example - Le serveur Web attend les demandes des navigateurs Internet et dès qu'il reçoit une demande d'un navigateur, il récupère une page HTML demandée et la renvoie à ce navigateur.

Notez que le client a besoin de connaître l'adresse du serveur, mais que le serveur n'a pas besoin de connaître l'adresse ni même l'existence du client avant que la connexion ne soit établie. Une fois la connexion établie, les deux parties peuvent envoyer et recevoir des informations.

Architectures à 2 et 3 niveaux

Il existe deux types d'architectures client-serveur -

  • 2-tier architecture- Dans cette architecture, le client interagit directement avec le serveur. Ce type d'architecture peut présenter des failles de sécurité et des problèmes de performances. Internet Explorer et Web Server fonctionnent sur une architecture à deux niveaux. Ici, les problèmes de sécurité sont résolus à l'aide de Secure Socket Layer (SSL).

  • 3-tier architectures- Dans cette architecture, un logiciel supplémentaire se trouve entre le client et le serveur. Ce logiciel intermédiaire est appelé «middleware». Les intergiciels sont utilisés pour effectuer toutes les vérifications de sécurité et l'équilibrage de charge en cas de charge importante. Un middleware prend toutes les demandes du client et après avoir effectué l'authentification requise, il transmet cette demande au serveur. Ensuite, le serveur effectue le traitement requis et renvoie la réponse au middleware et enfin le middleware renvoie cette réponse au client. Si vous souhaitez implémenter une architecture à 3 niveaux, vous pouvez conserver n'importe quel middleware comme Web Logic ou WebSphere entre votre serveur Web et votre navigateur Web.

Types de serveur

Vous pouvez avoir deux types de serveurs -

  • Iterative Server- C'est la forme la plus simple de serveur où un processus serveur sert un client et après avoir terminé la première demande, il prend la demande d'un autre client. Pendant ce temps, un autre client attend.

  • Concurrent Servers- Ce type de serveur exécute plusieurs processus simultanés pour traiter plusieurs demandes à la fois car un processus peut prendre plus de temps et un autre client ne peut pas attendre aussi longtemps. Le moyen le plus simple d'écrire un serveur concurrent sous Unix est de créer un processus enfant pour gérer chaque client séparément.

Comment faire du client

Les appels système pour établir une connexion sont quelque peu différents pour le client et le serveur, mais tous deux impliquent la construction de base d'un socket. Les deux processus établissent leurs propres sockets.

Les étapes impliquées dans l'établissement d'un socket côté client sont les suivantes:

  • Créez une prise avec le socket() appel système.

  • Connectez la prise à l'adresse du serveur à l'aide du connect() appel système.

  • Envoyez et recevez des données. Il existe plusieurs façons de procéder, mais la façon la plus simple est d'utiliser leread() et write() appels système.

Comment créer un serveur

Les étapes impliquées dans l'établissement d'une socket côté serveur sont les suivantes:

  • Créez une prise avec le socket() appel système.

  • Liez le socket à une adresse en utilisant le bind()appel système. Pour une socket serveur sur Internet, une adresse se compose d'un numéro de port sur la machine hôte.

  • Écoutez les connexions avec le listen() appel système.

  • Acceptez une connexion avec le accept()appel système. Cet appel bloque généralement la connexion jusqu'à ce qu'un client se connecte au serveur.

  • Envoyez et recevez des données à l'aide du read() et write() appels système.

Interaction client-serveur

Voici le diagramme montrant l'interaction complète du client et du serveur -

Diverses structures sont utilisées dans la programmation de socket Unix pour contenir des informations sur l'adresse et le port, ainsi que d'autres informations. La plupart des fonctions de socket nécessitent un pointeur vers une structure d'adresse de socket comme argument. Les structures définies dans ce chapitre sont liées à la famille de protocoles Internet.

chaussette

La première structure est sockaddr qui contient les informations de socket -

struct sockaddr {
   unsigned short   sa_family;
   char             sa_data[14];
};

Il s'agit d'une structure d'adresse de socket générique, qui sera transmise dans la plupart des appels de fonction de socket. Le tableau suivant fournit une description des champs de membre -

Attribut Valeurs La description
sa_famille

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Il représente une famille d'adresses. Dans la plupart des applications Internet, nous utilisons AF_INET.
sa_data Adresse spécifique au protocole Le contenu des 14 octets d'adresse spécifique au protocole est interprété en fonction du type d'adresse. Pour la famille Internet, nous utiliserons l'adresse IP du numéro de port, qui est représentée par la structure sockaddr_in définie ci-dessous.

sockaddr dans

La deuxième structure qui vous aide à faire référence aux éléments du socket est la suivante -

struct sockaddr_in {
   short int            sin_family;
   unsigned short int   sin_port;
   struct in_addr       sin_addr;
   unsigned char        sin_zero[8];
};

Voici la description des champs membres -

Attribut Valeurs La description
sa_famille

AF_INET

AF_UNIX

AF_NS

AF_IMPLINK

Il représente une famille d'adresses. Dans la plupart des applications Internet, nous utilisons AF_INET.
sin_port Port de service Un numéro de port 16 bits dans l'ordre des octets du réseau.
sin_addr Adresse IP Une adresse IP 32 bits dans l'ordre des octets du réseau.
sin_zero Non utilisé Vous venez de définir cette valeur sur NULL car cela n'est pas utilisé.

dans adr

Cette structure est utilisée uniquement dans la structure ci-dessus en tant que champ de structure et contient un netid / hostid de 32 bits.

struct in_addr {
   unsigned long s_addr;
};

Voici la description des champs membres -

Attribut Valeurs La description
s_addr port de service Une adresse IP 32 bits dans l'ordre des octets du réseau.

hôte

Cette structure est utilisée pour conserver les informations relatives à l'hôte.

struct hostent {
   char *h_name; 
   char **h_aliases; 
   int h_addrtype;  
   int h_length;    
   char **h_addr_list
	
#define h_addr  h_addr_list[0]
};

Voici la description des champs membres -

Attribut Valeurs La description
h_name ti.com etc. C'est le nom officiel de l'hôte. Par exemple, tutorialspoint.com, google.com, etc.
h_aliases TI Il contient une liste d'alias de nom d'hôte.
h_addrtype AF_INET Il contient la famille d'adresses et dans le cas d'une application basée sur Internet, ce sera toujours AF_INET.
h_length 4 Il contient la longueur de l'adresse IP, qui est de 4 pour l'adresse Internet.
h_addr_list in_addr Pour les adresses Internet, le tableau de pointeurs h_addr_list [0], h_addr_list [1], etc., sont des points vers la structure in_addr.

NOTE - h_addr est défini comme h_addr_list [0] pour conserver la compatibilité descendante.

servent

Cette structure particulière est utilisée pour conserver les informations relatives au service et aux ports associés.

struct servent {
   char  *s_name; 
   char  **s_aliases; 
   int   s_port;  
   char  *s_proto;
};

Voici la description des champs membres -

Attribut Valeurs La description
Le nom de http C'est le nom officiel du service. Par exemple, SMTP, FTP POP3, etc.
s_aliases ALIAS Il contient la liste des alias de service. La plupart du temps, ce paramètre sera défini sur NULL.
sport 80 Il aura un numéro de port associé. Par exemple, pour HTTP, ce sera 80.
s_proto

TCP

UDP

Il est défini sur le protocole utilisé. Les services Internet sont fournis en utilisant TCP ou UDP.

Conseils sur les structures de socket

Les structures d'adresse de socket font partie intégrante de chaque programme réseau. Nous les allouons, les remplissons et leur passons des pointeurs vers diverses fonctions de socket. Parfois, nous passons un pointeur vers l'une de ces structures à une fonction socket et il remplit le contenu.

Nous passons toujours ces structures par référence (c'est-à-dire que nous passons un pointeur vers la structure, pas la structure elle-même), et nous passons toujours la taille de la structure comme un autre argument.

Lorsqu'une fonction socket remplit une structure, la longueur est également passée par référence, afin que sa valeur puisse être mise à jour par la fonction. Nous appelons ces arguments valeur-résultat.

Toujours, définissez les variables de structure sur NULL (c'est-à-dire, '\ 0') en utilisant memset () pour les fonctions bzero (), sinon il peut obtenir des valeurs indésirables inattendues dans votre structure.

Lorsqu'un processus client souhaite connecter un serveur, le client doit disposer d'un moyen d'identifier le serveur auquel il souhaite se connecter. Si le client connaît l'adresse Internet 32 ​​bits de l'hôte sur lequel réside le serveur, il peut contacter cet hôte. Mais comment le client identifie-t-il le processus serveur particulier en cours d'exécution sur cet hôte?

Pour résoudre le problème d'identification d'un processus serveur particulier s'exécutant sur un hôte, TCP et UDP ont défini un groupe de ports bien connus.

Pour notre propos, un port sera défini comme un nombre entier compris entre 1024 et 65535. En effet, tous les numéros de port inférieurs à 1024 sont considérés comme connus - par exemple, telnet utilise le port 23, http utilise 80, ftp utilise 21, etc.

Les affectations de port aux services réseau se trouvent dans le fichier / etc / services. Si vous écrivez votre propre serveur, vous devez prendre soin d'attribuer un port à votre serveur. Vous devez vous assurer que ce port ne doit être attribué à aucun autre serveur.

Normalement, il est courant d'attribuer un numéro de port supérieur à 5000. Mais il existe de nombreuses organisations qui ont écrit des serveurs dont les numéros de port sont supérieurs à 5000. Par exemple, Yahoo Messenger fonctionne sur 5050, SIP Server sur 5060, etc.

Exemples de ports et de services

Voici une petite liste de services et de ports associés. Vous pouvez trouver la liste la plus à jour des ports Internet et des services associés sur IANA - Affectations de ports TCP / IP .

Service Port Number Service Description
écho sept UDP / TCP renvoie ce qu'il reçoit.
Jeter 9 UDP / TCP jette l'entrée.
jour 13 UDP / TCP renvoie l'heure ASCII.
charger 19 UDP / TCP renvoie des caractères.
ftp 21 Transfert de fichiers TCP.
telnet 23 Connexion à distance TCP.
smtp 25 Courriel TCP.
jour 37 UDP / TCP renvoie l'heure binaire.
tftp 69 Transfert de fichiers trivial UDP.
doigt 79 Informations TCP sur les utilisateurs.
http 80 TCP World Wide Web.
s'identifier 513 Connexion à distance TCP.
qui 513 UDP informations différentes sur les utilisateurs.
Xserver 6000 Fenêtres TCP X (NB> 1023).

Fonctions de port et de service

Unix fournit les fonctions suivantes pour récupérer le nom du service dans le fichier / etc / services.

  • struct servent *getservbyname(char *name, char *proto) - Cet appel prend le nom du service et le nom du protocole, et renvoie le numéro de port correspondant pour ce service.

  • struct servent *getservbyport(int port, char *proto) - Cet appel prend le numéro de port et le nom du protocole, et renvoie le nom de service correspondant.

La valeur de retour de chaque fonction est un pointeur vers une structure de la forme suivante -

struct servent {
   char  *s_name;
   char  **s_aliases;
   int   s_port;
   char  *s_proto;
};

Voici la description des champs membres -

Attribut Valeurs La description
Le nom de http C'est le nom officiel du service. Par exemple, SMTP, FTP POP3, etc.
s_aliases ALIAS Il contient la liste des alias de service. La plupart du temps, il sera défini sur NULL.
sport 80 Il aura le numéro de port associé. Par exemple, pour HTTP, ce sera 80.
s_proto

TCP

UDP

Il est défini sur le protocole utilisé. Les services Internet sont fournis en utilisant TCP ou UDP.

Malheureusement, tous les ordinateurs ne stockent pas les octets qui composent une valeur multi-octets dans le même ordre. Considérez un Internet 16 bits composé de 2 octets. Il existe deux manières de stocker cette valeur.

  • Little Endian - Dans ce schéma, l'octet de poids faible est stocké sur l'adresse de départ (A) et l'octet de poids fort est stocké sur l'adresse suivante (A + 1).

  • Big Endian - Dans ce schéma, l'octet de poids fort est stocké sur l'adresse de départ (A) et l'octet de poids faible est stocké sur l'adresse suivante (A + 1).

Pour permettre aux machines avec différentes conventions d'ordre d'octet de communiquer entre elles, les protocoles Internet spécifient une convention d'ordre d'octet canonique pour les données transmises sur le réseau. C'est ce qu'on appelle l'ordre des octets du réseau.

Lors de l'établissement d'une connexion de socket Internet, vous devez vous assurer que les données des membres sin_port et sin_addr de la structure sockaddr_in sont représentées dans l'ordre des octets du réseau.

Fonctions de classement des octets

Les routines de conversion des données entre la représentation interne d'un hôte et l'ordre des octets du réseau sont les suivantes:

Fonction La description
htons () Host to Network Short
htonl () Hôte vers réseau long
ntohl () Réseau pour héberger longtemps
ntohs () Réseau à hôte court

Vous trouverez ci-dessous plus de détails sur ces fonctions -

  • unsigned short htons(unsigned short hostshort) - Cette fonction convertit des quantités de 16 bits (2 octets) de l'ordre des octets de l'hôte en ordre des octets du réseau.

  • unsigned long htonl(unsigned long hostlong) - Cette fonction convertit les quantités 32 bits (4 octets) de l'ordre des octets de l'hôte en ordre des octets du réseau.

  • unsigned short ntohs(unsigned short netshort) - Cette fonction convertit des quantités de 16 bits (2 octets) de l'ordre des octets du réseau en ordre des octets de l'hôte.

  • unsigned long ntohl(unsigned long netlong) - Cette fonction convertit les quantités 32 bits de l'ordre des octets du réseau en ordre des octets de l'hôte.

Ces fonctions sont des macros et entraînent l'insertion du code source de la conversion dans le programme appelant. Sur les machines little-endian, le code changera les valeurs autour de l'ordre des octets du réseau. Sur les machines big-endian, aucun code n'est inséré car aucun n'est nécessaire; les fonctions sont définies comme nulles.

Programme pour déterminer l'ordre des octets de l'hôte

Conservez le code suivant dans un fichier byteorder.c , puis compilez-le et exécutez-le sur votre ordinateur .

Dans cet exemple, nous stockons la valeur de deux octets 0x0102 dans l'entier court, puis examinons les deux octets consécutifs, c [0] (l'adresse A) et c [1] (l'adresse A + 1) pour déterminer l'octet ordre.

#include <stdio.h>

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

   union {
      short s;
      char c[sizeof(short)];
   }un;
	
   un.s = 0x0102;
   
   if (sizeof(short) == 2) {
      if (un.c[0] == 1 && un.c[1] == 2)
         printf("big-endian\n");
      
      else if (un.c[0] == 2 && un.c[1] == 1)
         printf("little-endian\n");
      
      else
         printf("unknown\n");
   }
   else {
      printf("sizeof(short) = %d\n", sizeof(short));
   }
	
   exit(0);
}

Une sortie générée par ce programme sur une machine Pentium est la suivante -

$> gcc byteorder.c $> ./a.out
little-endian
$>

Unix fournit divers appels de fonctions pour vous aider à manipuler les adresses IP. Ces fonctions convertissent les adresses Internet entre des chaînes ASCII (ce que les humains préfèrent utiliser) et des valeurs binaires ordonnées par octets du réseau (valeurs stockées dans des structures d'adresses de socket).

Les trois appels de fonction suivants sont utilisés pour l'adressage IPv4 -

  • int inet_aton (const char * strptr, struct in_addr * addrptr)
  • in_addr_t inet_addr (const char * strptr)
  • char * inet_ntoa (struct in_addr inaddr)

int inet_aton (const char * strptr, struct in_addr * addrptr)

Cet appel de fonction convertit la chaîne spécifiée dans la notation par points standard Internet en une adresse réseau et stocke l'adresse dans la structure fournie. L'adresse convertie sera dans l'ordre des octets du réseau (octets classés de gauche à droite). Il renvoie 1 si la chaîne était valide et 0 en cas d'erreur.

Voici l'exemple d'utilisation -

#include <arpa/inet.h>

(...)

   int retval;
   struct in_addr addrptr
   
   memset(&addrptr, '\0', sizeof(addrptr));
   retval = inet_aton("68.178.157.132", &addrptr);

(...)

in_addr_t inet_addr (const char * strptr)

Cet appel de fonction convertit la chaîne spécifiée dans la notation par points standard Internet en une valeur entière pouvant être utilisée comme adresse Internet. L'adresse convertie sera dans l'ordre des octets du réseau (octets classés de gauche à droite). Il renvoie une adresse IPv4 ordonnée par octet de réseau binaire 32 bits et INADDR_NONE en cas d'erreur.

Voici l'exemple d'utilisation -

#include <arpa/inet.h>

(...)

   struct sockaddr_in dest;

   memset(&dest, '\0', sizeof(dest));
   dest.sin_addr.s_addr = inet_addr("68.178.157.132");
   
(...)

char * inet_ntoa (struct in_addr inaddr)

Cet appel de fonction convertit l'adresse d'hôte Internet spécifiée en une chaîne dans la notation par points standard Internet.

Voici l'exemple d'utilisation -

#include <arpa/inet.h>

(...)

   char *ip;
   
   ip = inet_ntoa(dest.sin_addr);
   
   printf("IP Address is: %s\n",ip);
   
(...)

Ce chapitre décrit les principales fonctions de socket requises pour écrire un client et un serveur TCP complets.

Le diagramme suivant montre l'interaction complète du client et du serveur -

La fonction socket

Pour effectuer des E / S réseau, la première chose qu'un processus doit faire est d'appeler la fonction socket, en spécifiant le type de protocole de communication souhaité et la famille de protocoles, etc.

#include <sys/types.h>
#include <sys/socket.h>

int socket (int family, int type, int protocol);

Cet appel renvoie un descripteur de socket que vous pouvez utiliser dans les appels système ultérieurs ou -1 en cas d'erreur.

Paramètres

family - Il spécifie la famille de protocoles et est l'une des constantes ci-dessous -

Famille La description
AF_INET Protocoles IPv4
AF_INET6 Protocoles IPv6
AF_LOCAL Protocoles de domaine Unix
AF_ROUTE Routage des sockets
AF_KEY Prise Ket

Ce chapitre ne couvre pas les autres protocoles sauf IPv4.

type- Il spécifie le type de socket que vous souhaitez. Il peut prendre l'une des valeurs suivantes -

Type La description
SOCK_STREAM Prise de flux
SOCK_DGRAM Prise datagramme
SOCK_SEQPACKET Socket de paquet séquencé
SOCK_RAW Prise brute

protocol - L'argument doit être défini sur le type de protocole spécifique donné ci-dessous, ou sur 0 pour sélectionner la valeur par défaut du système pour la combinaison donnée de famille et de type -

Protocole La description
IPPROTO_TCP Protocole de transport TCP
IPPROTO_UDP Protocole de transport UDP
IPPROTO_SCTP Protocole de transport SCTP

La fonction de connexion

La fonction de connexion est utilisée par un client TCP pour établir une connexion avec un serveur TCP.

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

Cet appel renvoie 0 s'il se connecte avec succès au serveur, sinon il renvoie -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • serv_addr - C'est un pointeur vers la structure sockaddr qui contient l'adresse IP et le port de destination.

  • addrlen - Réglez-le sur sizeof (struct sockaddr).

La fonction bind

La fonction de liaison attribue une adresse de protocole local à un socket. Avec les protocoles Internet, l'adresse de protocole est la combinaison d'une adresse IPv4 32 bits ou d'une adresse IPv6 128 bits, avec un numéro de port TCP ou UDP 16 bits. Cette fonction est appelée uniquement par le serveur TCP.

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr,int addrlen);

Cet appel renvoie 0 s'il se lie avec succès à l'adresse, sinon il renvoie -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • my_addr - C'est un pointeur vers struct sockaddr qui contient l'adresse IP locale et le port.

  • addrlen - Réglez-le sur sizeof (struct sockaddr).

Vous pouvez mettre votre adresse IP et votre port automatiquement

Une valeur 0 pour le numéro de port signifie que le système choisira un port aléatoire, et la valeur INADDR_ANY pour l'adresse IP signifie que l'adresse IP du serveur sera attribuée automatiquement.

server.sin_port = 0;  		     
server.sin_addr.s_addr = INADDR_ANY;

NOTE- Tous les ports inférieurs à 1024 sont réservés. Vous pouvez définir un port au-dessus de 1024 et en dessous de 65535 à moins qu'ils ne soient ceux utilisés par d'autres programmes.

La fonction d' écoute

La fonction d' écoute est appelée uniquement par un serveur TCP et elle effectue deux actions -

  • La fonction Listen convertit une socket non connectée en une socket passive, indiquant que le noyau doit accepter les demandes de connexion entrantes dirigées vers cette socket.

  • Le deuxième argument de cette fonction spécifie le nombre maximum de connexions que le noyau doit mettre en file d'attente pour cette socket.

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);

Cet appel renvoie 0 en cas de succès, sinon il renvoie -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • backlog - C'est le nombre de connexions autorisées.

La fonction d' acceptation

La fonction d' acceptation est appelée par un serveur TCP pour renvoyer la prochaine connexion terminée depuis l'avant de la file d'attente de connexion terminée. La signature de l'appel est la suivante -

#include <sys/types.h>
#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

Cet appel renvoie un descripteur non négatif en cas de succès, sinon il renvoie -1 en cas d'erreur. Le descripteur renvoyé est supposé être un descripteur de socket client et toutes les opérations de lecture-écriture seront effectuées sur ce descripteur pour communiquer avec le client.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • cliaddr - C'est un pointeur vers struct sockaddr qui contient l'adresse IP et le port du client.

  • addrlen - Réglez-le sur sizeof (struct sockaddr).

La fonction d' envoi

La fonction d' envoi est utilisée pour envoyer des données sur des sockets de flux ou des sockets de datagramme CONNECTÉS. Si vous souhaitez envoyer des données via des sockets de datagramme NON CONNECTÉS, vous devez utiliser la fonction sendto ().

Vous pouvez utiliser l' appel système write () pour envoyer des données. Sa signature est la suivante -

int send(int sockfd, const void *msg, int len, int flags);

Cet appel renvoie le nombre d'octets envoyés, sinon il retournera -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • msg - C'est un pointeur vers les données que vous souhaitez envoyer.

  • len - C'est la longueur des données que vous souhaitez envoyer (en octets).

  • flags - Il est mis à 0.

La fonction recv

La fonction recv est utilisée pour recevoir des données sur des sockets de flux ou des sockets de datagramme CONNECTÉS. Si vous souhaitez recevoir des données sur des sockets de datagramme NON CONNECTÉS, vous devez utiliser recvfrom ().

Vous pouvez utiliser l' appel système read () pour lire les données. Cet appel est expliqué dans le chapitre sur les fonctions d'assistance.

int recv(int sockfd, void *buf, int len, unsigned int flags);

Cet appel renvoie le nombre d'octets lus dans le tampon, sinon il renverra -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • buf - C'est le tampon dans lequel lire les informations.

  • len - C'est la longueur maximale du tampon.

  • flags - Il est mis à 0.

La fonction sendto

La fonction sendto est utilisée pour envoyer des données sur des sockets de datagramme NON CONNECTÉS. Sa signature est la suivante -

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

Cet appel renvoie le nombre d'octets envoyés, sinon il renvoie -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • msg - C'est un pointeur vers les données que vous souhaitez envoyer.

  • len - C'est la longueur des données que vous souhaitez envoyer (en octets).

  • flags - Il est mis à 0.

  • to - C'est un pointeur vers la structure sockaddr de l'hôte où les données doivent être envoyées.

  • tolen - Il est défini sur sizeof (struct sockaddr).

La fonction recvfrom

La fonction recvfrom est utilisée pour recevoir des données de sockets de datagramme NON CONNECTÉS .

int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);

Cet appel renvoie le nombre d'octets lus dans le tampon, sinon il renvoie -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • buf - C'est le tampon dans lequel lire les informations.

  • len - C'est la longueur maximale du tampon.

  • flags - Il est mis à 0.

  • from - C'est un pointeur vers struct sockaddr pour l'hôte où les données doivent être lues.

  • fromlen - Il est défini sur sizeof (struct sockaddr).

La fonction close

La fonction de fermeture est utilisée pour fermer la communication entre le client et le serveur. Sa syntaxe est la suivante -

int close( int sockfd );

Cet appel renvoie 0 en cas de succès, sinon il renvoie -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

La fonction d' arrêt

La fonction d' arrêt est utilisée pour fermer en douceur la communication entre le client et le serveur. Cette fonction donne plus de contrôle par rapport à la fonction de fermeture . Ci-dessous, la syntaxe de l' arrêt -

int shutdown(int sockfd, int how);

Cet appel renvoie 0 en cas de succès, sinon il renvoie -1 en cas d'erreur.

Paramètres

  • sockfd - C'est un descripteur de socket renvoyé par la fonction socket.

  • how - Mettez l'un des nombres -

    • 0 - indique que la réception n'est pas autorisée,

    • 1 - indique que l'envoi n'est pas autorisé, et

    • 2- indique que l'envoi et la réception ne sont pas autorisés. Quand how est défini sur 2, c'est la même chose que close ().

La fonction de sélection

La fonction de sélection indique lequel des descripteurs de fichier spécifiés est prêt pour la lecture, prêt pour l'écriture ou a une condition d'erreur en attente.

Lorsqu'une application appelle recv ou recvfrom , elle est bloquée jusqu'à ce que les données arrivent pour ce socket. Une application peut effectuer d'autres traitements utiles alors que le flux de données entrant est vide. Une autre situation se produit lorsqu'une application reçoit des données de plusieurs sockets.

L'appel de recv ou recvfrom sur un socket qui n'a pas de données dans sa file d'attente d'entrée empêche la réception immédiate des données d'autres sockets. L'appel de fonction de sélection résout ce problème en permettant au programme d'interroger tous les descripteurs de socket pour voir s'ils sont disponibles pour les opérations de lecture et d'écriture non bloquantes.

Voici la syntaxe de select -

int select(int  nfds, fd_set  *readfds, fd_set  *writefds, fd_set *errorfds, struct timeval *timeout);

Cet appel renvoie 0 en cas de succès, sinon il renvoie -1 en cas d'erreur.

Paramètres

  • nfds- Il spécifie la plage de descripteurs de fichiers à tester. La fonction select () teste les descripteurs de fichier dans la plage de 0 à nfds-1

  • readfds- Il pointe vers un objet de type fd_set qui en entrée, spécifie les descripteurs de fichier à vérifier pour être prêts à lire, et en sortie, indique quels descripteurs de fichier sont prêts à lire. Il peut être NULL pour indiquer un ensemble vide.

  • writefds- Il pointe vers un objet de type fd_set qui en entrée, spécifie les descripteurs de fichier à vérifier pour être prêts à écrire, et en sortie, indique quels descripteurs de fichier sont prêts à écrire. Il peut être NULL pour indiquer un ensemble vide.

  • exceptfds- Il pointe vers un objet de type fd_set qui, en entrée, spécifie les descripteurs de fichier à vérifier pour les conditions d'erreur en attente, et en sortie indique quels descripteurs de fichier ont des conditions d'erreur en attente. Il peut être NULL pour indiquer un ensemble vide.

  • timeout- Il pointe vers une structure timeval qui spécifie la durée pendant laquelle l'appel de sélection doit interroger les descripteurs pour une opération d'E / S disponible. Si la valeur du délai d'expiration est 0, select retournera immédiatement. Si l'argument timeout est NULL, select bloquera jusqu'à ce qu'au moins un descripteur de fichier / socket soit prêt pour une opération d'E / S disponible. Dans le cas contraire, select retournera une fois le temps écoulé dans le délai d'expiration OU lorsqu'au moins un descripteur de fichier / socket est prêt pour une opération d'E / S.

La valeur de retour de select est le nombre de descripteurs spécifiés dans les ensembles de descripteurs de fichiers prêts pour les E / S. Si la limite de temps spécifiée par le champ timeout est atteinte, sélectionnez return 0. Les macros suivantes existent pour manipuler un ensemble de descripteurs de fichiers -

  • FD_CLR(fd, &fdset)- Efface le bit du descripteur de fichier fd dans le jeu de descripteurs de fichier fdset.

  • FD_ISSET(fd, &fdset)- Renvoie une valeur différente de zéro si le bit du descripteur de fichier fd est défini dans l'ensemble de descripteurs de fichier pointé par fdset , et 0 dans le cas contraire.

  • FD_SET(fd, &fdset) - Définit le bit du descripteur de fichier fd dans le jeu de descripteurs de fichier fdset.

  • FD_ZERO(&fdset) - Initialise l'ensemble de descripteurs de fichier fdset pour qu'il ait zéro bit pour tous les descripteurs de fichier.

Le comportement de ces macros n'est pas défini si l'argument fd est inférieur à 0 ou supérieur ou égal à FD_SETSIZE.

Exemple

fd_set fds;

struct timeval tv;

/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;

/* tv now represents 1.5 seconds */
FD_ZERO(&fds);

/* adds sock to the file descriptor set */
FD_SET(sock, &fds); 

/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);

if (FD_ISSET(sock, &fds)) {
   recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
   /* do something */
}
else {
   /* do something else */
}

Ce chapitre décrit toutes les fonctions d'assistance, qui sont utilisées lors de la programmation de socket. D'autres fonctions d'assistance sont décrites dans les chapitres -Ports and Serviceset réseau Byte Orders.

La fonction d' écriture

La fonction d' écriture tente d'écrire n octets du tampon pointé par buf vers le fichier associé au descripteur de fichier ouvert, fildes .

Vous pouvez également utiliser la fonction send () pour envoyer des données à un autre processus.

#include <unistd.h>

int write(int fildes, const void *buf, int nbyte);

En cas de réussite, write () renvoie le nombre d'octets réellement écrits dans le fichier associé à fildes. Ce nombre n'est jamais supérieur à n octets. Sinon, -1 est renvoyé.

Paramètres

  • fildes - C'est un descripteur de socket renvoyé par la fonction socket.

  • buf - C'est un pointeur vers les données que vous souhaitez envoyer.

  • nbyte- C'est le nombre d'octets à écrire. Si nbyte vaut 0, write () retournera 0 et n'aura aucun autre résultat si le fichier est un fichier normal; sinon, les résultats ne sont pas spécifiés.

La fonction de lecture

La fonction de lecture tente de lire n octets du fichier associé au tampon, fildes, dans le tampon pointé par buf.

Vous pouvez également utiliser la fonction recv () pour lire des données vers un autre processus.

#include <unistd.h>

int read(int fildes, const void *buf, int nbyte);

En cas de réussite, write () renvoie le nombre d'octets réellement écrits dans le fichier associé à fildes. Ce nombre n'est jamais supérieur à n octets. Sinon, -1 est renvoyé.

Paramètres

  • fildes - C'est un descripteur de socket renvoyé par la fonction socket.

  • buf - C'est le tampon dans lequel lire les informations.

  • nbyte - C'est le nombre d'octets à lire.

La fonction fourche

La fonction fork crée un nouveau processus. Le nouveau processus appelé processus enfant sera une copie exacte du processus appelant (processus parent). Le processus enfant hérite de nombreux attributs du processus parent.

#include <sys/types.h>
#include <unistd.h>

int fork(void);

Une fois terminé, fork () renvoie 0 au processus enfant et l'ID de processus du processus enfant au processus parent. Sinon, -1 est renvoyé au processus parent, aucun processus enfant n'est créé et errno est défini pour indiquer l'erreur.

Paramètres

  • void - Cela signifie qu'aucun paramètre n'est requis.

La fonction bzero

La fonction bzero place nbyte octets nuls dans la chaîne s . Cette fonction est utilisée pour définir toutes les structures de socket avec des valeurs nulles.

void bzero(void *s, int nbyte);

Cette fonction ne renvoie rien.

Paramètres

  • s- Il spécifie la chaîne qui doit être remplie d'octets nuls. Ce sera une variable de structure point à socket.

  • nbyte- Il spécifie le nombre d'octets à remplir avec des valeurs nulles. Ce sera la taille de la structure du socket.

La fonction bcmp

La fonction bcmp compare la chaîne d'octets s1 à la chaîne d'octets s2. Les deux chaînes sont supposées avoir une longueur de n octets.

int bcmp(const void *s1, const void *s2, int nbyte);

Cette fonction renvoie 0 si les deux chaînes sont identiques, 1 sinon. La fonction bcmp () renvoie toujours 0 lorsque nbyte vaut 0.

Paramètres

  • s1 - Il spécifie la première chaîne à comparer.

  • s2 - Il spécifie la deuxième chaîne à comparer.

  • nbyte - Il spécifie le nombre d'octets à comparer.

La fonction bcopy

La fonction bcopy copie n octets de la chaîne s1 vers la chaîne s2. Les chaînes qui se chevauchent sont gérées correctement.

void bcopy(const void *s1, void *s2, int nbyte);

Cette fonction ne renvoie rien.

Paramètres

  • s1 - Il spécifie la chaîne source.

  • s2v - Il spécifie la chaîne de destination.

  • nbyte - Il spécifie le nombre d'octets à copier.

La fonction memset

La fonction memset est également utilisée pour définir des variables de structure de la même manière quebzero. Jetez un œil à sa syntaxe, donnée ci-dessous.

void *memset(void *s, int c, int nbyte);

Cette fonction renvoie un pointeur vers void; en fait, un pointeur vers la mémoire définie et vous devez le caster en conséquence.

Paramètres

  • s - Il spécifie la source à définir.

  • c - Il spécifie le caractère à définir sur n octets.

  • nbyte - Il spécifie le nombre d'octets à définir.

Pour faire d'un processus un serveur TCP, vous devez suivre les étapes ci-dessous -

  • Créez une socket avec l' appel système socket () .

  • Liez le socket à une adresse à l'aide de l' appel système bind () . Pour une socket serveur sur Internet, une adresse se compose d'un numéro de port sur la machine hôte.

  • Écoutez les connexions avec l' appel système listen () .

  • Acceptez une connexion avec l' appel système accept () . Cet appel se bloque généralement jusqu'à ce qu'un client se connecte au serveur.

  • Envoyez et recevez des données à l'aide des appels système read () et write () .

Maintenant, mettons ces étapes sous forme de code source. Mettez ce code dans le fichier server.c et compilez-le avec le compilateur gcc .

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int  n;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
      
   /* Now start listening for the clients, here process will
      * go in sleep mode and will wait for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   /* Accept actual connection from the client */
   newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
	
   if (newsockfd < 0) {
      perror("ERROR on accept");
      exit(1);
   }
   
   /* If connection is established then start communicating */
   bzero(buffer,256);
   n = read( newsockfd,buffer,255 );
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   
   /* Write a response to the client */
   n = write(newsockfd,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
      
   return 0;
}

Gérer plusieurs connexions

Pour permettre au serveur de gérer plusieurs connexions simultanées, nous apportons les modifications suivantes dans le code ci-dessus -

  • Placez l' instruction accept et le code suivant dans une boucle infinie.

  • Une fois la connexion établie, appelez fork () pour créer un nouveau processus.

  • Le processus enfant fermera sockfd et appellera la fonction de traitement en passant le nouveau descripteur de fichier socket comme argument. Lorsque les deux processus ont terminé leur conversation, comme indiqué par le retour de doprocessing () , ce processus se termine simplement.

  • Le processus parent ferme newsockfd . Comme tout ce code est dans une boucle infinie, il retournera à l'instruction accept pour attendre la prochaine connexion.

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

void doprocessing (int sock);

int main( int argc, char *argv[] ) {
   int sockfd, newsockfd, portno, clilen;
   char buffer[256];
   struct sockaddr_in serv_addr, cli_addr;
   int n, pid;
   
   /* First call to socket() function */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
   
   /* Initialize socket structure */
   bzero((char *) &serv_addr, sizeof(serv_addr));
   portno = 5001;
   
   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);
   
   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR on binding");
      exit(1);
   }
   
   /* Now start listening for the clients, here
      * process will go in sleep mode and will wait
      * for the incoming connection
   */
   
   listen(sockfd,5);
   clilen = sizeof(cli_addr);
   
   while (1) {
      newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
		
      if (newsockfd < 0) {
         perror("ERROR on accept");
         exit(1);
      }
      
      /* Create child process */
      pid = fork();
		
      if (pid < 0) {
         perror("ERROR on fork");
         exit(1);
      }
      
      if (pid == 0) {
         /* This is the client process */
         close(sockfd);
         doprocessing(newsockfd);
         exit(0);
      }
      else {
         close(newsockfd);
      }
		
   } /* end of while */
}

Le segment de code suivant montre une implémentation simple de la fonction de traitement .

void doprocessing (int sock) {
   int n;
   char buffer[256];
   bzero(buffer,256);
   n = read(sock,buffer,255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
   
   printf("Here is the message: %s\n",buffer);
   n = write(sock,"I got your message",18);
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
	
}

Pour faire d'un processus un client TCP, vous devez suivre les étapes ci-dessous & minus;

  • Créez une socket avec l' appel système socket () .

  • Connectez le socket à l'adresse du serveur à l'aide de l' appel système connect () .

  • Envoyez et recevez des données. Il y a plusieurs façons de faire cela, mais la façon la plus simple est d'utiliser les appels système read () et write () .

Maintenant, mettons ces étapes sous forme de code source. Mettez ce code dans le fichierclient.c et compilez-le avec gcc compilateur.

Exécutez ce programme et passez le nom d'hôte et le numéro de port du serveur, pour vous connecter au serveur, que vous devez déjà avoir exécuté dans une autre fenêtre Unix.

#include <stdio.h>
#include <stdlib.h>

#include <netdb.h>
#include <netinet/in.h>

#include <string.h>

int main(int argc, char *argv[]) {
   int sockfd, portno, n;
   struct sockaddr_in serv_addr;
   struct hostent *server;
   
   char buffer[256];
   
   if (argc < 3) {
      fprintf(stderr,"usage %s hostname port\n", argv[0]);
      exit(0);
   }
	
   portno = atoi(argv[2]);
   
   /* Create a socket point */
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   
   if (sockfd < 0) {
      perror("ERROR opening socket");
      exit(1);
   }
	
   server = gethostbyname(argv[1]);
   
   if (server == NULL) {
      fprintf(stderr,"ERROR, no such host\n");
      exit(0);
   }
   
   bzero((char *) &serv_addr, sizeof(serv_addr));
   serv_addr.sin_family = AF_INET;
   bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
   serv_addr.sin_port = htons(portno);
   
   /* Now connect to the server */
   if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
      perror("ERROR connecting");
      exit(1);
   }
   
   /* Now ask for a message from the user, this message
      * will be read by server
   */
	
   printf("Please enter the message: ");
   bzero(buffer,256);
   fgets(buffer,255,stdin);
   
   /* Send message to the server */
   n = write(sockfd, buffer, strlen(buffer));
   
   if (n < 0) {
      perror("ERROR writing to socket");
      exit(1);
   }
   
   /* Now read server response */
   bzero(buffer,256);
   n = read(sockfd, buffer, 255);
   
   if (n < 0) {
      perror("ERROR reading from socket");
      exit(1);
   }
	
   printf("%s\n",buffer);
   return 0;
}

Voici une liste de toutes les fonctions liées à la programmation des sockets.

Fonctions de port et de service

Unix fournit les fonctions suivantes pour récupérer le nom du service dans le fichier / etc / services.

  • struct servent *getservbyname(char *name, char *proto) - Cet appel prend un nom de service et un nom de protocole et renvoie le numéro de port correspondant pour ce service.

  • struct servent *getservbyport(int port, char *proto) - Cet appel prend un numéro de port et un nom de protocole et renvoie le nom de service correspondant.

Fonctions de classement des octets

  • unsigned short htons (unsigned short hostshort) - Cette fonction convertit des quantités de 16 bits (2 octets) de l'ordre des octets de l'hôte en ordre des octets du réseau.

  • unsigned long htonl (unsigned long hostlong) - Cette fonction convertit les quantités 32 bits (4 octets) de l'ordre des octets de l'hôte en ordre des octets du réseau.

  • unsigned short ntohs (unsigned short netshort) - Cette fonction convertit des quantités de 16 bits (2 octets) de l'ordre des octets du réseau en ordre des octets de l'hôte.

  • unsigned long ntohl (unsigned long netlong) - Cette fonction convertit les quantités 32 bits de l'ordre des octets du réseau en ordre des octets de l'hôte.

Fonctions d'adresse IP

  • int inet_aton (const char *strptr, struct in_addr *addrptr)- Cet appel de fonction convertit la chaîne spécifiée, dans la notation par points standard Internet, en une adresse réseau et stocke l'adresse dans la structure fournie. L'adresse convertie sera dans l'ordre des octets du réseau (octets classés de gauche à droite). Il renvoie 1 si la chaîne est valide et 0 en cas d'erreur.

  • in_addr_t inet_addr (const char *strptr)- Cet appel de fonction convertit la chaîne spécifiée, dans la notation par points standard Internet, en une valeur entière adaptée à une utilisation comme adresse Internet. L'adresse convertie sera dans l'ordre des octets du réseau (octets classés de gauche à droite). Il renvoie une adresse IPv4 ordonnée par octet de réseau binaire 32 bits et INADDR_NONE en cas d'erreur.

  • char *inet_ntoa (struct in_addr inaddr) - Cet appel de fonction convertit l'adresse d'hôte Internet spécifiée en une chaîne dans la notation par points standard Internet.

Fonctions principales de socket

  • int socket (int family, int type, int protocol) - Cet appel renvoie un descripteur de socket que vous pouvez utiliser dans les appels système ultérieurs ou il vous donne -1 en cas d'erreur.

  • int connect (int sockfd, struct sockaddr *serv_addr, int addrlen)- La fonction de connexion est utilisée par un client TCP pour établir une connexion avec un serveur TCP. Cet appel renvoie 0 s'il se connecte avec succès au serveur, sinon il renvoie -1.

  • int bind(int sockfd, struct sockaddr *my_addr,int addrlen)- La fonction bind attribue une adresse de protocole local à une socket. Cet appel renvoie 0 s'il se lie avec succès à l'adresse, sinon il renvoie -1.

  • int listen(int sockfd, int backlog)- La fonction d'écoute est appelée uniquement par un serveur TCP pour écouter la demande du client. Cet appel renvoie 0 en cas de succès, sinon il renvoie -1.

  • int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)- La fonction d'acceptation est appelée par un serveur TCP pour accepter les demandes des clients et établir la connexion réelle. Cet appel renvoie un descripteur non négatif en cas de succès, sinon il renvoie -1.

  • int send(int sockfd, const void *msg, int len, int flags)- La fonction send est utilisée pour envoyer des données via des sockets de flux ou des sockets de datagramme CONNECTÉS. Cet appel renvoie le nombre d'octets envoyés, sinon il renvoie -1.

  • int recv (int sockfd, void *buf, int len, unsigned int flags)- La fonction recv est utilisée pour recevoir des données sur des sockets de flux ou des sockets de datagramme CONNECTÉS. Cet appel renvoie le nombre d'octets lus dans le tampon, sinon il renvoie -1 en cas d'erreur.

  • int sendto (int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)- La fonction sendto est utilisée pour envoyer des données sur des sockets de datagramme NON CONNECTÉS. Cet appel renvoie le nombre d'octets envoyés, sinon il renvoie -1 en cas d'erreur.

  • int recvfrom (int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen)- La fonction recvfrom est utilisée pour recevoir des données de sockets de datagramme NON CONNECTÉS. Cet appel renvoie le nombre d'octets lus dans le tampon, sinon il renvoie -1 en cas d'erreur.

  • int close (int sockfd)- La fonction de fermeture est utilisée pour fermer une communication entre le client et le serveur. Cet appel renvoie 0 en cas de succès, sinon il renvoie -1.

  • int shutdown (int sockfd, int how)- La fonction d'arrêt est utilisée pour fermer en douceur une communication entre le client et le serveur. Cette fonction donne plus de contrôle par rapport à la fonction de fermeture. Il renvoie 0 en cas de succès, -1 sinon.

  • int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) - Cette fonction est utilisée pour lire ou écrire plusieurs sockets.

Fonctions d'assistance de socket

  • int write (int fildes, const void *buf, int nbyte)- La fonction d'écriture tente d'écrire n octets depuis le tampon pointé par buf dans le fichier associé au descripteur de fichier ouvert, fildes. En cas de réussite, write () renvoie le nombre d'octets réellement écrits dans le fichier associé à fildes. Ce nombre n'est jamais supérieur à n octets. Sinon, -1 est renvoyé.

  • int read (int fildes, const void *buf, int nbyte)- La fonction de lecture tente de lire n octets du fichier associé au descripteur de fichier ouvert, fildes, dans le tampon pointé par buf. En cas de réussite, write () renvoie le nombre d'octets réellement écrits dans le fichier associé à fildes. Ce nombre n'est jamais supérieur à n octets. Sinon, -1 est renvoyé.

  • int fork (void)- La fonction fork crée un nouveau processus. Le nouveau processus, appelé processus enfant, sera une copie exacte du processus appelant (processus parent).

  • void bzero (void *s, int nbyte)- La fonction bzero place n octets nuls dans la chaîne s. Cette fonction sera utilisée pour définir toutes les structures de socket avec des valeurs nulles.

  • int bcmp (const void *s1, const void *s2, int nbyte)- La fonction bcmp compare la chaîne d'octets s1 à la chaîne d'octets s2. Les deux chaînes sont supposées avoir une longueur de n octets.

  • void bcopy (const void *s1, void *s2, int nbyte)- La fonction bcopy copie n octets de la chaîne s1 vers la chaîne s2. Les chaînes qui se chevauchent sont gérées correctement.

  • void *memset(void *s, int c, int nbyte) - La fonction memset est également utilisée pour définir les variables de structure de la même manière que bzero.