Programming chat client, need some help (sockets & threads) [Archive] - C Board

PDA

View Full Version : Programming chat client, need some help (sockets & threads)


lalilulelo17
04-18-2008, 06:07 PM
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.


#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;
}

Salem
04-19-2008, 04:01 AM
> 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.