Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Tiago Souza Ribeiro

Servidor repetindo send() interminavelmente

Recommended Posts

Olá.

Estou iniciando agora em C++ (mesmo que nesse caso aqui, não tenha usado orientação a objeto; são apenas testes) mas já manejo bem a linguagem por ter familiaridade com PHP. Estou iniciando mesmo em sockets, os quais nunca usei.

 

Estava seguindo esse guia: http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html

Consegui desenvolver um pequeno servidor que apenas envia um texto ao usuário usando o método dos processos filhos, o qual é bem defasado e obtive esse resultado funcional: http://pastebin.com/sXmErmiT. Aí chegou na parte do select() e fui tentar refazer esse servidor usando essa, para mim nova, técnica.

 

Obtive o seguinte:

 

#include <sys/types.h> //socklen_t and fd_set types
#include <sys/socket.h> //socket() bind() listen() accept()
#include <netdb.h> //structs
#include <cstdio> //perror() fprintf()
#include <string.h> //memset()
#include <unistd.h> //close()
 
#define PORT "19456"
#define MAX_CONNECTIONS 10
 
using namespace std;
 
int main() {
 
     struct sockaddr_storage remote_addr;
     socklen_t remote_addr_size;
     struct addrinfo settings, *result_info, *info;
     int srv_socket, conn_socket, gai_result, biggest_descriptor, i, yes = 1;
     fd_set listening, write_set;
 
 
 
     FD_ZERO(&listening); //Clear
     FD_ZERO(&write_set); //the sets
 
     memset(&settings, 0, sizeof settings); //Remove valores padrão da struct
     settings.ai_family = AF_INET; //IPv4
     settings.ai_socktype = SOCK_STREAM;
     settings.ai_flags = AI_PASSIVE; //Usar IP's da própria máquina
 
     if((gai_result = getaddrinfo(NULL, PORT, &settings, &result_info)) != 0) { //Obtém informações do endereço, salva em result_info e usa configs de settings
          fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai_result));
          return 1;
     } //NULL no primeiro param faz usar AI_PASSIVE, e se a máquina tiver mais de um ipv4 no caso, vão haver vários addrinfo
 
     for(info = result_info; info != NULL; info = info->ai_next) { //percorre cada addrinfo obtido e tenta bind em cada um
          if((srv_socket = socket(info->ai_family, info->ai_socktype, info->ai_protocol)) == -1) {
               perror("Error on socket()");
               continue;
          }
 
          if(setsockopt(srv_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
               close(srv_socket);
               perror("Error on setsockopt()");
               return 1;
          }
 
          if(bind(srv_socket, info->ai_addr, info->ai_addrlen) == -1) {
               close(srv_socket);
               perror("Error on bind()");
               continue;
          }
 
          break; //não sei como, torna info acessível no escopo global
     }
 
     if(info == NULL) { //chegou ao último addrinfo (NULL) sem ter conseguido fazer o bind
          fprintf(stderr, "Failed on all bind()");
          return 1;
     }
 
     freeaddrinfo(result_info);
 
     if(listen(srv_socket, MAX_CONNECTIONS) == -1) {
          perror("Error on listen()"); //Envia para saída padrão de erros - stderr - "Error on listen(): mensagem de errno"
          return 1;
     }
 
     FD_SET(srv_socket, &listening);
     biggest_descriptor = srv_socket; //Maior (e no momento, único) descritor de arquivo
 
     while(1) {
          if(select(biggest_descriptor + 1, &listening, &write_set, NULL, NULL) == -1) {
               close(srv_socket);
               perror("Error on select()");
               return 1;
          }
 
          if(FD_ISSET(srv_socket, &listening)) { //Verifica se há nova conexão
               remote_addr_size = sizeof remote_addr;
 
               if((conn_socket = accept(srv_socket, (struct sockaddr *) &remote_addr, &remote_addr_size)) == -1) { //Aceita a nova conexão
                    close(srv_socket);
                    perror("Error on accept()");
                    return 1;
               }
 
               if(conn_socket > biggest_descriptor) //Atualiza a variável do maior descritor
                    biggest_descriptor = conn_socket;
 
               FD_SET(conn_socket, &write_set); //Adiciona o novo socket ao set de escrita
          }
 
          for(i = 0; i <= biggest_descriptor; i++) { //Percorre cada socket nos sets do select
               if(FD_ISSET(i, &write_set)) //Verifica se o socket está aguardando por escrita
                    if(send(i, "Test", 4, 0) == -1) { //Caso esteja, envia o texto
                         close(i);
                         perror("Error on send()");
                         return 1;
                    }
          }
     }
 
     return 0;
 
}
 


Não consegui entender o exemplo para select() no guia que eu estava seguindo e tentei fazer desse jeito aí, usando minha própria lógica. Também li um ou outro tutorial para ver se entendia. Achei mais fácil usar o poll(), mas quero entender esse método aí.

O servidor executa normalmente. O problema é que quando conecto-me a ele, algum problema na minha lógica fez com que ele ficasse me enviando "Test", no send(), sem parar, chegando a travar o computador. Acredito que o problema possa estar no for lá no final, acho que não entendi como percorrer todos os sockets que estão nos sets do select(), sei lá. Ainda ocorre que se eu fechar a conexão durante esse bombardeio de "Test", o servidor para de executar com a seguinte mensagem:

Error on send(): Connection reset by peer


Alguém pode ajudar?

Obrigado pela atenção; até mais.

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Desculpem-me por esse post logo após o meu outro, mas é que a opção de editar não está mais disponível aqui e alguém pode acabar tendo trabalho em vão me respondendo se eu não atualizar aqui:

Consegui resolver o problema do send() repetindo infinitamente :D. O que fiz foi apenas fechar o socket e removê-lo do writefds do select() após o send().

No entanto ainda me ocorrem uma dúvida (talvez duas, mas acho que a outra eu consigo solucionar):
- Se eventualmente o socket que eu remover for o maior descritor de arquivo entre todos que estão sendo usados no select(), como posso verificar entre os que restou, qual é o maior para poder usá-lo na variável biggest_descriptor? Eu poderia deixar do jeito que está até que numa nova conexão um socket maior surja e se torne o biggest_descriptor, mas e se nenhum dos novos sockets que forem accept()ed's não for maior que esse que foi removido? Há problema se eu deixar o biggest... como sendo um socket que já foi fechado e não mais usado no select()?

 

Obrigado pela atenção, e até mais.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.