Thread: Programming chat client, need some help (sockets & threads)

  1. #1
    Registered User
    Join Date
    Apr 2008
    Posts
    1

    Programming chat client, need some help (sockets & threads)

    Ok so here's the thing, I need to create a program that simulates a chat client, there's a server to which any number of clients can connect to and send messages from one client to another.

    Both the client and the server are pretty much done, one of the clients sends a message to another through the server and the message gets to the destinated client, BUT only after I've inputed an action, like requesting some type of information from the server (like asking for a list of the clients currently connected to the server).

    I tried creating a pthread to execute the readerMSG method so the client is constantly reading from the server, the problem is the read method kinda gets stuck, and so does the rest of the program, threaded or not.

    Any ideas on what I can do so I can keep the client constantly "listening" for anything from the server?

    This is the code for the client, I don't think the problem is with the server, so I'll only post if necessary.

    Code:
    #include "data.h"
    
    // Declaracion de funciones que se van utilizar
    void error(string);
    bool login(int,char *);
    void execute(int,string);
    void sendMessage(int,char**);
    void readMessage(int);
    void *readerMSG(void*);
    void *interfaceCliente(void*);
    int main(int argc, char *argv[]){
        //string command;
        // Datos de la coneccion
        int socketC; // El socket del cliente
        struct sockaddr_in serverChat; // Direccion al server
        char bufferC[BUFFER_SIZE];
        
        // Este thread se encarga de leer los mensajes
        pthread_t reader;
        // Se verifica que si los numeros de parametros estan correctos
        if(argc != 5){
            fprintf(stderr,"USAGE: cliente <username> <cliente's port> <server ip> <server's port>\n");
            exit(1);
        }
        
        // Se crea el socket a utilizar y se verifica si se pudo 
        socketC = socket(PF_INET, SOCK_STREAM, 0);
        if(socketC < 0){
            error("Failed to create socket");
        }
        // Se realiza la coneccion a servidor
        // se crea el ente que va manejar la direccion del servidor
        memset(&serverChat,0,sizeof(serverChat));
        serverChat.sin_family = AF_INET;        // Usa dominio internet
        serverChat.sin_addr.s_addr = inet_addr(argv[3]);    //Direccion ip del server
        serverChat.sin_port = htons(atoi(argv[4]));         // Puerto del server
        // Se realiza la coneccion
        if(connect(socketC,(struct sockaddr *)&serverChat,sizeof(serverChat)) < 0){
            error("ERROR: failed to connect with server");
        }
        if(!login(socketC,argv[1])){
            error("ERROR: failed to log to server");
        }
        //pthread_create(&reader,NULL,readerMSG,(void*)socketC);
        //readMessage(socketC);
        interfaceCliente((void*)socketC);
        return 0;
    }
    void *interfaceCliente(void *socketFV){
        string command;
        int socketC = (int)socketFV;
        while(1){
            //readMessage(socketC);
            cout<<"HLCHAT: ";
            getline(cin,command);
            execute(socketC,command); 
        }
    }
    
    void *readerMSG(void *socketFV){
        readMessage((int)socketFV);
    }
    void error(string msg){
        perror(msg.c_str());
        exit(1);
    }
    
    bool login(int socketF,char *username){
        // Donde el servidor va constestar a usuario
        char wbuffer[BUFFER_SIZE];
        bzero(wbuffer,BUFFER_SIZE);
        string message = LOGIN_WORD;
        // Se crea el mensaje que se enviara al servidor
        message = message + " " + username;
        // Se escribe al servidor
        if(write(socketF,getArray(message),message.length())<0){
            error("ERROR: failed to write to socket");
        }
        
        // Se obtiene la respuesta de servidor
        if(read(socketF,wbuffer,BUFFER_SIZE) <0){
            error("ERROR: failed to read from socket");
        }
        // Se analiza la respuesta del servidor
        char** response = theLexer(wbuffer);
        if(strcmp(response[0],ACCEPT_WORD) == 0){
            cout<<"You have loged successful"<<endl;
            return true;
        }
        else{
            cout<<"You have not loged successful"<<endl<<wbuffer<<endl;
            return false;
        }
    }
    
    void execute(int sockectF, string command){
        string message;
        char cBuffer[BUFFER_SIZE];
        bzero(cBuffer,BUFFER_SIZE);
        // Se escanea el comando
        char** tokens = theLexer(getArray(command));
        if(strcmp(tokens[0],LIST_WORD) == 0){
            message = LIST_WORD;
            if(write(sockectF,getArray(message),message.length()) < 0)
                error("ERROR: Failed to write to socket");
            if(read(sockectF,cBuffer,BUFFER_SIZE)< 0)
                error("ERROR: Failed to read from socket");
            cout<<cBuffer<<endl;
        }
        // se envia un mensaje
        else{
            sendMessage(sockectF,tokens);
        }
        
    }
    
    void readMessage(int socketF){
        char** tokens2;
        char cBuffer[BUFFER_SIZE];
        bzero(cBuffer,BUFFER_SIZE);
        int cont;
        //while(1){
            bzero(cBuffer,BUFFER_SIZE);
            if(read(socketF,cBuffer,BUFFER_SIZE) <0){
                error("ERROR: failed to read from socket");
            }
            tokens2 = theLexer(cBuffer);
            if(strcmp(tokens2[0],GET_MESSAGE_WORD)){
                cont = 0;
                while(tokens2[cont] != NULL){
                    if(cont == 0);
                    else if(cont == 3)
                        cout<<"\b";
                     else   
                        cout<<tokens2[cont]<<" ";
                }
                cout<<"\b\b";
            }
        //}
    }
    /* Se encarga de enviar un mensaje al servidor */
    void sendMessage(int sockectF,char** tokens){
        // Mensaje que recibira del servidor    
        char cBuffer[BUFFER_SIZE];
        bzero(cBuffer,BUFFER_SIZE);
        // se crea el mensaje que enviara al servidor
        string message = MESSAGE_WORD;
        message += " ";
        message += tokens[0]; 
        message += " $";
        int cont = 1;
        while(tokens[cont] != NULL){
            message += tokens[cont]; 
            message += " ";
            cont++;
        }
        message += "\b$";
        if(write(sockectF,getArray(message),message.length()) < 0)
            error("ERROR: Failed to write to socket");
        if(read(sockectF,cBuffer,BUFFER_SIZE)< 0)
                error("ERROR: Failed to read from socket");
        cout<<cBuffer<<endl;
    }

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    > bzero(cBuffer,BUFFER_SIZE);
    > if(read(socketF,cBuffer,BUFFER_SIZE) <0)
    1. bzero is a non-standard function.
    2. It's a waste of time calling bzero (or memset), if the rest of the code is correct (which it isn't)
    3. ALL (and I mean ALL) reads and writes to a SOCK_STREAM can result in fragmented data. Both sending and receiving can result in fragmented messages which YOU have to deal with.
    4. If read does actually fill the buffer, then there will be no \0 in there at all to make it a proper C-string anyway.

    Example
    send("hello world") might result in "hello world" being sent, or it may result in just "hell" being sent. The return result will tell you how many (if any) characters were sent, and it's up to you to try again with the remainder.
    Similarly, having sent "hello world" in a single call, there's no reason to assume that the corresponding read at the other end will read the whole thing at once. It may start by just getting "hello wo". Again, it's up to you to call read again to get the remainder of the data and reassemble the original meaning (say by looking for a delimiter).

    > char** response = theLexer(wbuffer); // char wbuffer[BUFFER_SIZE];
    > char** tokens = theLexer(getArray(command)); // std::string command
    Now what does theLexer() actually do?
    The ideal scenario would have it allocate lots of memory, and copy tokens into the allocated memory, and make sure that the input string was unmodified. It certainly needs to be so if one were to assume a naive implementation of say getArray.
    If it isn't trashing memory that it doesn't own, then I smell a lot of memory leaks.

    Everything in this program should be using std::string (it has methods for tokenising, use them). Only when you're at the stage of actually calling write to the socket do you demote to a char array. Similarly on read, you promote to std::string as soon as possible.
    Consider a wrapper around read/write which take/return std::string instead of char array.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Simple Client - Server problems (Async Sockets )
    By Cpp_Noob in forum Networking/Device Communication
    Replies: 4
    Last Post: 06-19-2009, 07:12 AM
  2. Concurrent chat, Sockets
    By jakemott in forum Linux Programming
    Replies: 6
    Last Post: 11-29-2008, 05:41 PM
  3. Unix sockets
    By karas in forum Linux Programming
    Replies: 8
    Last Post: 10-13-2007, 12:20 AM
  4. [newb] how to use threads with sockets ?
    By jabka in forum C Programming
    Replies: 1
    Last Post: 08-07-2007, 11:51 AM
  5. Requesting Java Chat Client Maintenence
    By ygfperson in forum A Brief History of Cprogramming.com
    Replies: 6
    Last Post: 04-02-2003, 01:57 PM