Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- [readme.txt]
- Mon Dec 24 10:53:53 BRST 2012
- by daemonio (http://daemoniolabs.wordpress.com)
- O Servidor
- ~~~~~~~~~~
- objetivo: fornecer troca de mensagens entre clientes, simulando um servidor de
- chat.
- Como é feito:
- 1) O total de clientes é definido pelo vetor list_of_clients. Esse vetor
- é do tipo "struct stclient" que é um estrutura com os seguintes campos:
- struct stclient {
- char nick[NICK_LEN+1] ;
- int sockfd ;
- int flag_has_nick ;
- int flag_is_connected ;
- } ;
- "nick" é o apelido/nome do utilizador. "sockfd" é o socket do cliente.
- As flags flag_has_nick e flag_is_connected indicam se o cliente
- já tem um apelido e se ele está conectado, respectivamente.
- 2) De início o servidor cria o socket que ficará de escuta. Esse socket
- é armazenado em socket_listening.
- 3) Após a criação do socket, os servidor limpa a lista de clientes:
- 250 /* "clean up" the list. */
- 251 for ( t = 0; t < max_users; t++ )
- 252 clean_stclient(&list_of_clients[t]) ;
- A função clean_stclient limpa cada struct do vetor.
- 3) O servidor trabalha de modo assincrono, isso é, ele não "trava" se
- ainda não há mensagens nos sockets dos clientes. Isso é feito através
- da função select. Confira a man page da função (man 3 select) para
- mais detalhes.
- 255 while ( 1 ) {
- 258 FD_ZERO(&select_set) ;
- 259 FD_SET(socket_listening, &select_set) ;
- 260 for ( t = 0; list_len > 0 && t < max_users; t++ ) {
- 261 if ( list_of_clients[t].sockfd != -1 ) {
- 262 FD_SET(list_of_clients[t].sockfd, &select_set) ;
- 263 }
- 264 }
- 266 printf("[+] Listening on %d [%d/%d] ...\n", port, list_len, max_users) ;
- Aqui vemos o uso de FD_ZERO para zerar o set select_set, usado na função select.
- Em seguida, chamamos FD_SET para adicionar socket_listening em select_set. Nesse
- set (select_set) devemos adicionar TODOS os sockets que serão monitorados por select().
- Isso inclui o socket de escuta e também todos os sockets dos clientes conectados, daí o
- iuso do loop for.
- 270 select_time.tv_sec = 2 ;
- 271 select_time.tv_usec = 0 ;
- 272
- 278 if ( (t=select(FD_SETSIZE, &select_set, NULL, NULL, &select_time)) < 0 ) {
- 279 perror("select") ;
- 280 return -1 ;
- 281 }
- Select irá esperar 2 segundos até que algo acontece nos sockets. Se algo acontecer nesses
- 2 segundos, um valor maior que zero é retornado. Se nada ocorrer, zero é retornado.
- 283 /* wow, we have something ... */
- 284 if ( t > 0 ) {
- 288 if ( FD_ISSET(socket_listening, &select_set) ) {
- 289 int n ;
- 291 if ( (n=accept(socket_listening, NULL, NULL)) < 0 ) {
- 292 perror("accept") ;
- 293 } else if ( insert_socket_into_list(n) == 1 ) { /* server is busy */
- 294 send(n,SERVER_BUSY,strlen(SERVER_BUSY),0) ;
- 295 close(n) ;
- 296 }
- 297 continue ;
- FD_ISSET testa qual socket sofreu modificação durante os 2 segundos de select(). Nesse caso,
- se o socket for o socket_listening então temos uma nova conexão. De inínicio aceitamos essa
- conexão, mas se o servidor estivar ocupado (insert_socket_into_list retornar 1 -- lista
- cheia) então fechamos a conexão.
- 298 } else {
- 299 int i ;
- 300
- 302 for ( i = 0; i < max_users; i++ ) {
- 303 if ( FD_ISSET(list_of_clients[i].sockfd, &select_set) ) {
- 304 if ( get_message_from_socket(list_of_clients[i].sockfd) == 0 ) {
- 305 int flag_system_msg = 0 ;
- 306
- 307 if(list_of_clients[i].flag_has_nick == 0) { /* setting the nickname */
- 308 list_of_clients[i].flag_has_nick = 1 ;
- 309 strncpy(list_of_clients[i].nick, msg, NICK_LEN) ;
- 310
- 311 /* remove trailling '\n' */
- 312 if(list_of_clients[i].nick[strlen(list_of_clients[i].nick)-1]=='\n') {
- 313 list_of_clients[i].nick[strlen(list_of_clients[i].nick)-1]=0 ;
- 314 }
- 315
- 316 snprintf(msg, MSG_LEN, "%s : is now on the chat!\n", list_of_clients[i].nick) ;
- 317 /* if it is 1, doenst include the prefix "<nick> said: " on message */
- 318 flag_system_msg = 1 ;
- 319 }
- 320 send_message_to_all(list_of_clients[i].sockfd, flag_system_msg) ;
- Testamos, agora, qual cliente mandou uma mensagem. PAra isso temos que percorrer toda a lista de sockets, daí o
- uso do loop for. O FD_ISSET, como sabemos, verifica se o socket em questão sofreu modificação. Se sim, sabemos
- que é uma mensagem, então obtemos essa mensagem com get_message_from_socket(). Recebida a mensage, agora temos
- que ver se ela é uma mensagem normal ou um apelido (= a primeira mensagem é sempre o apelido do usuário).
- Se flag_has_nick é zero então indica que é um apelido. Assim, setamos esse apelido na estrutura do cliente em
- questão e valoramos o valor da flag. Uma mensagem "apelido: is now on the chat" enviada para todas as pessoas
- do chat usando a função send_message_to_all().
- Se não for um setamento de apelido, então é uma mensagem normal, e ela é enviada para todos os clientes
- conectados usando a função send_message_to_all().
- O loop volta e tudo que vimos até agora é repetido.
- O Cliente
- ~~~~~~~~~~~
- objetivo: fornecer uma interface amigável para o cliente para acesso ao servidor de chat
- A sintaxe do programa deve receber o endereço do servidor, a porta, e o nome do utilizador.
- Como utilizar:
- $ alfaclient <endereço> <porto> <nome do utilizador>
- ex:
- $ alfaclient localhost 31337 maria
- Como é feito:
- 1) O cliente utiliza um interface bastante interessante. Ele divide a mesma tela de terminal
- tanto para envio quanto para recebimento de mensagem. Isso é feito da seguinte maneira: toda
- vez que o cliente aperta QUALQUER TECLA ele terá que digitar uma mensagem. Nesse momento,
- o recebimento de mensagens é cessado. Após digitar a mensagem, o cliente continua a
- receber mensagens normalmente.
- Para fazer isso temos que utilizar uma chamada non-block de getchar. A chamada non-block também
- é chamada de modo assincrono, pois a funcao nao trava para receber o caractere.
- 117 /* Obtem configuracoes antigas do terminal. */
- 118 tcgetattr(0,&initial_settings);
- 119
- 120 /* Seta novas configuracoes do terminal. */
- 121 new_settings = initial_settings;
- 122 new_settings.c_lflag &= ~ICANON;
- 123 new_settings.c_lflag &= ~ECHO;
- 124 new_settings.c_lflag &= ~ISIG;
- 125 new_settings.c_cc[VMIN] = 0;
- 126 new_settings.c_cc[VTIME] = 0;
- 127
- 128 /* Envia apelido. */
- 129 send(fd, nome_cliente, strlen(nome_cliente),0);
- Da linha 117 a linha 126 temos as rotinas para transformar a getchar() em non-block.
- Você não precisa entender isso, basta dizer que faz parte do procedimento inicial.
- Como a primeira mensagen do cliente deve ser seu apelido, então ele enviado na linha
- 129.
- 2) Para sabermos se recebmos uma mensagem, fazemos isso de modo assincrono., usando
- select() novamente.
- 131 /* Loop principal. */
- 132 while(1) {
- 133 /* Seta o socket em select. */
- 134 FD_ZERO(&select_set) ;
- 135 FD_SET(fd, &select_set) ;
- 136
- 137 /* Sem tempo de block para select. */
- 138 select_time.tv_sec = 0 ;
- 139 select_time.tv_usec = 0 ;
- 140
- 141 if( (t=select(FD_SETSIZE, &select_set, NULL, NULL, &select_time)) < 0 ) {
- 142 perror("select") ;
- 143 return -1 ;
- 144 }
- Em 135 o socket de recebimento de mensagens é adicionado. Usamos um tempo de zero segundos
- (sem delay) e em seguida, chamamos select.
- Se há algo no socket, então recebemos a mensagem:
- 146 /* Aqui temos algo no socket. */
- 147 if(t > 0) {
- 148 if(FD_ISSET(fd, &select_set)) {
- 149 int b ;
- 150 b = recv(fd, mensagem, sizeof mensagem,0);
- 151
- 152 /* Mostra mensagem na tela. */
- 153 mensagem[b] = '\0' ;
- 154 print_cor(mensagem, COR_1) ;
- 155
- 156 fflush(stdout) ;
- 157 }
- 158 }
- Se não recebemos mensagens, então temos que verificar se o usuário pressionou alguma
- tecla para enviar uma mensagem. O modo que escolhemos foi usar getchar() em modo
- de non-block:
- 160 /* Leitura non-block em getchar(). */
- 161 tcsetattr(0, TCSANOW, &new_settings);
- 162 t = getchar();
- 163 tcsetattr(0, TCSANOW, &initial_settings);
- 164
- 165 /* Retorno não EOF indica um tecla valida. */
- 166 if(t != EOF) {
- 167 /* Mostra o prompt de mensagem. */
- 168 print_cor("Digite mensagem: ", COR_2) ;
- 169 fgets(mensagem, sizeof mensagem, stdin) ;
- 170
- 171 /* Quit encerra o loop principal. */
- 172 if(strcmp(mensagem, "quit\n") == 0) {
- 173 break ;
- 174 }
- 175
- 176 /* Envia a mensagem. */
- 177 if(mensagem[0] != '\n') { /* Nao envia um ENTER isolado. */
- 178 send(fd, mensagem, strlen(mensagem),0);
- 179 }
- 180 }
- 181 }
- Antes de depois de getchar() devemos utilizar as funções indicadas em 161 e 163.
- A getchar() tentara uma leitura, mas se nada foi pressionado, então EOF é retornado.
- Porém, se há alguma tecla no buffer, o código da tecla é retornado. Nesse caso,
- comparamos o retorno com não EOF (t!=EOF) para assim chamarmos a função
- fgets() para a leitura dos dados.
- Por fim, em 177 a mensagem é enviada. Antes comparamos a mensagem com '\n' para
- não precisarmos enviar uma linha vazia para os demais usuarios.
- O loop se segue e tudo que vimos é repetido.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement