paulo1205
(usa Ubuntu)
Enviado em 14/04/2015 - 03:15h
Se você vir a manpage de
socket(7), verá que associar um timeout ao socket só afeta as operações de I/O propriamente ditas, não um
select() sobre esses descritores. Se você quiser um medidor de tempo de inatividade de cada cliente, terá de implementar por conta própria.
Minha sugestão:
1) Troque seu array de descritores por um array de estruturas com um formato parecido com o seguinte.
struct client {
int fd;
time_t last_activity;
};
2) Toda vez que você realizar uma operação com
client[i].fd, atualize o valor de
client[i].last_activity com a hora corrente.
3) Use um parâmetro não-nulo no último argumento de
select().
4) Antes do fim do laço de repetição, varra o array de clientes procurando aqueles com um descritor válido, mas com a hora da última atualização muito antiga, tomando as medidas necessárias.
Outras dicas:
- Use
-1, em vez de
0, para indicar descritores que não estão em uso, pois
0 é um descritor perfeitamente válido (geralmente, mas não necessariamente, associado ao terminal), ao passo que
-1 garantidamente não pode ser usado com descritor válido.
- Cuidado com SIGPIPE (ou, em outras palavras, coloque um tratador para SIGPIPE). Do jeito como está seu programa, se você escrever num socket e o outro lado tiver derrubado a conexão, você vai tomar um SIGPIPE, e o programa vai cair, derrubando todos os clientes.
- A função
select() e as macros para manipulação de conjuntos de descritores agora residem em
<sys/select.h>. Os comentários que você colocou ao lado dos includes, justificando seu uso, refeletem uma disposição obsoleta.
- Quando você tenta enviar a mensagem de boas-vindas na conexão recebida e nota que não consegue, mesmo assim adiciona esse cliente à lista de clientes para o restante do tempo de vida do programa. É isso mesmo que você quer? Eu imagino se abortar esse cliente logo de cara não seria melhor.
- Lembre que
recv() pode devolver três tipos de valores: dados recebidos com sucesso (>0), fim de dados (0) ou erro (-1, sendo que existem casos em que esse suposto erro não significa fim de conexão). Você tem um tratamento especial para o fim de conexão, e assume que tudo o que não for fim de conexão é sucesso. Eu tendo a achar que um caso de erro não deve acontecer no seu programa do jeito como está agora, mas eu acho bom colocar esse código lá, até para prepará-lo para receber o tratador de SIGPIPE, que eu sugeri acima.
- Se o seu programa é realmente um bom servidor de eco, esteja preparado para clientes que não envie apenas coisas que possam ser tratadas como string do C. Suponha, por exemplo, que alguém envie a seguinte sequência de bytes:
"Mensagem\0truncada\r\n"
Você vai ter
recv recebendo todos esses bytes, mas vai ecoar de volta apenas “Mensagem” e descartar o resto do buffer, já que
strlen() vai parar de contar ao encontrar o byte nulo recebido na entrada.
- Você está ecoando os caracteres recebidos sem verificar como foi o resultado da execução de
send(). Cuidado, pois você pode ter erros (incluindo um SIGPIPE que, se não tratado, pode matar o programa) ou sucesso parcial (apenas uma parte dos dados enviada com sucesso; o restante teria de ser reenviado depois).