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.