-------------------------------------------------------- BFZ#1/ Programmation reseau agonn -------------------------------------------------------- I - Introduction ================ Ce document est basé sur la traduction de "BASIC C Socket Programming In Unix For Newbie by Bracaman". Je donc vais vous apprendre a creer des applications reseau pour UNIX. Avant toute chose, je tiens a dire que ce paper pose juste les BASES de la programmation reseau, il serait utopique de vouloir tout expliquer a ce sujet. Nous allons donc voir ensemble les différentes fonctions et structures utiles a la programmation de sockets et nous allons construire un serveur et un client très basique. Let's go! II - Les Structures =================== En programmation de socket, les structures servent a stocker les informations a propos des adresses. Notre premiere structure est la struct sockaddr qui stocke les informations sur le socket: struct sockaddr{ unsigned short sa_family; /* address family */ char sa_data[14]; /* 14 bytes of protocol address */ }; Mais il y a une autre structure qui peut vous aider a referencer les elements du socket: struct sockaddr_in { short int sin_family; /* Address family */ unsigned short int sin_port; /* Port */ struct in_addr sin_addr; /* Internet Address */ unsigned char sin_zero[8]; /* Same size as struct sockaddr */ }; La structure hosetnt sert a obtenir des informatuions a propos de l'host, elle se définit comme suit: struct hostent { char *h_name; /* Official name of host. */ char **h_aliases; /* Alias list. */ int h_addrtype; /* Host address type. */ int h_length; /* Length of address. */ char **h_addr_list; /* List of addresses from name server. */ #define h_addr h_addr_list[0] /* Address, for backward compatibility. */ }; Ca peut paraitre compliqué a certains mais au fur et a mesure vous apprendrez a vous servir et a comprendre ces structures. III - Adresses IP ================== Je vais parler ici de différentes fonctions servant a manipuler les adresse IP. inet_addr() qui convertit une adresse IP en un unsigned long: dest.sin_addr.s_addr = inet_addr("127.0.0.1"); inet_ntoa() convertit un string d'adresse IP en long: char *IP; ip = inet_ntoa(dest.sin_addr); printf("l'adresse est: %s\n", ip); IV - Les Fonctions ================== On passe au plus interessant.. Je vais ici vous présenter les différentes fonctions utiles pour programmer des sockets. Je mets egalement la syntaxe avec les header pour vous faciliter la tache. Pour voir ces fonctions en action, vous pouvez passer aux codes sources du client et du serveur... 1 - socket() ============ #include #include int socket(int domain,int type,int protocol); La fonction socket() sert a.. définir un socket. Elle retourne -1 en cas d'erreur Voila, passons aux arguments: domain = AF_INET pour utiliser le protocole internet, on ne s'occupera pas des autres types car ils ne sont pratiquement jamais utilisés.. type = SOCK_STREAM pour utiliser les stream sockets (qui utilisent le protocole TCP) ou SOCK_DGRAM pour les datagram sockets (qui utilisent UDP) protocol = On le le met a 0 2 - bind() ========== #include #include int bind(int fd, struct sockaddr *my_addr,int addrlen); La fonction bind() est utilisée pour associer un socket a un port. Elle retourne -1 en cas d'erreur. Arguments: fd = Le descripteur de socket retourné par socket() my_addr = un pointeur pour la structure sockaddr addrlen = sizeof(struct sockaddr) 3 - connect() ============= #include #include int connect(int fd, struct sockaddr *serv_addr, int addrlen); La fonction socket() est utilisée pour connecter une adresse IP a un port défini. Elle retourne -1 en cas d'erreur. Arguments: fd = Le desrcipteur de socket retourné par socket() serv_addr = Un pointeur pour la structure sockaddr qui contient l'adresse IP de destination et le port addrlen = sizeof(struct sockaddr) 4 - listen() ============ #include #include int listen(int fd,int backlog); listen() est utilisée pour attendre une connection. Avant cela, vous devez appeler la fonction bind() pour définir le port a écouter et après avoir appelé listen(), vous devez appeler accept() pour accepter les connections entrantes. listen() retourne -1 en cas d'erreur Arguments: fd = Le descripteur de socket retourné par socket() backlog = Le nombre de connections autorisées 5 - accept() ============ #include int accept(int fd, void *addr, int *addrlen); Utilisé pour accepter une connection (comme par hasard..). Retourne -1 en cas d'erreur. Arguments: fd = Le descripteur de socket retourné par l'appel a listen() addr = Un pointeur pour la struct sockaddr_in où vous pouvez déterminer quel host vous appelle depuis quel port. addrlen = La taille de la struct sockaddr_in 6 - send() ========== int send(int fd,const void *msg,int len,int flags); Transmet des données. Retourne -1 en cas d'erreur. Arguments: fd = Le descripteur de socket où vous voulez envoyer des données msg = Pointeur vers les données que vous voulez envoyer len = la taille des données que vous voulez transmettre (en bits) flags = 0 7 - recv() ========== int recv(int fd, void *buf, int len, unsigned int flags); Recoit les données. Retourne -1 en cas d'erreur. Arguments: fd = le descripteur de socket dans le lequel lire les données buf = le buffer où lire les données len = la taille de données maximum du buffer flags = 0 8 - sendto() ============ int sendto(int fd,const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen); Même fonction que send() mais avec 2 arguments en plus. Sert seulement pour les datagram sockets non connectés. Retourne -1 en cas d'erreur. Arguments: fd, msg, len ,flags = Pareil que send() to = Un pointeur pour la struct sockaddr tolen = sizeof(struct sockaddr) 9 - recvfrom() ============== int recvfrom(int fd,void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen); La même chose que recv() pour les datagram sockets non connectés. Retourne -1 en cas d'erreur. Arguments: fd, buf, len, flags = Pareil que recv() from = Pointeur pour la struct sockaddr dromlen = sizeof(struct sockaddr) 10 - close() ============ close(fd) Ferme la connection. 11 - gethostname() ================== #include int gethostname(char *hostname, size_t size); gethostname() sert a trouver le nom d'une machine locale. Arguments: hostname = Un pointeur vers un array qui contient hostname size = La taille de l'array hostname (en bytes) V - Codes Sources ================= Quelques exemples de ce qu'on peut faire avec les sockets :) <----------- CUT HERE --------------------------------------------> */ serveur.c $gcc serveur.c -o serveur Code by Bracaman */ #include #include #include #include #define PORT 3455 /* Le port que nous allons utiliser */ #define BACKLOG 2 /* L enombre de connections autorisées */ main() { int fd, fd2; /* descripteurs de socket */ struct sockaddr_in server; /* informations sur le serveur */ struct sockaddr_in client; /* informations sur le client */ int sin_size; if ((fd=socket(AF_INET, SOCK_STREAM, 0)) == -1 ){ /* Si socket() est egal -1 : erreur */ printf("socket() error\n"); exit(-1); } /* Si pas d'erreur notre socket est créé */ server.sin_family = AF_INET; server.sin_port = htons(PORT); /* Convertit notre port */ server.sin_addr.s_addr = INADDR_ANY; /* INADDR_ANY définit votre adresse IP */ bzero(&(server.sin_zero),8); /* On met a zero le reste de la structure */ if(bind(fd,(struct sockaddr*)&server,sizeof(struct sockaddr))==-1){ /* On appelle bind() */ printf("bind() error\n"); exit(-1); } if(listen(fd,BACKLOG) == -1){ /* On appelle listen() */ printf("listen() error\n"); exit(-1); } while(1){ sin_size=sizeof(struct sockaddr_in); if ((fd2 = accept(fd,(struct sockaddr *)&client,&sin_size))==-1){ /* On appelle accept() */ printf("accept() error\n"); exit(-1); } printf("You got a connection from %s\n",inet_ntoa(client.sin_addr) ); /* On nous donne l'IP du client */ send(fd2,"Welcome to my server.\n",22,0); /* On lui envoie un message de bienvenue */ close(fd2); /* close fd2 */ } } <----------- CUT HERE ---------------------------------------------------> <----------- CUT HERE ---------------------------------------------------> */ client.c $gcc client.c -o client Code by Bracaman */ #include #include #include #include #include /* Pour struct hostent */ #define PORT 3550 /* Port */ #define MAXDATASIZE 100 /* Nombre maximum de bits de données */ int main(int argc, char *argv[]) { int fd, numbytes; /* descripteur de socket */ char buf[MAXDATASIZE]; /* Le buffer qui va stocker le texte recu */ struct hostent *he; /* Structure qui prend les informations sur l'host */ struct sockaddr_in server; /* Informations sur l'adresse du serveur */ if (argc !=2) { /* Notre programme a besoin de 2 arguments */ printf("Usage: %s \n",argv[0]); exit(-1); } if ((he=gethostbyname(argv[1]))==NULL){ /* appel a gethostbyname() */ printf("gethostbyname() error\n"); exit(-1); } if ((fd=socket(AF_INET, SOCK_STREAM, 0))==-1){ /* creation du socket */ printf("socket() error\n"); exit(-1); } server.sin_family = AF_INET; server.sin_port = htons(PORT); /* Conversion */ server.sin_addr = *((struct in_addr *)he->h_addr); /*he->h_addr passe les infos sur he a "h_addr" */ bzero(&(server.sin_zero),8); if(connect(fd, (struct sockaddr *)&server,sizeof(struct sockaddr))==-1){ /* on appelle connect() */ printf("connect() error\n"); exit(-1); } if ((numbytes=recv(fd,buf,MAXDATASIZE,0)) == -1){ /* on appelle recv() */ printf("recv() error\n"); exit(-1); } buf[numbytes]='\0'; printf("Server Message: %s\n",buf); /* On recoit le message du serveur */ close(fd); /* Ferme la connection */ } <------------ CUT HERE --------------------------------------------------------------> VI - Outro ========== Voilà, j'espère que vous en saurez un peu plus sur la programmation reseau (même si ce n'est pas super complet..). Si vous voulez d'autres codes sources, j'en ai mit un dans le dossier /src, c'est en fait le code d'une backdoor TCP qui fait office de serveur :) Je rappelle que cet article est basé sur "BASIC C Socket Programming In Unix For Newbies by Bracaman", vous pouvez lui écrire a BracaMan@clix.pt. Enjoy ! agonn < root@brainfaktor.org >