Ferramentas de Usuário

Ferramentas de Site


dev_geral:c:snippet:chat_c_2

Tabela de Conteúdos

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,"nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnBem 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,"nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn 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 exitn ");
        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 conversacaon");
  close(ServerSocketID);
}
dev_geral/c/snippet/chat_c_2.txt · Última modificação em: 2018/05/14 21:37 (edição externa)