Minichat multi-client

Description

Voila, un mini-chat multithread ,multi client en console, Je poste cet première source, pas pour montrer quelques chose d'innovant mais plutôt pour que les plus expérimenter m'aide a améliorer ma façon de programmer car vous allez surement la trouver un peu barbare notamment l'utilisation de variable globale car je n'arrivai pas a passer des arguments a un thread.
Néanmoins je suis sur que cette source pourra en apprendre beaucoup au débutant sur l'utilisation des sockets car je l'est énormément commenter(un peu trop même,lol).
n'oubliez pas d'inclure la librairie winsock2 ainsi que pthread(la dll est dans le projet).

Source / Exemple :


///////////////////////////////////////////////SERVEUR///////////////////////////////////////////////
#include <stdio.h>

#include <windows.h>
#include <pthread.h>//utilisation des threads
#include <winsock2.h>//pour les sockets

#define PORT 2000 //le port sur lequel le serveur ecoute
#define MAX_CLIENT 3 // cette constante sert a definir le nombre de maximum de client qui sera autoriser par le serveur

struct Client//structure ou sera enregistre les information pour chaque client
{
    SOCKET SockClient;// son socket
    bool Connecte;//si ce client est deja utiliser ou pas (1 si deja utiliser,sinon 0)
    pthread_t ThreadReception; //son thread de reception( la fonction recv est une fonction "bloquante" donc utilisation de thread
    char Pseudo[50];//le pseudo du client
};

Client client[MAX_CLIENT];//tableau  de type Client,ou seront stoquer tout les clients

int IDNouveauThread=0;//variable temporaire qui me sert a envoyer une variable d'une fonction a une autre( car j'arrive pas a envoyer une variable a une fonction lancer en thread)
int ServeurPlein=0;//1:le serveur est plein,0 le serveur a encore des emplacement de libre

void *Reception(void *data);//fonction qui ecoute un client et renvoie les donnees recu au autre client
int EnvoieDonnee(SOCKET SockClient,char *Donnee);/*envoie donnee au client
                                                  renvoie 1 si tout c'est bien passer sinon revoie 0*/
int Ecoute();/*fonction qui met en ecoutesur le port selectionne plus haut(attente de client)
               revoie 1 si l'ecoute a ete lancer avec succes
               sinon revoie 0*/

SOCKET SockServeur;
 int Retour=0;
 SOCKADDR_IN Sin = {0};

int main(void)
{

    int I=0;

    if (Ecoute())//on lance l'ecoute sur le port(defini plus haut)
    {
        printf("Le Serveur ecoute sur le port %d\nEn attente de la connections des clients\n",PORT);//si 1, c ok
    }
    else
    {
        printf("Erreur lors de la mise en ecoute sur le port no %d\n",PORT);//0 erreur, on est obliger de sortir
        system("pause");
        return 0;
    }

    I=0;//variable qui va servir a parcourir le tableau de Client

    do
    {

        if (I>=MAX_CLIENT)//veut dire que l'on a atteint la limite maxi des clients
        {
            printf("Serveur Plein\n");
            ServeurPlein=1;//on indique que le Serveur est saturer
            shutdown(SockServeur, 1);//on eteint le socket de reception
            closesocket(SockServeur);//idem

            do
            {

                Sleep(1000);//sert a evite de charger le processeur
            }
            while (ServeurPlein);//temps que la variable ServeurPlein est a 1 cela veut dire que aucun emplacement a ete liberer
            //un emplacement a ete liberer
            if (Ecoute())//on relance l'ecoute
            {
                printf("Serveur en attente de client\n");
            }
            else
            {
                printf("Erreur lors de la mise en ecoute sur le port no %d\n",PORT);
                system("pause");
                return 0;
            }
            I=0;//on remet i a 0 pour la prochaine recherche
        }
        else
        {
            if (client[I].Connecte==false)//veut dire que cette emplacement de client n'est pas utiliser, donc OK
            {

                printf("Emplacement libre trouver:%d\n",I);
                Sleep(10);
                SOCKADDR_IN CSin = {0};
                int sizeofcsin = sizeof(CSin);

                client[I].SockClient = accept(SockServeur, (SOCKADDR *)&CSin, &sizeofcsin);//on accepte la connection

                if ( client[I].SockClient != INVALID_SOCKET )//si la connection a reussi
                {
                    client[I].Connecte=true;//on passe la variable a 1 pour indique que se client est dorenavant utilise
                    Retour = recv(client[I].SockClient, client[I].Pseudo, sizeof(client[I].Pseudo)-1, 0);//on recoit le pseudo
                    printf("Client no %d connecte sous le pseudo: %s\n\n",I,client[I].Pseudo);
                    IDNouveauThread=I;//je passe ma variable i dans une variable temporaire pour savoir sur quelle client je doit ecouter pour la fonction suivante
                    pthread_create(&client[I].ThreadReception, NULL, Reception, NULL);//on lance le thread ou va etre recu les donnes de se client
                    I=0;//on repasse i a 0 pour une prochaine recherche d'emplacement libre

                }
                else
                {
                    printf("Erreur\n");

                }

            }
            I++;//on poursuit la recherche de nouveau client
        }

    }
    while (1);

WSACleanup();
    system("pause");
    return 0;
}

void *Reception(void *Donnee)
{
    int RetourReception;
    int I=0;
    int IDThreadActuel=IDNouveauThread;
    char DonneeRecu[1024];//variable ou sera stocker les donnees recu
    char DonneeAEnvoyer[1024];
    int Connecte=true;//la conection au serveur est ok

    do
    {
        I=0;// variable qui va servir a parcourir les clients auquel on va envoyer les donnees recu d'un client
        RetourReception = recv(client[IDThreadActuel].SockClient, DonneeRecu, sizeof(DonneeRecu)-1, 0);//on recoit les donnees(fonction "bloquante"
        if (RetourReception != SOCKET_ERROR)//connection avec le client OK
        {
            DonneeRecu[RetourReception] = '\0';
            do
            {
                if (client[I].Connecte==true)//cela veut dire que se client est connecte donc on peut lui envoyer les donnees que l'on vient de recevoir
                {
                    if (I!=IDThreadActuel)//pour ne pas envoyer les donnees au client qui vient de les envoyer(ben oui on sait se que l'on vient de taper lol)
                    {

                        DonneeAEnvoyer[0]='\0';
                        strcat(DonneeAEnvoyer,client[IDThreadActuel].Pseudo);
                        strcat(DonneeAEnvoyer," dit: ");
                        strcat(DonneeAEnvoyer,DonneeRecu);
                        if (!EnvoieDonnee(client[I].SockClient,DonneeAEnvoyer))//si l'envoie de donnee a echouer
                        {
                            printf("%s deconnecte\n",client[I].Pseudo);
                            closesocket(client[I].SockClient);//on ferme son socket client
                            client[I].Connecte=false;//on indique que cet emplacement est desormais libre
                            ServeurPlein=0;//le serveur n'est plus plein si il l'etait(je sais j'aurais pu mettre un if pour verifier si il l'etait...)

                        }
                    }
                }

                I++;//on continue a parcourir les autres clients

            }
            while (I<MAX_CLIENT);//tant que l'on a pas atteind la limite de client
            I=0;//pour une prochaine recherche lors de la prochaine reception de donnee
        }
        else//connection avec le client NOK
        {
            printf("%s deconnecte\n",client[IDThreadActuel].Pseudo);
            shutdown(client[IDThreadActuel].SockClient, 2);//on ferme le socket de se thread
            closesocket(client[IDThreadActuel].SockClient);//idem
            client[IDThreadActuel].Connecte=false;//on indique que cet emplacement est desormais libre
            ServeurPlein=0;//voir plus haut

            Connecte=false;//on sort de la boucle pour terminer le thread puisque l'on a perdu la connection avec ce client
        }
    }
    while (Connecte);

    return &Donnee;
}

int EnvoieDonnee(SOCKET SockClient,char *Donnee)
{
    int RetourEnvoie;

    RetourEnvoie=send(SockClient, Donnee, strlen(Donnee), 0);//envoie les donnee (variable data)
    if (RetourEnvoie!=SOCKET_ERROR)
    {
        return 1;
    }

    return 0;
}

int Ecoute()
{
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2,0), &WSAData);//init winsock
    SOCKADDR_IN Sin = {0};//structure qui va contenir les informations  de notre socket serveur

    SockServeur = socket(AF_INET, SOCK_STREAM, 0);//creation du socket

    if ( SockServeur != INVALID_SOCKET )//si creation ok
    {

        Sin.sin_addr.s_addr    = htonl(INADDR_ANY);//on rempli la structure
        Sin.sin_family    = AF_INET;//idem
        Sin.sin_port    = htons(PORT);//idem

        Retour = bind(SockServeur, (SOCKADDR *)&Sin, sizeof(Sin));//on associe le socket a notre adresse local(enfin je crois,lol)
        if ( Retour != SOCKET_ERROR )//si ok
        {
            Retour = listen(SockServeur, 2);//on commence ll'ecoute (l'attente de client)
            if ( Retour != SOCKET_ERROR )//si tout c'est bien passer
            {

                return 1;

            }

        }
    }

    return 0;

}

/////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////CLIENT////////////////////////////////////////////////
#include <stdio.h>
#include <windows.h>
#include <winsock2.h>
#include <pthread.h>//utilisation des thread car fonction recv bloquante

#define TPSATTENTE 5

SOCKET Sock;
pthread_t ThreadReception;

void *Reception(void *Donnee);//fonction ou se fait la reception de donnee envoyer par le serveur

int main(void)
{
    char Ip[16];
    unsigned int Port=0;
    char Pseudo[50];
    int Erreur;
    int TempsDAttente=0;
    bool Connecte=false;

    printf("\t\t\t\tClient multiThread\n\n\n");
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2,0), &WSAData);//init winsock

    printf("Choisir un Pseudo:\n");// un pseudo
    Pseudo[0]='\0';
    fgets(Pseudo,sizeof Pseudo,stdin);
    Pseudo[strlen(Pseudo)-1]='\0';//pour retirer le retour chariot qui est enregistre dans le fgets
    printf("Adresse du serveur:\n"); //on recupere les differente infos, adresse
    Ip[0]='\0';
    fgets(Ip,sizeof Ip,stdin);
    Ip[strlen(Ip)-1]='\0';//pour retirer le retour chariot qui est enregistre dans le fgets
    printf("Port de connection:\n");  //port de connection
    scanf("%d",&Port);

    Sock = socket(AF_INET, SOCK_STREAM, 0);//on creer le socket
    if ( Sock != INVALID_SOCKET )//si creation ok
    {

        printf("\nSocket client no %d ouvert\n",Sock);

        SOCKADDR_IN Sin;
        Sin.sin_addr.s_addr    = inet_addr(Ip); //on rempli la structure sin qui va servir au socket
        Sin.sin_family    = AF_INET;
        Sin.sin_port    = htons(Port);
        printf("Connection a %s sur le port %d en cours...\n",Ip,Port);

        do
        {

            Erreur = connect(Sock, (SOCKADDR *)&Sin, sizeof Sin);//on essaye de se connecte au serveur
            if (Erreur != SOCKET_ERROR)
            {
                Connecte=true;
            }
            else
            {
                if (TempsDAttente<=TPSATTENTE)
                {
                    Sleep(1000);// on attend 1 seconde avant de reesayer (pour eviter de charge le processeur
                    TempsDAttente++;
                }
                else
                {
                    printf("Temps de connection trop long");
                    goto deconnection;

                }
            }
        }
        while (!Connecte);//tant que l'on arrive pas a se connecter

        printf("Connection au serveur effectue avec succes\n\n");

        char DonneeAEnvoyer[512];
        Erreur = send(Sock, Pseudo, strlen(Pseudo), 0);//on envoie le pseudo au serveur
        if (Erreur == SOCKET_ERROR)//si il y a erreur cela veut dire que l'on a perdu la connection avec le serveur donc on sort
        {
            goto deconnection;
        }
        pthread_create(&ThreadReception, NULL, Reception, NULL);//on lance le thread qui va recevoir les donnees du serveur
        do
        {

            DonneeAEnvoyer[0]='\0';//pour etre sur de ne pas envoyer autre chose que se qui a ete saisie
            fgets(DonneeAEnvoyer, sizeof DonneeAEnvoyer, stdin);//on recupere les saisies du clavier
            Erreur = send(Sock, DonneeAEnvoyer, strlen(DonneeAEnvoyer), 0);//on les envoie au serveur
            if (Erreur == SOCKET_ERROR)
            {
                Connecte=false;//si erreur, on sort de la boucle
            }

        }
        while (Connecte);
deconnection:

        printf("Connection avec le serveur interrompu\n");
        closesocket(Sock);//on ferme le socket

    }
    else
    {
        printf("Socket invalide\n");
        return EXIT_FAILURE;
    }

    WSACleanup();//on nettoie le wsa.

    system("pause");
    return EXIT_SUCCESS;
}

void *Reception(void *Donnee)
{
    int Erreur;
    bool Connecte=true;
    char DonneeRecu[50];
    do
    {
        Erreur = recv(Sock, DonneeRecu, sizeof(DonneeRecu)-1, 0);//on recoit les donnees, fonction bloquante d'ou le thread
        if (Erreur == SOCKET_ERROR)
        {
            Connecte=false;//voir plus haut
        }
        else
        {
            DonneeRecu[Erreur] = '\0';

            printf(">>>> %s",DonneeRecu);
        }
    }
    while (Connecte);

    printf("perte de la connection avec le serveur\n");
    closesocket(Sock);//on ferme le socket
    return &Donnee;

}

//////////////////////////////////////////////////////////////////////////////////////////////////////

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.