Chat
Chat em C, com uma abordagem ao estilo "routing" em que todo o trabalho fica do lado do servidor, que faz o encaminhamento das mensagens.
Servidor
/*servidor*/
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h> //biblioteca pra uso de threads
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<netdb.h>
#define MAX_USERS 100
#define MAX_THREADS 500
#define MAX_BUF 200 /*Limite maximo de dados transaccionaveis em bytes. todos os restantes sao apagados para evitar um possivel overlow*/
#define PROMPT "P@P Chat -> "
#define FULL 1
#define EMPTY 0
#define SUCCESS 1
#define FAIL 0
#define TRUE 1
#define FALSE 0
/*Variaveis Globais*/
// Tabela de utilizadores para efectuar o routing entre mensagens e clientes
struct RoutingTable /* -------------------------------------------------*/
{ /* | UID | slot | Nick | Cliente_Socket_ID |*/
int UID[MAX_USERS]; /* -------------------------------------------------*/
int slot[MAX_USERS]; /* | 20 | FULL | apocsantos| 365 |*/
char Nickname[MAX_USERS][MAX_BUF]; /* -------------------------------------------------*/
int ClientSocketID[MAX_USERS]; /* | 15 | EMPTY | SarahPeky | 765 |*/
}route; /* -------------------------------------------------*/
int GlobalUID = 0; // id de Utilizador
int usercount = 0; // numero de utilizadores
int ChatServerSocketID; // id do Socket do Servidor
/* Funcoes*/
/*1) Parse the buffer recieved from client*/
int extract(char * str_rcv,char * msg)
{
int size_count = 0; /*medida preventiva contra buffer overflow*/
int count = 0;
char Nickname[MAX_BUF];
str_rcv += 5; size_count=5; /*salta o "send" e o espaco*/
//salta espacos
while((*str_rcv==' ') && (size_count < MAX_BUF))
{
str_rcv++;
size_count++;
}
// grava o nick em memoria
while((*str_rcv!=' ')&& (size_count < MAX_BUF))
{
Nickname[count] = *str_rcv;
count++;size_count++; str_rcv++;
}
Nickname[count]='\0';
// novamente salta os espacos
while((*str_rcv==' ') && size_count < MAX_BUF)
{
str_rcv++;
size_count++;
}
// grava a mensagem em memoria
while((*str_rcv!='\0')&& (size_count < MAX_BUF))
{
*msg = *str_rcv; msg++; str_rcv++;
size_count++;
}
/*Retira o ClientID*/
for(count=0;count<MAX_USERS;count++)
{
if((!strcmp(route.Nickname[count],Nickname)) && (route.slot[count]==FULL))
return(route.ClientSocketID[count]);
}
return(FAIL);
}
/*2) adiciona um user*/
int add_user(char * str_rcv,int ClientSocketID)
{
int count=0;
while(count<MAX_USERS)
{
if(route.slot[count]==EMPTY)
{
usercount++; GlobalUID++;
route.UID[count]=GlobalUID;
strcpy(route.Nickname[count],str_rcv);
route.slot[count]=FULL;
route.ClientSocketID[count] = ClientSocketID;
return(GlobalUID);
}
count++;
}
return(FAIL);
}
/*3) Remove um user*/
int remove_user(int uid)
{
int count=0;
while(count<MAX_USERS)
{
if(route.UID[count]==uid)
{
route.slot[count]=EMPTY;
usercount--; // nota: Não é decrementado o GlobalID para evitar duplicacao de dados
return(SUCCESS);
}
count++;
}
return(FAIL);
}
/*4) Verifica se o nick não está em uso*/
int isunique(char * str_rcv)
{
int count=0;
for(count=0;count<MAX_USERS;count++)
{
if((!strcmp(str_rcv,route.Nickname[count])) && (route.slot[count]==FULL))
return(FAIL);
}
return(SUCCESS);
}
void *ChatThread();
/*funcao Main()*/
int main(int argc, char**argv)
{
struct sockaddr_in ChatServerAddress;
pthread_t ChatServerThreadID[MAX_THREADS];
int count=0;
int server_running = TRUE;
char ServerIp[50];
char ServerPort[50];
/*Limpa a tabela de routing entre clientes*/
for(count=0;count<MAX_USERS;count++)
route.slot[count] = EMPTY;
memset((void*)&ChatServerAddress,'\0',sizeof(ChatServerAddress));
/*Obtem o ip e a Porta*/
printf("Ip do Servidor:");gets(ServerIp);
printf("Porta:");gets(ServerPort);
// passa os parametros para a estrutura de sockets
ChatServerAddress.sin_family = AF_INET;
ChatServerAddress.sin_port = htons(atoi(ServerPort));
inet_aton(ServerIp,(&ChatServerAddress.sin_addr));
/*cria um socket servidor*/
ChatServerSocketID = socket(AF_INET,SOCK_STREAM,0);
/*dá um nome ao socket atravez do bind*/
if(bind(ChatServerSocketID,(struct sockaddr *)&ChatServerAddress,sizeof(ChatServerAddress))<0)
{
puts("Erro:Configuração de rede errada.");
exit(1);
}
/*cria uma lista de escuta (queue) com o tamanho MAX_THREADS*/
listen(ChatServerSocketID,MAX_THREADS);
/*cria as threads de chat*/
for(count=0;count<MAX_THREADS;count++)
pthread_create(&ChatServerThreadID[count],NULL,ChatThread,NULL);
printf("Servidor levantado e a funcionar em - [%s:%s]\n",ServerIp,ServerPort);
/*Fecha threads do chat*/
for(count=0;count<MAX_THREADS;count++)
{
pthread_join(ChatServerThreadID[count],NULL);
}
printf("Servidor a desligar...[OK]");
close(ChatServerSocketID);
}
/*Esta funcao faz o grosso do trabalho para permitir clientes ainda mais pequenos*/
void *ChatThread()
{
char str_rcv[MAX_BUF],str_write_buffer[MAX_BUF];
char msg[MAX_BUF],nick[MAX_BUF];
int RouteSocketID, ClientSocketID;
int ClientUserID;
int ClientSocketLength;
int count = 0;
int isunique_flag=FALSE;
char ClientIp[INET_ADDRSTRLEN];
struct sockaddr_in ClientSocketAddress;
memset((void*)&ClientSocketAddress,'\0',sizeof(ClientSocketAddress));
ClientSocketLength = sizeof(ClientSocketAddress);
ClientSocketID = accept(ChatServerSocketID,(struct sockaddr*)&ClientSocketAddress,&ClientSocketLength);
/*Obtem o Ip do cliente*/
inet_ntop(AF_INET,(void*)&(ClientSocketAddress.sin_addr),(char*)ClientIp,INET_ADDRSTRLEN);
/*Inicializa o ecra e informação visivel nele*/
while(!isunique_flag && strcmp(str_rcv,"exit"))
{
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,"\nInsira Nick (ou exit para sair): ");
write(ClientSocketID,str_write_buffer,MAX_BUF);
memset(str_rcv,'\0',MAX_BUF);
read(ClientSocketID,str_rcv,MAX_BUF);
isunique_flag = isunique(str_rcv);
}
if(strcmp(str_rcv,"exit"))
{
/*Regista user*/
if((ClientUserID=add_user(str_rcv,ClientSocketID)))
{
strcpy(nick,str_rcv); //mantem o nick
/*Envia as instruções de uso para os users (tipo welcome dos IRCd)*/
memset(str_write_buffer,'\0',MAX_BUF);
// Limpa o ecrao e dá as boas vindas tipo, Benvindo: <nick>
strcpy(str_write_buffer,"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nBem Vindo ao TPSI-CHAT: ");
strcat(str_write_buffer,nick);
strcat(str_write_buffer," [");strcat(str_write_buffer,ClientIp);strcat(str_write_buffer,"]\n");
strcat(str_write_buffer,"\nComandos:[list], [send <nickname> <msg>], [exit]\n");
write(ClientSocketID,str_write_buffer,MAX_BUF);
memset(str_write_buffer,'\0',MAX_BUF);
strcat(str_write_buffer,"\nLigado : ");
strcat(str_write_buffer,nick);
strcat(str_write_buffer," [");strcat(str_write_buffer,ClientIp);strcat(str_write_buffer,"]");
puts(str_write_buffer);
}
else
{
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,"\nLamentamos mas o numero de ligacoes maximo foi atingido. Por favor tente novamente mais tarde.\n");
write(ClientSocketID,str_write_buffer,MAX_BUF);
strcpy(str_rcv,"exit"); // prepara a saida
}
}
/*inicia a conversacao! (se a entrada for nick(no inicio) e comando a seguir, então sai)*/
while(strcmp(str_rcv,"exit"))
{
/*Apresenta a prompt de dados*/
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,PROMPT);
write(ClientSocketID,PROMPT,MAX_BUF);
/*Aguarda pelo input via standard IO*/
memset(str_rcv,'\0',MAX_BUF);
read(ClientSocketID,str_rcv,MAX_BUF);
/*Activa os comandos - list, send, exit*/
/*list*/
if(!strcmp(str_rcv,"list"))
{
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,"\nLista de Utilizadoresn");
write(ClientSocketID,str_write_buffer,MAX_BUF);
for(count=0;count<MAX_USERS;count++)
{
if(route.slot[count] == FULL)
{
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,route.Nickname[count]);
strcat(str_write_buffer,", ");
write(ClientSocketID,str_write_buffer,MAX_BUF);
}
}
}
/*send: Envia a mensagem. Pode causar alguma falha de sincronismo com o ecra mas efectua tentativa de sincronismo de seguida*/
else if(!strncmp(str_rcv,"send ",5))
{
memset(msg,'\0',MAX_BUF);
RouteSocketID = extract(str_rcv,msg);
if(RouteSocketID)
{
memset(str_write_buffer,'\0',MAX_BUF);
/*De msg + Continua*/
strcat(str_write_buffer,"*\n[Msg] ");
strcat(str_write_buffer,nick); /*[Msg] <Nickn>: <msg> */
strcat(str_write_buffer,": ");
strcat(str_write_buffer,msg); //escreve a mensagem no buffer
strcat(str_write_buffer,"\n");
strcat(str_write_buffer,PROMPT);
write(RouteSocketID,str_write_buffer,MAX_BUF); //envia a mensagem utilizando o route
}
else
{
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,"\nO utilizador não está ligado. Use: send <nickn> <msg>");
write(ClientSocketID,str_write_buffer,MAX_BUF);
}
}
/*sai*/
else if(!strcmp(str_rcv,"sai"))
{
if(remove_user(ClientUserID))
{
memset(str_write_buffer,'\0',MAX_BUF);
strcat(str_write_buffer,"\nDesligado: ");
strcat(str_write_buffer,nick);
strcat(str_write_buffer," [");strcat(str_write_buffer,ClientIp);strcat(str_write_buffer,"]");
puts(str_write_buffer);
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n Adeus:");
strcat(str_write_buffer,nick);
strcat(str_write_buffer," [");strcat(str_write_buffer,ClientIp);strcat(str_write_buffer,"]\n");
write(ClientSocketID,str_write_buffer,MAX_BUF);
break;
}
else
{
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,"\nErro: Caso aconteça por favor envie e-mail para antoniopocsantos@gmail.com com o report");
write(ClientSocketID,str_write_buffer,MAX_BUF);
}
}
/*sendchan: Envia a mensagem para canal (broadcast). Pode causar alguma falha de sincronismo com o ecra mas efectua tentativa de sincronismo de seguida
else if(!strncmp(str_rcv,"sendall ",5))
{
memset(msg,'\0',MAX_BUF);
RouteSocketID = extract(str_rcv,msg);
if(RouteSocketID)
{
memset(str_write_buffer,'\0',MAX_BUF);
//De msg + Continua
strcat(str_write_buffer,"*\n[Msg] ");
strcat(str_write_buffer,nick); //[Msg] <Nickn>: <msg>
strcat(str_write_buffer,": ");
strcat(str_write_buffer,msg);
strcat(str_write_buffer,"\n");
strcat(str_write_buffer,PROMPT);
//devia estar aqui o ciclo de envio de mensagem para canal
for (int i =0; i< MAX_USERS; i++){
write(RouteSocketID,str_write_buffer,MAX_BUF);
}
//terminaria aqui
}
else
{
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,"\nO cana inexistente. Use: send <nickn> <msg> ou sendch <canal> <mensagem>");
write(ClientSocketID,str_write_buffer,MAX_BUF);
}
}
*/
/*Comando desconhecido*/
else
{
memset(str_write_buffer,'\0',MAX_BUF);
strcpy(str_write_buffer,"Comando desconhecido. Digite: list ou send <nick> <msg> ou exit\n ");
write(ClientSocketID,str_write_buffer,MAX_BUF);
}
}
close(ClientSocketID);
pthread_exit(NULL);
}
Cliente
/*Cliente de chat*/
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h> // biblioteca posix de threads
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_BUF 200 //tamanho maximo do buffer
int ServerSocketID;
char str_ip[MAX_BUF];
char str_rcv[MAX_BUF];
/*Thread para output das mensagens no ecra (modo de texto)*/
void * displaymsg()
{
while(strcmp(str_ip,"exit"))
{
memset(str_rcv,'\0',MAX_BUF);
read(ServerSocketID,str_rcv,MAX_BUF);
puts(str_rcv);
}
pthread_exit(NULL);
}
/*Thread para receber input do teclado e enviar para o servidor*/
void * inputmsg()
{
while(strcmp(str_ip,"exit"))
{
memset(str_ip,'\0',MAX_BUF);
gets(str_ip);
write(ServerSocketID,str_ip,MAX_BUF);
}
pthread_exit(NULL);
}
int main() //funcao principal
{
pthread_t DisplayMsgThreadID, InputMsgThreadID;
struct sockaddr_in ServerSocketAddress;
char ServerIp[50]; //ip do servidor
char ServerPort[50]; //porta do servidor
/*Obtem Ip e Porta do servidor*/
memset((void*)&ServerSocketAddress,'\0',sizeof(ServerSocketAddress));
printf("Server Ip:");gets(ServerIp);
printf("Server Port:");gets(ServerPort);
//Preenche os parametros da estrutura de sockets
ServerSocketAddress.sin_family = AF_INET;
ServerSocketAddress.sin_port = htons(atoi(ServerPort));
inet_aton(ServerIp,(&ServerSocketAddress.sin_addr));
ServerSocketID = socket(AF_INET,SOCK_STREAM,0);
if(connect(ServerSocketID,(struct sockaddr *)&ServerSocketAddress,sizeof(ServerSocketAddress))<0)
{
puts("Erro: Configuracao de rede errada.");
exit(1);
}
pthread_create(&DisplayMsgThreadID,NULL,displaymsg,NULL);
pthread_create(&InputMsgThreadID,NULL,inputmsg,NULL);
pthread_join(InputMsgThreadID,NULL);
pthread_join(DisplayMsgThreadID,NULL);
printf("Pronto a iniciar conversacao\n");
close(ServerSocketID);
}