Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

1. Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

M.
XProtoman

(usa Fedora)

Enviado em 17/07/2015 - 07:42h

Bom dia a todos,

Estou tentando desenhar um servidor que não desperdice recursos(uso o valgrind para analisar alocações e liberação de memória). Estou acostumado um pouco com pthreads, porém threads em geral podem não ser a melhor solução, fiz alguns testes com pthread de cancelar a thread e os recursos alocados não são liberados, então se eu usasse como descrevi abaixo teria problemas.

Como eu penso servidor:
Quando penso em servidores imagino algo como IRC(só tive experiência como usuário, nem cheirei o IRCD), geralmente quando mexia com Sockets quando um cliente conectava criava uma thread para gerencia-lo(autenticação depois manutenção da conexão; cuidar do timeout) e outra para receber mensagens(filha da de gerenciamento). Usava posix threads.

Na thread de gerenciamento do cliente se ocorresse timeout a thread de receber mensagens era encerrada por cancelamento e depois a thread de gerencimaneto terminava normalmente.

Realmente devo usar threads em C/C++ para esse trabalho com soquetes no lado servidor ou devo usar outra solução? Ou a forma que estou desenhando o servidor está errado?


  


2. Existem algumas bibliotecas

Uilian Ries
uilianries

(usa Linux Mint)

Enviado em 17/07/2015 - 07:56h



Não faça do zero, existem algumas bibliotecas em C++ pra ajudar.

Eu já utilizei essas duas:

Boost ASIO

De uma olhada nesse exemplo, é um servidor TCP
http://www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/tutorial/tutdaytime3.html

Em geral, ASIO é a melhor, será integrada ao C++17. O porém é ser mais "difícil" de utilizar.

Uma mais fácil, e que funciona, é da Poco Project.
Esse exemplo tem um servidor TCP e também exemplo de client.
http://pocoproject.org/slides/200-Network.pdf

Também tem a Open Framework, Qt, Weeb Toolkit (Wt). Todas elas são boas, porém usei apenas pra conhecer, não em projetos para produção.

De qualquer forma, não tente fazer no braço, apenas com threads e sokets, além de ser muito trabalhoso, vai precisar gerenciar uma série de tarefas que essas bibliotecas já suportam e está estável.


3. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

M.
XProtoman

(usa Fedora)

Enviado em 17/07/2015 - 08:08h

Bom dia obrigado por responder.

Construí a um tempo uma classe de Sockets(resolve até nomes durante connect, send, recv, bind), estou querendo trabalhar mesmo diretamente com o problema porque é até um aprendizado(para usa-lo além de sockets).

Threads tem tido uma utilidade bacana, mas acho que estou na beirada do que elas podem fazer relacionadas a liberação de recursos. Entender como solucionar o problema vai ajudar tanto em C como C++.


4. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

Paulo
paulo1205

(usa Ubuntu)

Enviado em 17/07/2015 - 10:11h

Eu ainda não estudei sobre isso (até o faria agora, pois o assunto me interessa, mas estou cum coisas para entregar no trabalho hoje, então não vou poder dedicar tempo), mas eu imagino que o modelo de threads do C++11 (que é inspirado em pthreads) deva ter alguma provisão para alocação/liberação de recursos num modelo RAII.

Você poderia pesquisar a respeito -- e quiçá dar um retorno para a gente, aqui.


5. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

Uilian Ries
uilianries

(usa Linux Mint)

Enviado em 17/07/2015 - 10:28h


Quanto as threads, a Boost Thread foi incorporada ao C++11, porém eu ainda prefiro usar Boost Thread, do que a STL Thread.

Até para ter uma ideia de suporte do C++ por compilador, pode consultar o livro: C++11 Rocks! Alex Korban

Embora a STL aparente estável, já obtive problemas utilizando std::bind, junto a std::thread, com MSVC.

A versão da Boost está mais atual (tento a premissa que está utilizando a versão mais atual da biblioteca).
Boost Thread tem função para verificação se uma interrupção foi requisitada a ela (boost::thread::interruption_requested()). Coisa que é feita com sentinelas, utilizando flags atômicas.


6. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

M.
XProtoman

(usa Fedora)

Enviado em 17/07/2015 - 10:59h

Bom dia, obrigado por responder, veja um exemplo que fiz em C++ usando thread:


#include <iostream>
#include <thread>
#include <unistd.h>

void teste()
{
for ( size_t pf = -1, p = 0; pf > p; p++ )
{
}
}

int main()
{
std::thread *thread = new std::thread(teste);
thread->detach();
sleep(1);
delete thread;
return 0;
}


Resultado do valgrind(desconsiderar a falta de 1 liberação porque a biblioteca de C++ do Fedora 22 tem esse bug):

$ valgrind ./teste.run
==14044== Memcheck, a memory error detector
==14044== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14044== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==14044== Command: ./teste.run
==14044==
==14044==
==14044== HEAP SUMMARY:
==14044== in use at exit: 73,328 bytes in 3 blocks
==14044== total heap usage: 4 allocs, 1 frees, 73,336 bytes allocated
==14044==
==14044== LEAK SUMMARY:
==14044== definitely lost: 0 bytes in 0 blocks
==14044== indirectly lost: 0 bytes in 0 blocks
==14044== possibly lost: 576 bytes in 1 blocks
==14044== still reachable: 72,752 bytes in 2 blocks
==14044== suppressed: 0 bytes in 0 blocks
==14044== Rerun with --leak-check=full to see details of leaked memory
==14044==
==14044== For counts of detected and suppressed errors, rerun with: -v
==14044== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)


Esse delete acho que faz o mesmo papel do cancel do pthread. Aquele for é proposital, poderia ser um sleep(10). Quando você cancela a thread recursos são perdidos pelo que mostra o valgrind.

Eu já tentei ver algumas soluções como fork e select, porém não queria viciar as respostas.

Li em algum lugar que select tem limitações, mas se usasse ele para gerenciar novas conexões, gerenciar timeout e receber mensagens seria 10, não existiriam deadlocks(outro problema que estou me esquivando, o foco no momento é outro). Talvez select para um servidor grande como MMO não seja aconselhável por exemplo, alguém em algum lugar falou de limite de 1024, um dos problemas do select é só usar um núcleo(obviamente). Talvez select seja parte da solução, em vez de ter um thread de gerenciamento(timeout) e um para recebimento de mensagens poderia ser um thread só e usar select para receber mensagens e timeout.

Fork tem problemas de recursos duplicados, acho que não é o caminho.


7. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

Uilian Ries
uilianries

(usa Linux Mint)

Enviado em 17/07/2015 - 11:31h

Tu realmente precisa instanciar por ponteiro e com detach?

Está utilizando o sleep como mecanismo para aguardar a thread finalizar, evite isso, é ineficiente e vai te dar dores de cabeça quando falhar. Ao invés, utilize std::thread::join()

Detach permite a thread chama, ser executada independente da thread a quem a criou.

Eu, utilizaria o seguinte código.



#include <iostream>
#include <thread>
#include <chrono>

void teste()
{
std::this_thread::sleep_for (std::chrono::seconds(1));
}

int main()
{
std::thread my_thread(teste);
my_thread.join();

return 0;
}


O Paulo falou sobre RAII, muito bem lembrado. Se for utilizar ponteiro, pense em usar smart pointers, pesquise sobre.


8. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

M.
XProtoman

(usa Fedora)

Enviado em 17/07/2015 - 11:39h

Bom dia,

O for ou o sleep são propositais, quero que a thread fique presa antes de eu acionar delete(cancel) simulando com o mínimo de recursos o que aconteceria se eu encerrasse uma thread de conexão, foi a forma que encontrei de mostrar que os recursos não são liberados se você cancelar uma thread. O deatch é obrigatório, join no caso de gerenciamento de conexões não seria o correto a utilizar porque você acabaria fazendo o programa esperar todos os joins terminarem e ambiente de conexão é dinâmico, a natureza do problema não permite join.

Sobre o ponteiro é outra traquinagem proposital, preciso fazer meu cancel(delete) no momento indicado.


9. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

Paulo
paulo1205

(usa Ubuntu)

Enviado em 17/07/2015 - 14:03h

Copiei seu exemplo aqui, compilei-o e rodei sob o valgrind dezenas de vezes, e não consegui ver nenhum leak. Que opções você está usando na compilação e na invocação do valgrind?


10. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

Paulo
paulo1205

(usa Ubuntu)

Enviado em 17/07/2015 - 14:21h

Aumentei o seu programa, mexendo na função teste.

void teste()
{
int fd=open("/etc/group", O_RDONLY, 0644);
raii_fd rfd(dup(fd));
std::vector<int> a(1000);
for ( size_t pf = -1, p = 0; pf > p; p++ )
{
a[pf%1000]=pf;
}
}


raii_fd é uma classe que eu fiz há alguns anos que, pelo nome, acho que dispensa maiores explicações. std::vector, por natureza, é uma forma RAII de simular arrays, usando alocação a partir da memória livre.

O valgrind, com esse programa, continuou indicando zero vazamentos. Com um strace, eu confirmei que os recursos controlados com RAII foram devidamente desalocados; o descritor fd foi o único recurso não liberado quando a thread foi cancelada -- como, aliás, esperado.


11. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

M.
XProtoman

(usa Fedora)

Enviado em 17/07/2015 - 14:43h

Opa, boa tarde,

Compilei assim:
$ g++ -std=c++14 -pthread teste.cpp -o teste.run 

Não usei nenhum parâmetro no valgrind, apenas:
$ valgrind ./teste.run 


O teste anterior foi no Fedora 22, testei no Debian 8.1 em máquina virtual usando o exemplo de for e o de sleep que falei em ambos os casos existiu perda:
$ valgrind ./teste.run 
==7347== Memcheck, a memory error detector
==7347== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7347== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==7347== Command: ./teste.run
==7347==
==7347==
==7347== HEAP SUMMARY:
==7347== in use at exit: 336 bytes in 2 blocks
==7347== total heap usage: 3 allocs, 1 frees, 344 bytes allocated
==7347==
==7347== LEAK SUMMARY:
==7347== definitely lost: 0 bytes in 0 blocks
==7347== indirectly lost: 0 bytes in 0 blocks
==7347== possibly lost: 288 bytes in 1 blocks
==7347== still reachable: 48 bytes in 1 blocks
==7347== suppressed: 0 bytes in 0 blocks
==7347== Rerun with --leak-check=full to see details of leaked memory
==7347==
==7347== For counts of detected and suppressed errors, rerun with: -v
==7347== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)


Repetindo o código:

#include <iostream>
#include <thread>
#include <unistd.h>

void teste()
{
for ( size_t pf = -1, p = 0; pf > p; p++ )
{
}
//sleep(10);
}

int main()
{
std::thread *thread = new std::thread(teste);
thread->detach();
sleep(1);
delete thread;
return 0;
}



12. Re: Dúvidas sobre Sockets, Threads e gerenciamento recursos em servidor C/C++

Paulo
paulo1205

(usa Ubuntu)

Enviado em 17/07/2015 - 14:48h

Eu nem mesmo vejo no seu programa recurso alocado na thread. Será que o problema está mesmo nela?

De todo modo, o meu ambiente de teste é diferente do seu. Estou usando o Ubuntu 14.04, e compilei os programas usando a opção "-std=c++11" (meu g++ nem suporta c++14). A versão do valgrind é "3.10.0.SVN and LibVEX".

Suas alocações estão usando brk() ou mmap()?



01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts