Envio de Sockets. [RESOLVIDO]

1. Envio de Sockets. [RESOLVIDO]

Marcio Donizeti Piai
PIAIXX5000

(usa Red Hat)

Enviado em 03/01/2017 - 10:32h

Pessoal. Bom dia. Tentarei explicar minha dúvida .

Temos uns 11 PCs no campo que rodam Linux (Gentoo versão antiga). Cada um desses PCs tem IP diferenciado. Eles enviam sockets para um PC na Central (com Windows 7 - aplicação Delphi 5) para um mesmo IP - classe 192.168.1.... em portas diferenciadas a intervalos curtos.

Resumo do código: s1 é o descritor do socket.

if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) >= 0) // abrir o socket
{
sin.sin_family = PF_INNET; // sin é a estrutura do socket
sin.sin_port = htons (portax); // cada PC no campo manda p/ um mesmo IP em portas distintas
inet_aton (ipx, & (sin.sin_addr));
bzero (&(sin.sin_zero ), 8);
tam = sizeof (struct (sockaddr);
fcntl (s1, F_SETFL, O_NOMBLOCK); // utilizando socket não bloqueante
}
else // tratar o erro;
CONECTAR:
retorno = (conect (s1, &sin, tam) ;
if (retorno >= 0) conec = 1; //conectou - às vezes não conecta logo de cara - insistir um pouco
else
{
dá um tempinho;
volta para CONECTAR;
se o tempinho for muito significa que não conseguiu conexão - trata o errr0
}

até aqui tudo bem está conectado. O có
//LOOP de envio
if (conec == 1) // está conectado
{
if (send (s1, stringx, 172,0) >= 0) // enviou sem problemas . este envio fica dentro de um loop
else trata o erro.

O código de retorno do SEND é sempre 115 que significa "Operação em andamento"
O envio chega normalmente no destino mesmo com código 115. Eu gostaria de receber retorno = 0, não sei
porque recebo 115. Essa é a primeira dúvida.

Segunda dúvida:
Se durante o processo de envio das mensagens a rede cai, o comando SEND continua dando retorno 115 em vez de -1
Isto faz com que o programa continue rodando mesmo sem a rede de pé , o que para mim não é bom. Tenho que parar
o programa que roda nos PCs de campo.
Para tratar isto o que seria recomendável:

De vez em quando durante o processo de envio, testar um conect para ver se a rede continua de pé.
Seria isso o melhor ou alguem tem uma outra idéia.

Desde já agradeço a tenção dos Srs (as).


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 04/01/2017 - 20:03h

XProtoman escreveu:

Você acha que para sistemas como Linux e *BSDs e alguns Unix é interessante tratar diferenciadamente PF e AF? Porque se for vou ser obrigado a criar uma classe "Familia" para abrigar o valor de PF e AF. O que acha?


Eu acho que é importante que o programa esteja correto de todos os pontos de vista possíveis, inclusive semântico e estético.

Vou dar um exemplo esdrúxulo ao extremo. Suponha que eu sei que AF_INET e PF_INET correspondem ao valor inteiro 2 (o que, por sinal, é verdade nos BSDs, no Linux, no SOlaris e no AIX -- e talvez o seja até no Windows, se bobear). Isso significa que eu posso livremente deixar de usar as constantes simbólicas, e passar a usar números? Ou que eu poderia usar outras constantes simbólicas que também tenham o valor 2 (como, por exemplo, O_RDWR, de <fcntl.h>)?

Veja as quatro linhas de código abaixo, que compilam igualmente sem problemas em qualquer sistema (pelo menos POSIX) e produzem o mesmo efeito final, e diga qual a que você esperaria encontrar para indicar que a função chamada tem um argumento que representa uma família de protocolos (protocol family, em Inglês), e leve em conta que eu só faço a brincadeira com o primeiro argumento (os outros dois permitiriam zoações semelhantes).

int sock=socket(2, SOCK_STREAM, 0); 

int sock=socket(PF_INET, SOCK_STREAM, 0); 

int sock=socket(O_RDWR, SOCK_STREAM, 0); 

int sock=socket(AF_INET, SOCK_STREAM, 0); 


No outro lado da moeda, você tem uma estrutura que tem um campo que descreve uma família de endereços (no Inglês, address family). Qual das alternativas abaixo, que também compilam sem erro nem diagnóstico de impropriade pelo compilador, parece testar isso da melhor maneira (considere que peer_addr é do tipo struct sockaddr, e cujo valor foi preenchido através de uma chamada a accept(), que utilizou seu endereço como segundo argumento)?

if(peer_addr.sa_family==2 && ntohs(*((struct sockaddr_in *)&peer_addr)->sin_port)<1024){ /* ... */ } 

if(peer_addr.sa_family==PF_INET && ntohs(*((struct sockaddr_in *)&peer_addr)->sin_port)<1024){ /* ... */ } 

if(peer_addr.sa_family==O_RDWR && ntohs(*((struct sockaddr_in *)&peer_addr)->sin_port)<1024){ /* ... */ } 

if(peer_addr.sa_family==AF_INET && ntohs(*((struct sockaddr_in *)&peer_addr)->sin_port)<1024){ /* ... */ } 


Atualmente tenho uma classe Soquete e Endereço que só trabalham com valores PF e não AF.


Em muitos fóruns, eu vejo a recomendação no sentido contrário (i.e. usar sempre AF_*, seguindo o exemplo dos livros do W. Richard Stevens, autor, já falecido, dos excelentes e aclamados Advanced Programming in the UNIX Environment e UNIX Network Programming).

EU tento entender a coisa à luz do momento histórico. A interface de sockets que inspirou tudo isso que temos hoje foi publicada em 1982, junto com o 4.2BSD, o que significa que deve ter começado a ser criada alguns anos antes disso. Nessa época, a linguagem de implementação do UNIX era um C K&R, que não possuía enum nem void, e era extremamente leniente com conversão entre ponteiros (e mesmo entre ponteiros e inteiros). Os recursos dessa linguagem eram pouco mais do que o de um assembly.

E, no entanto, existe um quê de orientação a objetos com herança no projeto dos sockets BSD, mesmo implementado nessa linguagem primitiva: as mesmas funções de envio e recepção de dados podem ser usadas para sockets em domínios distintos e com protocolos distintos. Também são comuns as funções de sincronização e controle. E existe até uma certa relação entre sockets e descritores nativos de arquivos comuns, que reaproveitam chamadas como read(), write() e até mesmo close() e fcntl().

A chamada socket() funciona como construtor do objeto base, que é “herdado” pelos objetos finais. Uma parte da construção tem de ser feita manualmente pelo programador, ao criar uma estrutura de endereço de uma família compatível com o objeto base, e depois associar ao socket tal endereço dessa família através de uma chamada a bind() ou a connect(), terminando a construção do objeto usado para comunicação. A chamada accept() também tem algo de construtor (e, na gíria de C++, construtor virtual, ainda por cima), pois faz um clone do socket passivo com propriedades, ainda que com alguns valores diferentes, do socket passivo original.

Mas isso tudo foi feito em C, e num C muito primitivo, e para rodar em máquinas com recursos ridiculamente limitados, se comparadas às de hoje (por exemplo: 256kB, ou até menos, de RAM total). Em lugar de ter diversos ponteiros dentro de estruturas (que é como funcionam as classes abstratas e funções-membros virtuais de classes do C++), o socket tinha um punhado de valores inteiros para representar o próprio identificador do socket (descritor de arquivo), o domínio de comunicação, o protocolo usado dentro desse domínio e o modo de endereçamento. Nunca vi o código, mas a julgar pela forma da API, uma função como send() deve ter alguns blocos de switches ou um emaranhado de ifs, que seleciona o comportamento da função a partir dos valores dessas constantes. C++ faria através de despacho dinâmico, com funções virtuais especializadas para cada tipo de objeto.

De novo sem ter olhado código, eu acho que a ideia inicial de distinguir entre o domínio da comunicação (ou família de protocolos) e a família de endereços subjacentes à comunicação com tais protocolos era boa, e eu não sei o motivo de, no fim das contas, essas duas coisas terem se misturado e confundido. Com olhos de 2017, não de 1982, faria um bocado de sentido que o domínio PF_INET pudesse suportar tanto endereços IPv4 (família de endereços AF_INET), quanto IPv6 (família AF_INET6), sem a necessidade de um domínio separado PF_INET6, ainda mais quando quase todos os protocolos de transporte e de sinalização são os mesmos (i.e. TCP, UDP, ICMP, IGMP etc.).

Aliás, essa confusão, até onde sei, pode até nem mesmo ser necessariamente verdadeira. Não conheço nenhuma referência que oficialmente proíba você tentar associar um endereço do tipo AF_INET6 a um socket criado com PF_INET, e me arrisco a opinar que uma implementação que resolvesse fazê-lo possivelmente não violaria nenhuma regra explícita da API.

3. Re: Envio de Sockets. [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 03/01/2017 - 20:41h

PIAIXX5000 escreveu:

Pessoal. Bom dia. Tentarei explicar minha dúvida .

Temos uns 11 PCs no campo que rodam Linux (Gentoo versão antiga). Cada um desses PCs tem IP diferenciado. Eles enviam sockets para um PC na Central (com Windows 7 - aplicação Delphi 5) para um mesmo IP - classe 192.168.1.... em portas diferenciadas a intervalos curtos.

Resumo do código: s1 é o descritor do socket.


Nota antes de começar: o código abaixo tem vários erros, que suponho serem de transcrição. Do jeito como está, ele não compilaria.

if  ((s1 = socket(PF_INET, SOCK_STREAM, 0))  >= 0)    // abrir o socket
{
sin.sin_family = PF_INNET; // sin é a estrutura do socket


Aqui está um erro (e não me refiro à duplicidade do “N” na hora de transcrever “PF_INET”).

Ao usar a função socket(), o primeiro argumento é uma constante cujo nome começa com “PF_” (por exemplo: PF_INET ou PF_LOCAL), mas ao atribuir à estrutura do endereço, você deve usar constantes cujos nomes começam com “AF_” (por exemplo: AF_INET ou AF_LOCAL).

Na prática, no Linux todas as constantes PF_xxx têm os mesmos valores das constantes AF_xxx, de modo que muita gente usa as duas indistintamente. Mas é teoricamente possível que uma mesma família de protocolos (primeiro argumento de socket()) possa ser usada com diferentes famílias de endereços (campo da estrutura que representa o endereço), e vice-versa. Mesmo que essa possibilidade teórica nunca tenha sido usada na prática, um programa preocupado com correção estrita deveria levar em conta a natureza diferente das informações representadas.

Então, a maneira certa de fazer seria dizer o seguinte.

sin.sin_family=AF_INET;[/quote]

[code] sin.sin_port = htons (portax); // cada PC no campo manda p/ um mesmo IP em portas distintas
inet_aton (ipx, & (sin.sin_addr));


Você não está testando se a função retorna erro. Um programa seguro deveria se preocupar com isso.

    bzero (&(sin.sin_zero ), 8); 


bzero() é uma função não-padronizada, e ainda por cima marcada como obsoleta mesmo nos sistemas que a suportam. Prefira usar memset().

Além disso, assumir que o tamanho do campo sin_zero é exatamente igual a 8 até é uma coisa geralmente válida (e está até na documentação). Contudo, constantes soltas no meio do código geralmente o deixam menos compreensível para quem lê (especialmente se não for familiarizado com o assunto de que trata o código). Além do mais, esse valor pode não ser sempre verdadeiro; por exemplo, num computador cujo “byte” tenha 16 bits -- e existem computadores assim -- possivelmente o tamanho do campo sin_zero seria diferente (talvez igual a 4).

Seria melhor você usar “memset(&sin.sin_zero, 0, sizeof sin.sin_zero)”. Note que eu evito a função obsoleta e não-portável, e deixo o compilador calcular o tamanho da área a ser preenchida com zeros.

    tam = sizeof (struct (sockaddr); 


Outro problema aqui. O tipo struct sockaddr é um tipo básico que nunca é realmente usado por nenhum socket real. Se fosse numa linguagem orientada a objetos, ele seria uma classe base essencialmente vazia (exceto por um campo que informa o tamanho dos demais objetos, e que tem de ser devidamente preenchido quando você criar objetos das classes derivadas). O que se usam em sockets reais são tipos como struct sockaddr_un (sockets locais no sistema de arquivos do UNIX), struct sockaddr_in (IPv4) ou struct sockaddr_in6 (IPv6).

O certo seria você fazer o seguinte.

tam = sin.sin_len = sizeof sin; 


O campo sin_len tem o mesmo offset dentro da struct sockaddr_in que o campo sock_len de struct sockaddr. Desse modo, uma função que aceita um ponteiro para struct sockaddr pode receber um ponteiro para struct sockaddr_in e ler corretamente o valor do tamanho real da estrutura.

    fcntl (s1, F_SETFL, O_NOMBLOCK);  // utilizando socket não bloqueante 


Você quis dizer O_NONBLOCK, que implica que as operações sobre o socket serão não-blocantes. Ou seja: em vez de parar para esperar a conclusão da operação, o sistema deixa o programa prosseguir, enquanto tenta realizar a operação em background. Passa a ser obrigação do programador verificar, posteriormente, se a operação que estava “em andamento” foi ou não concluída, quer com sucesso, quer com erro.

Essa linha explica o comportamento de você receber um valor de retorno em conjunto com uma sinalização de “operação em andamento” (EINPROGRESS) em algumas operações (provavelmente o connect(), já que send() não prevê esse tipo de retorno).

    }
else // tratar o erro;
CONECTAR:
retorno = (conect (s1, &sin, tam) ;


Além de corrigir o nome da função, o certo seria você fazer a conversão explícita de &sin, cujo tipo é “ponteiro para struct sockaddr_in” para “ponteiro para struct sockaddr”, que é o que a função connect() espera receber. Lembre-se de que C não é uma linguagem orientada a objetos, e essa derivação de tipos feita “na unha”, com alguns dos campos de estruturas diferentes ocupando os mesmos offsets, não o livra de -- antes o obriga a -- fazer explicitamente as devidas conversões de tipo, principalmente se você quer que a compilação seja “limpa”, sem nenhuma mensagem de erro ou de waning.

retorno = connect (s1, (struct sockaddr *)&sin, tam); 


   if   (retorno >= 0)  conec = 1;   //conectou  - às vezes não conecta logo de cara - insistir um pouco
else
{
dá um tempinho;
volta para CONECTAR;
se o tempinho for muito significa que não conseguiu conexão - trata o errr0
}


Se você quer garantia de conexão, talvez fosse melhor simplesmente não definir o socket como não-blocante.

Alternativamente, você pode usar select(), de preferência com um valor de timeout, para verificar se a conexão completou ou não, e tomar as devidas ações em cada caso.

//LOOP  de envio
if (conec == 1) // está conectado
{
if (send (s1, stringx, 172,0) >= 0) // enviou sem problemas . este envio fica dentro de um loop
else trata o erro.


O código de retorno do SEND é sempre 115 que significa "Operação em andamento"


Hummm... Acho improvável. Como eu disse acima, connect() num socket não-blocante prevê “erro” de operação em andamento. A chamada send(), não. Ou ela retorna a quantidade de bytes enviados -- e aí esse 115 seria o tamanho da mensagem que conseguiu ser enviada --, ou ela retorna -1, e a variável global errno indica algum outro tipo de erro.

O que você chama de código de retorno? O valor devolvido pela função, ou o código de erro em errno?

O envio chega normalmente no destino mesmo com código 115. Eu gostaria de receber retorno = 0, não sei
porque recebo 115. Essa é a primeira dúvida.


De novo, é preciso saber o que você está realmente observando, e como o está fazendo. Como está instrumentando o programa ou ambiente para ver o que está vendo?

Pode ser uma incrível coincidência que EINPROGRESS valha 115, e o pedaço de sua mensagem que conseguiu ser enviado sem erros tenha exatamente 115 caracteres.

O que você mostrou não é suficiente para um diagnóstico preciso.

Segunda dúvida:
Se durante o processo de envio das mensagens a rede cai, o comando SEND continua dando retorno 115 em vez de -1
Isto faz com que o programa continue rodando mesmo sem a rede de pé , o que para mim não é bom. Tenho que parar
o programa que roda nos PCs de campo.


De novo eu insisto com você para que confirme que é realmente o send(), e não o connect() que causa o estado EINPROGRESS.

Para tratar isto o que seria recomendável:

De vez em quando durante o processo de envio, testar um conect para ver se a rede continua de pé.
Seria isso o melhor ou alguem tem uma outra idéia.


São PCs de campo que se conectam ao servidor, certo? Se eles caírem ou se cair a rede deles, o único jeito de você inferir o problema é a falta da chegada de informações ao servidor centralizado vindo de determinados PCs dentro do intervalo esperado.

Minha recomendação é a mesma que já foi feita acima. Ou você deixa de usar sockets não-blocantes (o que pode não ser uma opção válida no caso de a sua aplicação ter de fazer outras coisas ao mesmo tempo), ou passa a usar algum mecanismo de sincronização desses sockets não-blocantes, para você garantir, nesses momentos, que existe uma conexão ativa, antes de tentar transmitir qualquer coisa por ela.

Para mais detalhes, só com mais detalhes da sua parte.


4. Re: Envio de Sockets. [RESOLVIDO]

M.
XProtoman

(usa Fedora)

Enviado em 03/01/2017 - 22:24h

Paulo, tentei ler atentamente cada parte do que você postou porque é muito rico em detalhes. Uma coisa me chamou a atenção a questão do PF_INET e AF_INET, o primeiro representa o protocolo e outro endereço, verifiquei no Linux e OpenBSD, realmente tem o mesmo valor.

Você acha que para sistemas como Linux e *BSDs e alguns Unix é interessante tratar diferenciadamente PF e AF? Porque se for vou ser obrigado a criar uma classe "Familia" para abrigar o valor de PF e AF. O que acha?

Atualmente tenho uma classe Soquete e Endereço que só trabalham com valores PF e não AF.

____________________
“Mas nós sabemos que a razão de a maioria estar aqui, é a nossa afinidade com a desobediência!” (Morpheus)


5. Envio de Sockets.

Marcio Donizeti Piai
PIAIXX5000

(usa Red Hat)

Enviado em 04/01/2017 - 09:34h

paulo1205 escreveu:

PIAIXX5000 escreveu:


Pessoal. Bom dia. Tentarei explicar minha dúvida .

Temos uns 11 PCs no campo que rodam Linux (Gentoo versão antiga). Cada um desses PCs tem IP diferenciado. Eles enviam sockets para um PC na Central (com Windows 7 - aplicação Delphi 5) para um mesmo IP - classe 192.168.1.... em portas diferenciadas a intervalos curtos.

Resumo do código: s1 é o descritor do socket.


Nota antes de começar: o código abaixo tem vários erros, que suponho serem de transcrição. Do jeito como está, ele não compilaria.

if  ((s1 = socket(PF_INET, SOCK_STREAM, 0))  >= 0)    // abrir o socket
{
sin.sin_family = PF_INNET; // sin é a estrutura do socket


Aqui está um erro (e não me refiro à duplicidade do “N” na hora de transcrever “PF_INET”).

Ao usar a função socket(), o primeiro argumento é uma constante cujo nome começa com “PF_” (por exemplo: PF_INET ou PF_LOCAL), mas ao atribuir à estrutura do endereço, você deve usar constantes cujos nomes começam com “AF_” (por exemplo: AF_INET ou AF_LOCAL).

Na prática, no Linux todas as constantes PF_xxx têm os mesmos valores das constantes AF_xxx, de modo que muita gente usa as duas indistintamente. Mas é teoricamente possível que uma mesma família de protocolos (primeiro argumento de socket()) possa ser usada com diferentes famílias de endereços (campo da estrutura que representa o endereço), e vice-versa. Mesmo que essa possibilidade teórica nunca tenha sido usada na prática, um programa preocupado com correção estrita deveria levar em conta a natureza diferente das informações representadas.

Então, a maneira certa de fazer seria dizer o seguinte.

sin.sin_family=AF_INET; 


[code] sin.sin_port = htons (portax); // cada PC no campo manda p/ um mesmo IP em portas distintas
inet_aton (ipx, & (sin.sin_addr));


Você não está testando se a função retorna erro. Um programa seguro deveria se preocupar com isso.

    bzero (&(sin.sin_zero ), 8); 


bzero() é uma função não-padronizada, e ainda por cima marcada como obsoleta mesmo nos sistemas que a suportam. Prefira usar memset().

Além disso, assumir que o tamanho do campo sin_zero é exatamente igual a 8 até é uma coisa geralmente válida (e está até na documentação). Contudo, constantes soltas no meio do código geralmente o deixam menos compreensível para quem lê (especialmente se não for familiarizado com o assunto de que trata o código). Além do mais, esse valor pode não ser sempre verdadeiro; por exemplo, num computador cujo “byte” tenha 16 bits -- e existem computadores assim -- possivelmente o tamanho do campo sin_zero seria diferente (talvez igual a 4).

Seria melhor você usar “memset(&sin.sin_zero, 0, sizeof sin.sin_zero)”. Note que eu evito a função obsoleta e não-portável, e deixo o compilador calcular o tamanho da área a ser preenchida com zeros.

    tam = sizeof (struct (sockaddr); 


Outro problema aqui. O tipo struct sockaddr é um tipo básico que nunca é realmente usado por nenhum socket real. Se fosse numa linguagem orientada a objetos, ele seria uma classe base essencialmente vazia (exceto por um campo que informa o tamanho dos demais objetos, e que tem de ser devidamente preenchido quando você criar objetos das classes derivadas). O que se usam em sockets reais são tipos como struct sockaddr_un (sockets locais no sistema de arquivos do UNIX), struct sockaddr_in (IPv4) ou struct sockaddr_in6 (IPv6).

O certo seria você fazer o seguinte.

tam = sin.sin_len = sizeof sin; 


O campo sin_len tem o mesmo offset dentro da struct sockaddr_in que o campo sock_len de struct sockaddr. Desse modo, uma função que aceita um ponteiro para struct sockaddr pode receber um ponteiro para struct sockaddr_in e ler corretamente o valor do tamanho real da estrutura.

    fcntl (s1, F_SETFL, O_NOMBLOCK);  // utilizando socket não bloqueante 


Você quis dizer O_NONBLOCK, que implica que as operações sobre o socket serão não-blocantes. Ou seja: em vez de parar para esperar a conclusão da operação, o sistema deixa o programa prosseguir, enquanto tenta realizar a operação em background. Passa a ser obrigação do programador verificar, posteriormente, se a operação que estava “em andamento” foi ou não concluída, quer com sucesso, quer com erro.

Essa linha explica o comportamento de você receber um valor de retorno em conjunto com uma sinalização de “operação em andamento” (EINPROGRESS) em algumas operações (provavelmente o connect(), já que send() não prevê esse tipo de retorno).

    }
else // tratar o erro;
CONECTAR:
retorno = (conect (s1, &sin, tam) ;


Além de corrigir o nome da função, o certo seria você fazer a conversão explícita de &sin, cujo tipo é “ponteiro para struct sockaddr_in” para “ponteiro para struct sockaddr”, que é o que a função connect() espera receber. Lembre-se de que C não é uma linguagem orientada a objetos, e essa derivação de tipos feita “na unha”, com alguns dos campos de estruturas diferentes ocupando os mesmos offsets, não o livra de -- antes o obriga a -- fazer explicitamente as devidas conversões de tipo, principalmente se você quer que a compilação seja “limpa”, sem nenhuma mensagem de erro ou de waning.

retorno = connect (s1, (struct sockaddr *)&sin, tam); 


   if   (retorno >= 0)  conec = 1;   //conectou  - às vezes não conecta logo de cara - insistir um pouco
else
{
dá um tempinho;
volta para CONECTAR;
se o tempinho for muito significa que não conseguiu conexão - trata o errr0
}


Se você quer garantia de conexão, talvez fosse melhor simplesmente não definir o socket como não-blocante.

Alternativamente, você pode usar select(), de preferência com um valor de timeout, para verificar se a conexão completou ou não, e tomar as devidas ações em cada caso.

//LOOP  de envio
if (conec == 1) // está conectado
{
if (send (s1, stringx, 172,0) >= 0) // enviou sem problemas . este envio fica dentro de um loop
else trata o erro.


O código de retorno do SEND é sempre 115 que significa "Operação em andamento"


Hummm... Acho improvável. Como eu disse acima, connect() num socket não-blocante prevê “erro” de operação em andamento. A chamada send(), não. Ou ela retorna a quantidade de bytes enviados -- e aí esse 115 seria o tamanho da mensagem que conseguiu ser enviada --, ou ela retorna -1, e a variável global errno indica algum outro tipo de erro.

O que você chama de código de retorno? O valor devolvido pela função, ou o código de erro em errno?

O envio chega normalmente no destino mesmo com código 115. Eu gostaria de receber retorno = 0, não sei
porque recebo 115. Essa é a primeira dúvida.


De novo, é preciso saber o que você está realmente observando, e como o está fazendo. Como está instrumentando o programa ou ambiente para ver o que está vendo?

Pode ser uma incrível coincidência que EINPROGRESS valha 115, e o pedaço de sua mensagem que conseguiu ser enviado sem erros tenha exatamente 115 caracteres.

O que você mostrou não é suficiente para um diagnóstico preciso.

Segunda dúvida:
Se durante o processo de envio das mensagens a rede cai, o comando SEND continua dando retorno 115 em vez de -1
Isto faz com que o programa continue rodando mesmo sem a rede de pé , o que para mim não é bom. Tenho que parar
o programa que roda nos PCs de campo.


De novo eu insisto com você para que confirme que é realmente o send(), e não o connect() que causa o estado EINPROGRESS.

Para tratar isto o que seria recomendável:

De vez em quando durante o processo de envio, testar um conect para ver se a rede continua de pé.
Seria isso o melhor ou alguem tem uma outra idéia.


São PCs de campo que se conectam ao servidor, certo? Se eles caírem ou se cair a rede deles, o único jeito de você inferir o problema é a falta da chegada de informações ao servidor centralizado vindo de determinados PCs dentro do intervalo esperado.

Minha recomendação é a mesma que já foi feita acima. Ou você deixa de usar sockets não-blocantes (o que pode não ser uma opção válida no caso de a sua aplicação ter de fazer outras coisas ao mesmo tempo), ou passa a usar algum mecanismo de sincronização desses sockets não-blocantes, para você garantir, nesses momentos, que existe uma conexão ativa, antes de tentar transmitir qualquer coisa por ela.

Para mais detalhes, só com mais detalhes da sua parte.[/quote]

----------------------------------------------------------------------------------------------
Paulo1205. Agradeço a tua atenção e obrigado pelas dicas.

O código compila sem warnnigs. A dica do bzero,8 foi boa. Eu utilizava PCs de 32 bits e agora estou utilizando PCs de 64 bits.

Vou voltar a utilizar socket bloqueante para esta tarefa, mesmo porque este é um programa "satélite", e pode ficar esperando.
O programa principal roda normalmente mesmo estando o programa de envio de Socket parado.
O código de retorno da função send é 172 (tamanho da string). 115 é o código de erro. Vou utilizar socket bloqueante para
acabar com isso.

Aproveitando teu valioso conhecimento, tenho uma dúvida do lado do Servidor.
O programa do servidor nada transmite para a Central. Fica aguardando a Central transmitir dados para ele.
Aguardo com a função Listen e Accept e chegando algum dado leio com Recv.
A função Listen tem um parâmetro que indica o numero de strings sockets na fila. O processo é como abaixo. Não vou entrar em detalhes do "C" (está mais ou menos igual ao programa cliente).

Processo: (não estou comentando os tratamentos de erros)

LOOP:
1-Abrir Socket

2-Bind

3-Liten (5)

4-Acept

5-Recv

Após receber dado com sucesso, trato e:

6-Fecha Acept
7-Fecha Socket.

É este o processo com tudo bloqueante.
Na tua opinião, está coerente?
Ah! Em caso de erro no Acept deve-se fechá-lo e voltar para Listen ou voltar para o Loop?

Desde já agradeço.






6. Envio de Sockets.

Marcio Donizeti Piai
PIAIXX5000

(usa Red Hat)

Enviado em 04/01/2017 - 10:03h

Paulo1205. Obrigado pelas dicas . Vou corrigir AF_ bzero.

Vou voltar a utilizar socket bloqueante.

Aproveitando teu valioso conhecimento, tenho uma dúvida do lado do Servidor.
O programa do servidor nada transmite para a Central. Fica aguardando a Central transmitir dados para ele.
Aguardo com a função Listen e Accept e chegando algum dado leio com Recv.
A função Listen tem um parâmetro que indica o numero de strings sockets na fila. O processo é como abaixo. Não vou entrar em detalhes do "C" (está mais ou menos igual ao programa cliente).

Processo: (não estou comentando os tratamentos de erros)

LOOP:
1-Abrir Socket

2-Bind

3-Liten (5)

4-Acept

5-Recv

Após receber dado com sucesso, trato e:

6-Fecha Acept
7-Fecha Socket.

É este o processo com tudo bloqueante.
Na tua opinião, está coerente?
Ah! Em caso de erro no Acept deve-se fechá-lo e voltar para Listen ou voltar para o Loop?

Desde já agradeço.



7. Resolvido

Marcio Donizeti Piai
PIAIXX5000

(usa Red Hat)

Enviado em 05/01/2017 - 09:21h

paulo1205 escreveu:

XProtoman escreveu:

Você acha que para sistemas como Linux e *BSDs e alguns Unix é interessante tratar diferenciadamente PF e AF? Porque se for vou ser obrigado a criar uma classe "Familia" para abrigar o valor de PF e AF. O que acha?


Eu acho que é importante que o programa esteja correto de todos os pontos de vista possíveis, inclusive semântico e estético.

Vou dar um exemplo esdrúxulo ao extremo. Suponha que eu sei que AF_INET e PF_INET correspondem ao valor inteiro 2 (o que, por sinal, é verdade nos BSDs, no Linux, no SOlaris e no AIX -- e talvez o seja até no Windows, se bobear). Isso significa que eu posso livremente deixar de usar as constantes simbólicas, e passar a usar números? Ou que eu poderia usar outras constantes simbólicas que também tenham o valor 2 (como, por exemplo, O_RDWR, de <fcntl.h>)?

Veja as quatro linhas de código abaixo, que compilam igualmente sem problemas em qualquer sistema (pelo menos POSIX) e produzem o mesmo efeito final, e diga qual a que você esperaria encontrar para indicar que a função chamada tem um argumento que representa uma família de protocolos (protocol family, em Inglês), e leve em conta que eu só faço a brincadeira com o primeiro argumento (os outros dois permitiriam zoações semelhantes).

int sock=socket(2, SOCK_STREAM, 0); 

int sock=socket(PF_INET, SOCK_STREAM, 0); 

int sock=socket(O_RDWR, SOCK_STREAM, 0); 

int sock=socket(AF_INET, SOCK_STREAM, 0); 


No outro lado da moeda, você tem uma estrutura que tem um campo que descreve uma família de endereços (no Inglês, address family). Qual das alternativas abaixo, que também compilam sem erro nem diagnóstico de impropriade pelo compilador, parece testar isso da melhor maneira (considere que peer_addr é do tipo struct sockaddr, e cujo valor foi preenchido através de uma chamada a accept(), que utilizou seu endereço como segundo argumento)?

if(peer_addr.sa_family==2 && ntohs(*((struct sockaddr_in *)&peer_addr)->sin_port)<1024){ /* ... */ } 

if(peer_addr.sa_family==PF_INET && ntohs(*((struct sockaddr_in *)&peer_addr)->sin_port)<1024){ /* ... */ } 

if(peer_addr.sa_family==O_RDWR && ntohs(*((struct sockaddr_in *)&peer_addr)->sin_port)<1024){ /* ... */ } 

if(peer_addr.sa_family==AF_INET && ntohs(*((struct sockaddr_in *)&peer_addr)->sin_port)<1024){ /* ... */ } 


Atualmente tenho uma classe Soquete e Endereço que só trabalham com valores PF e não AF.


Em muitos fóruns, eu vejo a recomendação no sentido contrário (i.e. usar sempre AF_*, seguindo o exemplo dos livros do W. Richard Stevens, autor, já falecido, dos excelentes e aclamados Advanced Programming in the UNIX Environment e UNIX Network Programming).

EU tento entender a coisa à luz do momento histórico. A interface de sockets que inspirou tudo isso que temos hoje foi publicada em 1982, junto com o 4.2BSD, o que significa que deve ter começado a ser criada alguns anos antes disso. Nessa época, a linguagem de implementação do UNIX era um C K&R, que não possuía enum nem void, e era extremamente leniente com conversão entre ponteiros (e mesmo entre ponteiros e inteiros). Os recursos dessa linguagem eram pouco mais do que o de um assembly.

E, no entanto, existe um quê de orientação a objetos com herança no projeto dos sockets BSD, mesmo implementado nessa linguagem primitiva: as mesmas funções de envio e recepção de dados podem ser usadas para sockets em domínios distintos e com protocolos distintos. Também são comuns as funções de sincronização e controle. E existe até uma certa relação entre sockets e descritores nativos de arquivos comuns, que reaproveitam chamadas como read(), write() e até mesmo close() e fcntl().

A chamada socket() funciona como construtor do objeto base, que é “herdado” pelos objetos finais. Uma parte da construção tem de ser feita manualmente pelo programador, ao criar uma estrutura de endereço de uma família compatível com o objeto base, e depois associar ao socket tal endereço dessa família através de uma chamada a bind() ou a connect(), terminando a construção do objeto usado para comunicação. A chamada accept() também tem algo de construtor (e, na gíria de C++, construtor virtual, ainda por cima), pois faz um clone do socket passivo com propriedades, ainda que com alguns valores diferentes, do socket passivo original.

Mas isso tudo foi feito em C, e num C muito primitivo, e para rodar em máquinas com recursos ridiculamente limitados, se comparadas às de hoje (por exemplo: 256kB, ou até menos, de RAM total). Em lugar de ter diversos ponteiros dentro de estruturas (que é como funcionam as classes abstratas e funções-membros virtuais de classes do C++), o socket tinha um punhado de valores inteiros para representar o próprio identificador do socket (descritor de arquivo), o domínio de comunicação, o protocolo usado dentro desse domínio e o modo de endereçamento. Nunca vi o código, mas a julgar pela forma da API, uma função como send() deve ter alguns blocos de switches ou um emaranhado de ifs, que seleciona o comportamento da função a partir dos valores dessas constantes. C++ faria através de despacho dinâmico, com funções virtuais especializadas para cada tipo de objeto.

De novo sem ter olhado código, eu acho que a ideia inicial de distinguir entre o domínio da comunicação (ou família de protocolos) e a família de endereços subjacentes à comunicação com tais protocolos era boa, e eu não sei o motivo de, no fim das contas, essas duas coisas terem se misturado e confundido. Com olhos de 2017, não de 1982, faria um bocado de sentido que o domínio PF_INET pudesse suportar tanto endereços IPv4 (família de endereços AF_INET), quanto IPv6 (família AF_INET6), sem a necessidade de um domínio separado PF_INET6, ainda mais quando quase todos os protocolos de transporte e de sinalização são os mesmos (i.e. TCP, UDP, ICMP, IGMP etc.).

Aliás, essa confusão, até onde sei, pode até nem mesmo ser necessariamente verdadeira. Não conheço nenhuma referência que oficialmente proíba você tentar associar um endereço do tipo AF_INET6 a um socket criado com PF_INET, e me arrisco a opinar que uma implementação que resolvesse fazê-lo possivelmente não violaria nenhuma regra explícita da API.





8. Envio de Sockets.

Marcio Donizeti Piai
PIAIXX5000

(usa Red Hat)

Enviado em 05/01/2017 - 09:22h

Resolvido


9. Re: Envio de Sockets. [RESOLVIDO]

M.
XProtoman

(usa Fedora)

Enviado em 10/01/2017 - 18:00h

Obrigado mais uma vez paulo1205,

Estava implementando a classe "Familia" para resolver esse problema de PF_ e AF_, estou feliz em saber que esse é o caminho mais correto.

____________________
“Mas nós sabemos que a razão de a maioria estar aqui, é a nossa afinidade com a desobediência!” (Morpheus)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts