Laboratoire 11 - Sockets
Sommaire
Remarque: n’oubliez pas, lors de l’utilisation d’appels systèmes, de traiter les cas d’erreur.
Echo
- Le programme
srv_echo.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<signal.h>
void gere(int sock) {
fprintf(stderr, "** Terminaison du serveur et libération des ressources **\n");
close(sock);
unlink("sock");
exit(EXIT_SUCCESS);
}
int main(int argc, char** argv){
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = gere;
sigaction(SIGINT, &action, NULL);
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "sock", sizeof(addr.sun_path)-1);
int res = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (res == -1) {
perror("bind");
exit(1);
}
listen(sock, SOMAXCONN);
//listen(sock, 0);
char buf[100];
int cli, resRead, resWrite, compteur=0;
while(1){
fprintf(stderr, "**En écoute..**\n");
cli = accept(sock, NULL, NULL);
if(cli == -1){
perror("accept");
exit(1);
}
fprintf(stderr, "**Client n°%d: Début session**\n",++compteur);
while((resRead = read(cli, buf, sizeof(buf))) ){
if(resRead == -1){
perror("read cli");
break;
}
resWrite = write(cli, buf, resRead);
if(resWrite==-1){
perror("write cli");
break;
}
}
close(cli);
fprintf(stderr, "**Client n°%d: Fin session**\n",compteur);
}
}
- Le Programme
cli.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<signal.h>
void gere(int sig) {
fprintf(stderr, "Connexion perdue\n");
exit(1);
}
int main(int argc, char **argv)
{
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = gere;
sigaction(SIGPIPE, &action, NULL);
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "sock", sizeof(addr.sun_path)-1);
fprintf(stderr, "**Connexion en cours**\n");
int res = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
if(res==-1) {
perror("connect");
exit(1);
}
fprintf(stderr, "**Connecté**\n");
int resWrite, resRead;
char buf[100];
while(fgets(buf, sizeof(buf)-1, stdin)!=NULL){
fprintf(stderr, "**Envoi en cours**\n");
resWrite=write(sock, buf, strlen(buf));
if(resWrite==-1){
perror("write cli");
exit(1);
}
fprintf(stderr, "**Message envoyé**\n");
fprintf(stderr, "**En attente du serveur...**\n");
resRead = read(sock, buf, sizeof(buf)-1 );
if(resRead == -1){
perror("read cli");
return 1;
}
buf[resRead]='\0';
printf("Serveur: %s",buf);
}
close(sock);
fprintf(stderr, "**Connexion teminée**\n");
return 0;
}
-
La valeur du
backlog
delisten(2)
mise à0
- En mettant backlog à 0, au maximum 2 clients arrivent à se connecter: un client en cours de traitement et l’autre attend dans la file d’attente du serveur, les autres clients vont essayer de se connecter(le code(côté client) bloque à l’appel système
connect
) jusqu’à ce qu’une place se libère. NB: lorsqu’un client est accepté il ne compte plus dans le backlog - En écrivant une ligne dans le client n°2, le serveur ne répond pas car il est en train de traiter avec le client n°1, lorsqu’on termine le client n°1 avec Ctrl-d, le serveur commence à servir le client n°2 et le client n°3 se connecte au serveur.
- En mettant backlog à 0, au maximum 2 clients arrivent à se connecter: un client en cours de traitement et l’autre attend dans la file d’attente du serveur, les autres clients vont essayer de se connecter(le code(côté client) bloque à l’appel système
-
La valeur du
backlog
delisten(2)
mise àSOMAXCONN
- En mettant backlog au max, plusieurs clients arrivent à se connecter et attendent que le client 1 soit servi.
Poll (multiplexage)
- Le programme
srv_poll.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<poll.h>
#include<signal.h>
void gere(int sock) {
fprintf(stderr, "** Terminaison du serveur et libération des ressources **\n");
close(sock);
unlink("sock");
exit(EXIT_SUCCESS);
}
int main(int argc, char** argv){
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = gere;
sigaction(SIGINT, &action, NULL);
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "sock", sizeof(addr.sun_path)-1);
int res = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (res == -1) {
perror("bind");
exit(1);
}
//listen(sock, 10);
listen(sock, 0);
//PRÉPARATION DU POLL
struct pollfd *fds=NULL;
nfds_t nfds=1;
int resPoll;
fds = malloc(sizeof(struct pollfd));
if(fds==NULL){
perror("malloc fds");
exit(1);
}
fds[0].fd = sock;
fds[0].events = POLLIN;
char buf[100];
int resRead, resWrite, ligne_log=0, nbr_clients=0, nbre_client_max=10, cli;
while(1){
fprintf(stderr, "%d **En écoute..**\n", ++ligne_log);
resPoll = poll(fds, nfds, -1);
if(resPoll==-1){
perror("poll");
exit(1);
}
for(int i=0; i<nfds ;i++){
if(i==0 && fds[i].revents != 0 && fds[i].revents == POLLIN ){
if(nbr_clients<nbre_client_max){
fprintf(stderr, "%d **Un nouveau client vient de se connecter**\n", ++ligne_log);
nfds++;
fds = realloc(fds, sizeof(struct pollfd)*nfds);
if(fds==NULL){
perror("realloc fds");
exit(1);
}
fds[nfds-1].fd = accept(sock, NULL, NULL);
if(fds[nfds-1].fd == -1){
perror("accept");
exit(1);
}
fds[nfds-1].events = POLLIN | POLLHUP;
nbr_clients++;
}else{
cli = accept(sock, NULL, NULL);
if(cli == -1){
perror("accept");
exit(1);
}
close(cli);
fprintf(stderr, "%d **Un client vient d'être refusé(max clients=%d)**\n", ++ligne_log, nbre_client_max);
}
}else if(fds[i].revents != 0){
if((fds[i].revents & POLLHUP) != 0 ){
fprintf(stderr, "%d **Un client s'est deconnecté**\n", ++ligne_log);
close(fds[i].fd);
fds[i].fd = -1;//pour que poll ignore cette case
nbr_clients--;
}else if(fds[i].revents == POLLIN){
fprintf(stderr, "%d **Le serveur répond**\n", ++ligne_log);
resRead = read(fds[i].fd, buf, sizeof(buf));
if(resRead == -1){
perror("read");
return 1;
}
resWrite = write(fds[i].fd, buf, resRead);
if(resWrite == -1){
perror("write");
exit(1);
}
}
}
}
}
}
Chat
- Le programme
srv_chat.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<poll.h>
#include<signal.h>
void gere(int sock) {
fprintf(stderr, "** Terminaison du serveur et libération des ressources **\n");
close(sock);
unlink("sock");
exit(EXIT_SUCCESS);
}
int main(int argc, char** argv){
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = gere;
sigaction(SIGINT, &action, NULL);
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "sock", sizeof(addr.sun_path)-1);
int res = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (res == -1) {
perror("bind");
exit(1);
}
//listen(sock, 10);
listen(sock, 0);
//PRÉPARATION DU POLL
struct pollfd *fds=NULL;
nfds_t nfds=1;
int resPoll;
fds = malloc(sizeof(struct pollfd));
if(fds==NULL){
perror("malloc fds");
exit(1);
}
fds[0].fd = sock;
fds[0].events = POLLIN;
char buf[100], buf_copie[120];
int resRead, resWrite, ligne_log=0, nbr_clients=0, nbre_client_max=10, cli;
while(1){
fprintf(stderr, "%d **En écoute..**\n", ++ligne_log);
resPoll = poll(fds, nfds, -1);
if(resPoll==-1){
perror("poll");
exit(1);
}
for(int i=0; i<nfds ;i++){
if(i==0 && fds[i].revents != 0 && fds[i].revents == POLLIN ){
if(nbr_clients<nbre_client_max){
fprintf(stderr, "%d **Un nouveau client vient de se connecter**\n", ++ligne_log);
nfds++;
fds = realloc(fds, sizeof(struct pollfd)*nfds);
if(fds==NULL){
perror("realloc fds");
exit(1);
}
fds[nfds-1].fd = accept(sock, NULL, NULL);
if(fds[nfds-1].fd == -1){
perror("accept");
exit(1);
}
fds[nfds-1].events = POLLIN | POLLHUP;
nbr_clients++;
}else{
cli = accept(sock, NULL, NULL);
if(cli == -1){
perror("accept");
exit(1);
}
close(cli);
fprintf(stderr, "%d **Un client vient d'être refusé(max clients=%d)**\n", ++ligne_log, nbre_client_max);
}
}else if(fds[i].revents != 0){
if((fds[i].revents & POLLHUP) !=0) {
fprintf(stderr, "%d **Un client s'est deconnecté**\n", ++ligne_log);
close(fds[i].fd);
fds[i].fd = -1;//pour que poll ignore cette case
nbr_clients--;
}else if(fds[i].revents == POLLIN){
fprintf(stderr, "%d **Le serveur répond**\n", ++ligne_log);
resRead = read(fds[i].fd, buf, sizeof(buf)-1);
if(resRead == -1){
perror("read");
return 1;
}
for(int j=1; j<nfds ;j++){
if(j!=i && fds[j].fd!=-1){
buf[resRead] = '\0';
snprintf(buf_copie, sizeof(buf_copie)-1, "Client %d: %s", i+1,buf);
resWrite = write(fds[j].fd, buf_copie, resRead+10);
if(resWrite == -1){
perror("write");
exit(1);
}
}
}
}
}
}
}
}
- Le programme
cli.c
ne permet pas une communication synchronisée, vue qu’il suit le shéma suivant en boucle lecture->envoie au serveur->attente d’une réponse. /cli < /usr/share/dict/words
remplace lestdin
par le fichier/usr/share/dict/words
et commence à lire une ligne après l’autre et l’envoyer au serveur, qui ensuite va l’envoyer aux autres clients.strings /dev/urandom | ./cli
crée un tube et on va lire une ligne après l’autre à partir du tube, et à chaque fois on va envoyer au serveur qui va ensuite envoyer aux autres clients.