Advertisement
daemonio

readme.txt do chatserv

Dec 24th, 2012
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.08 KB | None | 0 0
  1. [readme.txt]
  2. Mon Dec 24 10:53:53 BRST 2012
  3.  
  4. by daemonio (http://daemoniolabs.wordpress.com)
  5.  
  6.  
  7. O Servidor
  8. ~~~~~~~~~~
  9.  
  10. objetivo: fornecer troca de mensagens entre clientes, simulando um servidor de
  11. chat.
  12.  
  13. Como é feito:
  14.  
  15. 1) O total de clientes é definido pelo vetor list_of_clients. Esse vetor
  16. é do tipo "struct stclient" que é um estrutura com os seguintes campos:
  17.  
  18. struct stclient {
  19. char nick[NICK_LEN+1] ;
  20. int sockfd ;
  21. int flag_has_nick ;
  22. int flag_is_connected ;
  23. } ;
  24.  
  25. "nick" é o apelido/nome do utilizador. "sockfd" é o socket do cliente.
  26. As flags flag_has_nick e flag_is_connected indicam se o cliente
  27. já tem um apelido e se ele está conectado, respectivamente.
  28.  
  29. 2) De início o servidor cria o socket que ficará de escuta. Esse socket
  30. é armazenado em socket_listening.
  31.  
  32. 3) Após a criação do socket, os servidor limpa a lista de clientes:
  33.  
  34. 250 /* "clean up" the list. */
  35. 251 for ( t = 0; t < max_users; t++ )
  36. 252 clean_stclient(&list_of_clients[t]) ;
  37.  
  38. A função clean_stclient limpa cada struct do vetor.
  39.  
  40. 3) O servidor trabalha de modo assincrono, isso é, ele não "trava" se
  41. ainda não há mensagens nos sockets dos clientes. Isso é feito através
  42. da função select. Confira a man page da função (man 3 select) para
  43. mais detalhes.
  44.  
  45. 255 while ( 1 ) {
  46. 258 FD_ZERO(&select_set) ;
  47. 259 FD_SET(socket_listening, &select_set) ;
  48. 260 for ( t = 0; list_len > 0 && t < max_users; t++ ) {
  49. 261 if ( list_of_clients[t].sockfd != -1 ) {
  50. 262 FD_SET(list_of_clients[t].sockfd, &select_set) ;
  51. 263 }
  52. 264 }
  53. 266 printf("[+] Listening on %d [%d/%d] ...\n", port, list_len, max_users) ;
  54.  
  55. Aqui vemos o uso de FD_ZERO para zerar o set select_set, usado na função select.
  56. Em seguida, chamamos FD_SET para adicionar socket_listening em select_set. Nesse
  57. set (select_set) devemos adicionar TODOS os sockets que serão monitorados por select().
  58. Isso inclui o socket de escuta e também todos os sockets dos clientes conectados, daí o
  59. iuso do loop for.
  60.  
  61. 270 select_time.tv_sec = 2 ;
  62. 271 select_time.tv_usec = 0 ;
  63. 272
  64. 278 if ( (t=select(FD_SETSIZE, &select_set, NULL, NULL, &select_time)) < 0 ) {
  65. 279 perror("select") ;
  66. 280 return -1 ;
  67. 281 }
  68.  
  69. Select irá esperar 2 segundos até que algo acontece nos sockets. Se algo acontecer nesses
  70. 2 segundos, um valor maior que zero é retornado. Se nada ocorrer, zero é retornado.
  71.  
  72. 283 /* wow, we have something ... */
  73. 284 if ( t > 0 ) {
  74. 288 if ( FD_ISSET(socket_listening, &select_set) ) {
  75. 289 int n ;
  76. 291 if ( (n=accept(socket_listening, NULL, NULL)) < 0 ) {
  77. 292 perror("accept") ;
  78. 293 } else if ( insert_socket_into_list(n) == 1 ) { /* server is busy */
  79. 294 send(n,SERVER_BUSY,strlen(SERVER_BUSY),0) ;
  80. 295 close(n) ;
  81. 296 }
  82. 297 continue ;
  83.  
  84. FD_ISSET testa qual socket sofreu modificação durante os 2 segundos de select(). Nesse caso,
  85. se o socket for o socket_listening então temos uma nova conexão. De inínicio aceitamos essa
  86. conexão, mas se o servidor estivar ocupado (insert_socket_into_list retornar 1 -- lista
  87. cheia) então fechamos a conexão.
  88.  
  89. 298 } else {
  90. 299 int i ;
  91. 300
  92. 302 for ( i = 0; i < max_users; i++ ) {
  93. 303 if ( FD_ISSET(list_of_clients[i].sockfd, &select_set) ) {
  94. 304 if ( get_message_from_socket(list_of_clients[i].sockfd) == 0 ) {
  95. 305 int flag_system_msg = 0 ;
  96. 306
  97. 307 if(list_of_clients[i].flag_has_nick == 0) { /* setting the nickname */
  98. 308 list_of_clients[i].flag_has_nick = 1 ;
  99. 309 strncpy(list_of_clients[i].nick, msg, NICK_LEN) ;
  100. 310
  101. 311 /* remove trailling '\n' */
  102. 312 if(list_of_clients[i].nick[strlen(list_of_clients[i].nick)-1]=='\n') {
  103. 313 list_of_clients[i].nick[strlen(list_of_clients[i].nick)-1]=0 ;
  104. 314 }
  105. 315
  106. 316 snprintf(msg, MSG_LEN, "%s : is now on the chat!\n", list_of_clients[i].nick) ;
  107. 317 /* if it is 1, doenst include the prefix "<nick> said: " on message */
  108. 318 flag_system_msg = 1 ;
  109. 319 }
  110. 320 send_message_to_all(list_of_clients[i].sockfd, flag_system_msg) ;
  111.  
  112. Testamos, agora, qual cliente mandou uma mensagem. PAra isso temos que percorrer toda a lista de sockets, daí o
  113. uso do loop for. O FD_ISSET, como sabemos, verifica se o socket em questão sofreu modificação. Se sim, sabemos
  114. que é uma mensagem, então obtemos essa mensagem com get_message_from_socket(). Recebida a mensage, agora temos
  115. que ver se ela é uma mensagem normal ou um apelido (= a primeira mensagem é sempre o apelido do usuário).
  116.  
  117. Se flag_has_nick é zero então indica que é um apelido. Assim, setamos esse apelido na estrutura do cliente em
  118. questão e valoramos o valor da flag. Uma mensagem "apelido: is now on the chat" enviada para todas as pessoas
  119. do chat usando a função send_message_to_all().
  120.  
  121. Se não for um setamento de apelido, então é uma mensagem normal, e ela é enviada para todos os clientes
  122. conectados usando a função send_message_to_all().
  123.  
  124. O loop volta e tudo que vimos até agora é repetido.
  125.  
  126.  
  127. O Cliente
  128. ~~~~~~~~~~~
  129.  
  130. objetivo: fornecer uma interface amigável para o cliente para acesso ao servidor de chat
  131. A sintaxe do programa deve receber o endereço do servidor, a porta, e o nome do utilizador.
  132.  
  133. Como utilizar:
  134.  
  135. $ alfaclient <endereço> <porto> <nome do utilizador>
  136.  
  137. ex:
  138.  
  139. $ alfaclient localhost 31337 maria
  140.  
  141. Como é feito:
  142.  
  143. 1) O cliente utiliza um interface bastante interessante. Ele divide a mesma tela de terminal
  144. tanto para envio quanto para recebimento de mensagem. Isso é feito da seguinte maneira: toda
  145. vez que o cliente aperta QUALQUER TECLA ele terá que digitar uma mensagem. Nesse momento,
  146. o recebimento de mensagens é cessado. Após digitar a mensagem, o cliente continua a
  147. receber mensagens normalmente.
  148.  
  149. Para fazer isso temos que utilizar uma chamada non-block de getchar. A chamada non-block também
  150. é chamada de modo assincrono, pois a funcao nao trava para receber o caractere.
  151.  
  152.  
  153. 117 /* Obtem configuracoes antigas do terminal. */
  154. 118 tcgetattr(0,&initial_settings);
  155. 119
  156. 120 /* Seta novas configuracoes do terminal. */
  157. 121 new_settings = initial_settings;
  158. 122 new_settings.c_lflag &= ~ICANON;
  159. 123 new_settings.c_lflag &= ~ECHO;
  160. 124 new_settings.c_lflag &= ~ISIG;
  161. 125 new_settings.c_cc[VMIN] = 0;
  162. 126 new_settings.c_cc[VTIME] = 0;
  163. 127
  164. 128 /* Envia apelido. */
  165. 129 send(fd, nome_cliente, strlen(nome_cliente),0);
  166.  
  167.  
  168. Da linha 117 a linha 126 temos as rotinas para transformar a getchar() em non-block.
  169. Você não precisa entender isso, basta dizer que faz parte do procedimento inicial.
  170.  
  171. Como a primeira mensagen do cliente deve ser seu apelido, então ele enviado na linha
  172. 129.
  173.  
  174. 2) Para sabermos se recebmos uma mensagem, fazemos isso de modo assincrono., usando
  175. select() novamente.
  176.  
  177. 131 /* Loop principal. */
  178. 132 while(1) {
  179. 133 /* Seta o socket em select. */
  180. 134 FD_ZERO(&select_set) ;
  181. 135 FD_SET(fd, &select_set) ;
  182. 136
  183. 137 /* Sem tempo de block para select. */
  184. 138 select_time.tv_sec = 0 ;
  185. 139 select_time.tv_usec = 0 ;
  186. 140
  187. 141 if( (t=select(FD_SETSIZE, &select_set, NULL, NULL, &select_time)) < 0 ) {
  188. 142 perror("select") ;
  189. 143 return -1 ;
  190. 144 }
  191.  
  192. Em 135 o socket de recebimento de mensagens é adicionado. Usamos um tempo de zero segundos
  193. (sem delay) e em seguida, chamamos select.
  194.  
  195. Se há algo no socket, então recebemos a mensagem:
  196.  
  197. 146 /* Aqui temos algo no socket. */
  198. 147 if(t > 0) {
  199. 148 if(FD_ISSET(fd, &select_set)) {
  200. 149 int b ;
  201. 150 b = recv(fd, mensagem, sizeof mensagem,0);
  202. 151
  203. 152 /* Mostra mensagem na tela. */
  204. 153 mensagem[b] = '\0' ;
  205. 154 print_cor(mensagem, COR_1) ;
  206. 155
  207. 156 fflush(stdout) ;
  208. 157 }
  209. 158 }
  210.  
  211. Se não recebemos mensagens, então temos que verificar se o usuário pressionou alguma
  212. tecla para enviar uma mensagem. O modo que escolhemos foi usar getchar() em modo
  213. de non-block:
  214.  
  215. 160 /* Leitura non-block em getchar(). */
  216. 161 tcsetattr(0, TCSANOW, &new_settings);
  217. 162 t = getchar();
  218. 163 tcsetattr(0, TCSANOW, &initial_settings);
  219. 164
  220. 165 /* Retorno não EOF indica um tecla valida. */
  221. 166 if(t != EOF) {
  222. 167 /* Mostra o prompt de mensagem. */
  223. 168 print_cor("Digite mensagem: ", COR_2) ;
  224. 169 fgets(mensagem, sizeof mensagem, stdin) ;
  225. 170
  226. 171 /* Quit encerra o loop principal. */
  227. 172 if(strcmp(mensagem, "quit\n") == 0) {
  228. 173 break ;
  229. 174 }
  230. 175
  231. 176 /* Envia a mensagem. */
  232. 177 if(mensagem[0] != '\n') { /* Nao envia um ENTER isolado. */
  233. 178 send(fd, mensagem, strlen(mensagem),0);
  234. 179 }
  235. 180 }
  236. 181 }
  237.  
  238. Antes de depois de getchar() devemos utilizar as funções indicadas em 161 e 163.
  239.  
  240. A getchar() tentara uma leitura, mas se nada foi pressionado, então EOF é retornado.
  241. Porém, se há alguma tecla no buffer, o código da tecla é retornado. Nesse caso,
  242. comparamos o retorno com não EOF (t!=EOF) para assim chamarmos a função
  243. fgets() para a leitura dos dados.
  244.  
  245. Por fim, em 177 a mensagem é enviada. Antes comparamos a mensagem com '\n' para
  246. não precisarmos enviar uma linha vazia para os demais usuarios.
  247.  
  248.  
  249. O loop se segue e tudo que vimos é repetido.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement