Newsgroup: it.comp.lang.c
Titolo: Passaggio struct con socket (lungo)

Scusate se insisto coi miei problemi sulla programmazione client-server
mediante socket unix in dominio tcp-ip ma sto incontrando parecchi
problemini...
Lo strano "fenomeno" che ho rilevato e` che praticamente la mia
applicazione distribuita gira solo sulla stessa macchina e toppa se
lancio server e client da macchine diverse.
Mi spiego meglio, lancio il server su una macchina, a video compare il
numero di porta su cui e` in attesa il server, mi sposto sull'altra e
batto client nome-macchina-server #porta-server, il client si connette
regolarmente, il server lo accetta ma quando a sua volta cerca di
connettersi al client (e` richiesto dalle specifiche) per chiudere
l'anello fallisce proprio sulla connect con errore:
"Connection refused"
I dati per fare la connessione arrivano al server dal client con
read/write, cioe` faccio riempiere al client una struttura di tipo
sock_addr_in coi dati della socket passiva aperta che poi viene passata
con la write al server che a sua volta li legge dalla struttura e li
ricopia nella struttura sock_addr_in "vera" della socket di connessione
che sta aprendo per connettersi.
Ho controllato il codice piu` volte senza trovare soluzioni. Tenete
conto poi che lasciando tutto localhost funziona a meraviglia.
Avete idee?
Sto commettendo qualche errore madornale senza accorgermene?
Puo` essere che ci siano errori gravi mascherati fintanto si vada in
localhost?
Visto che le macchine su cui faccio la prova hanno nome noto ho provato
a far scrivere dal server stesso l'indirizzo della macchina cosiccome fa
il client ed ha funzionato. Cioe`:
prendendosi solo i campi sin_family e sin_port dalla struttura passata
dal client mediante write e unendo a questi l'indirizzo ricavato dal
sistema (gethostbyname) il server riesce a connettersi tranquillamente
al client. Se invece prende anche l'indirizzo leggendolo dalla struttura
compilata dal client toppa. Perche`?
Non e` sufficiente passargli il campo sin_addr.s_addr? Ho provato anche
a passare sin_addr soltanto o tutti e 2 ma non e` cambiato nulla.
Forse che, lato client, devo fare qualche operazione preventiva sulla
struttura sock_addr_in che poi voglio passare al server tramite write?
Ecco i pezzi di codice incriminati (msg e` la struttura che contiene a
sua volta una struttura di tipo sock_addr_in):
1)
CODICE CLIENT:
  ...
  /*crea socket per ricevere da anello*/
  sock_p = socket(AF_INET, SOCK_STREAM, 0);
  s_p.sin_family = AF_INET;           /*protocol suite*/
  s_p.sin_addr.s_addr = INADDR_ANY;   /*IP Address*/
  s_p.sin_port = 0;                    /*port*/
  lungh_s_p = sizeof s_p;
  bind(sock_p, &s_p, lungh_s_p)
  listen(sock_p, 5);
  /*crea la socket per connettersi al server*/
  sock_a = socket(AF_INET, SOCK_STREAM, 0);
  /*prende come host l'argomento e ritorna il puntatore struttura
indirizzo host*/
  hp_a = gethostbyname (argv[1]);
  if(hp_a == 0)
  {
    fprintf(stderr, "%s:unknown host\n", argv[1]);
    exit(2);
  }
  (void) bcopy((char*)hp_a->h_addr, (char*)&s_a.sin_addr,
hp_a->h_length);
  s_a.sin_family = AF_INET;             /*protocol suite*/
  s_a.sin_port =htons(atoi(argv[2]));   /*port*/
  lungh_s_a = sizeof s_a;
  /*connessione al server*/
  if(connect(sock_a, &s_a, lungh_s_a)<0)
  {
    printf("errore di connessione\n");
    exit(1);
  }
  /*scrive indirizzo nuova porta dove deve trasmettere*/
  /* il server*/
  msg.codice="X";
  msg.fsin.sin_family=s_p.sin_family;
  msg.fsin.sin_addr.s_addr=s_p.sin_addr.s_addr;
  msg.fsin.sin_port=s_p.sin_port;
  if(write(sock_a, &msg, sizeof msg)<0)
    printf("errore di write\n");
  ...
CODICE SERVER;
        ...
        read(fd, &msg, sizeof msg);
        switch(msg.codice)
        ...
        case "X":/*ricezione dati da nuovo client*/
          /*crea socket per trasmettere messaggi sull'anello*/
          sock_a = socket(AF_INET, SOCK_STREAM, 0);
          /*inizializza struttura attiva server*/
          s_a.sin_family=msg.fsin.sin_family;
          s_a.sin_addr.s_addr=msg.fsin.sin_addr.s_addr;
          s_a.sin_port=msg.fsin.sin_port;
          lungh_s_a = sizeof s_a;
          /*connessione al client*/
          if(connect(sock_a, &s_a, lungh_s_a)<0)
          {
            printf("errore di connessione\n");
            exit(1);
          }
          ...
Questo fallisce sulla connect del server verso il client!!!
A parte localhost
2)
CODICE CLIENT resta lo stesso
CODICE SERVER:
        read(fd, &msg, sizeof msg);
        switch(msg.codice)
        ...
        case "X":/*ricezione dati da nuovo client*/
          /*crea socket per trasmettere messaggi sull'anello*/
          sock_a = socket(AF_INET, SOCK_STREAM, 0);
         /*prende come host il nome di macchina (NOTO!)*/
         hp_a = gethostbyname ("nome_host_server");
         if(hp_a == 0)
         {
           fprintf(stderr, "unknown host\n");
           exit(2);
         }
         (void) bcopy((char*)hp_a->h_addr, (char*)&s_a.sin_addr,
hp_a->h_length);
          /*inizializza resto della struttura attiva server*/
          s_a.sin_family=msg.fsin.sin_family;
          s_a.sin_port=msg.fsin.sin_port;
          lungh_s_a = sizeof s_a;
          /*connessione al client*/
          if(connect(sock_a, &s_a, lungh_s_a)<0)
          {
            printf("errore di connessione\n");
            exit(1);
          }
          ...
Questo funziona!!!
Saluti e grazie
-- 
Giuseppe R.
E-mail: mailto:
giuseppe...@SPAMnetit.alcatel.it
--
Posted from 
ns.alcanet.it
 [194.243.74.35] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG

         case "X":/*ricezione dati da nuovo client*/
            di nuovo errato:punto 4:
            i casini aumentano, se oltre ad utilizzare strutture, la dimensione dei campi tra i vari sistemi e' diversa.
            per esempio long a 64 bits su un sistema e long a 32 su un'altro
punto 5:
            i casini crescono esponenzialmente se un sistema e' big-endian e l' altro little-endian.
punto 6:
            La causa del malfunzionamento e' la seguente:
1)
CODICE CLIENT:
  ...
  /*crea socket per ricevere da anello*/
  sock_p = socket(AF_INET, SOCK_STREAM, 0);
  s_p.sin_addr.s_addr = INADDR_ANY;   /*IP Address*/----> errato
  s_p.sin_addr.s_addr = [ tuo ip] -->ESATTO.
specificando qui INADDR_ANY dici : indirizzo 0.0.0.0 e non l'ip dell' interfaccia che vuoi utilizzare,ossia leghi la socket con la bind a tutte le interfaccie del
sistema locale.(infatti funziona solo su localhost)
quindi passandolo al tuo server lui cerchera' di connettersi a :  0.0.0.0 porta 0, e non esistendo sul tuo server tale socket ti da indietro un connection refused.
Ivan Molella
--
Posted from 
mta09-acc.tin.it
 [212.216.176.40] 


 quindi passandolo al tuo server lui cerchera' di connettersi a :  0.0.0.0 porta 0, e non esistendo sul tuo server tale socket ti da indietro un connection refused.
Non mi torna.
Io "apro" la socket con socket(...), poi faccio quegli assegnamenti di
rito che tu mi contesti e poi faccio la bind(). Non basta quest'ultima
per compilare la struttura sock_addr_in che compete a quella socket? Mi
pare di ricordare che dopo la bind() la socket e` univocamente associata
a host-ipaddress e quindi credevo che a quel punto bastasse passare i
campi della struttura sock_addr_in per permettere a un punto remoto di
connettersi. Sbaglio?
-- 
Giuseppe R.
E-mail: mailto:
giuseppe...@SPAMnetit.alcatel.it
--
Posted from 
ns.alcanet.it
 [194.243.74.35] 


 connettersi. Sbaglio?
Allora.
Tu esegui la bind della socket con INADDR_ANY impostato.
Cosi' facendo specifichi l'indirizzo 0.0.0.0 ossia tutte le interfaccie di rete dell' host.
Se il tuo host ha 2 interfacce di rete con 2 ip address esempio: 10.10.20.5 e 10.10.20.6
la socket si mette in listen per entrambi gli indirizzi.
Poi passando INADDR_ANY come indirizzo alla struttura dici al server:
collegati a 0.0.0.0 porta 0.
Lui ovviamente non trova tale socket perche' questa porta e' in listen sul client.
Per capirci e' come se facessi dal server telnet 0.0.0.0 e invece volevi collegarti al tuo host client.
In fine specificando la porta 0 dici alla bind di scegliere lei una porta per il tuo server,quindi il tuo client
non saprebbe neanche a che porta connettersi,ti consiglio:
  msg.codice="X";
  msg.fsin.sin_family=s_p.sin_family;
  msg.fsin.sin_addr.s_addr=htonl(>IP CLIENT<)
  msg.fsin.sin_port=htons(>PORT != 0< )
Prova, se non funziona ritiro tutto.
Ivan Molella

Se a qualcuno interessa ho risolto il problema per tentativi ed aiuti.
Ad essere sincero non ho capito fino in fondo perche` adesso si chiude
l'anello ma alla base c'era effettivamente il passaggio della struttura
sock_addr_in da un processo all'altro.
Il ragionamento e` stato quello di duplicare quello che viene fatto dal
client per acquisire i dati dal server. Con la differenza che, per il
client, il server e` noto e quindi sa come recuperare i suoi dati, per
il server il client non e` noto e per connettersi su di lui deve per
forza entrare in contatto dei suoi dati, in altre parole e` il client
stesso che DEVE passarglieli in qualche modo.
Definita la struttura:
  struct hostent *hp_b;      /*struttura per mettere a disposizione
dati*/
e il buffer:
  char localhost[33];
e creata la socket passiva da cui il client riceve:
  sock_p = socket(AF_INET, SOCK_STREAM, 0);
  if (sock_p <0...)
  s_p.sin_family = AF_INET;           /*protocol suite*/
  s_p.sin_addr.s_addr = INADDR_ANY;   /*IP Address*/
  s_p.sin_port = 0;                    /*port*/
  lungh_s_p = sizeof s_p;
  if (bind(sock_p, &s_p, lungh_s_p)<0...)
Ho aggiunto la seguente parte:
  gethostname(localhost, 32);
  hp_b = gethostbyname (localhost);
  if(hp_b == 0)
  {
    fprintf(stderr, "localhost: unknown host\n");
    exit(2);
  }
  (void) bcopy((char*)hp_b->h_addr, (char*)&s_p.sin_addr,
hp_b->h_length);
Poi il client prosegue normalmente, apre una socket attiva, con questa
si connette al server, viene accettato, e gli passa i dati della sua
socket passiva mediante write.
  if(write(sock_a, &struct-x, sizeof struct-x)<0...)
Dove naturalmente la struct-x contiene la struttura di tipo sock_addr_in
riempita poc'anzi (s_p).
Adesso funziona anche se l'ho testata poco. Prima dell'aggiunta della
gethostname, gethostbyname non c'era verso. Come che queste due funzioni
siano NECESSARIE per riempire a dovere la struttura sock_addr_in. La
cosa non mi soddisfa del tutto e infatti vi chiedo un parere. E` tutto
corretto o e` un miraggio? Oppure sto mascherando l'errore con un altro
errore?
---
Domanda d'appendice:
non mi e` chiara la differenza dei campi .sin_addr e .sin_addr.s_addr
all'interno della struttura sock_addr_in, voglio dire sono ridondanti o
servono entrambe per definire la socket? Io passo il primo dei due e la
cosa sembra funzionare.
La domanda puo` essere generalizzata, se una struttura x contiene una
sottostruttura y oltre ad altri campi, il passaggio con socket di x mi
garantisce di passare tutto?


 Io passo il primo dei due e la cosa sembra funzionare.
  sin_addr e` una specie di record variante: contiene l'indirizzo IP del
peer cui e` collegata la socket in tre formati diversi: quattro char,
due short oppure un long (tutti senza segno), s_addr e` una macro che
maschera appunto il campo unsigned long.
  La "cosa sembra funzionare", ovviamente, se la funzione si attende un
parametro di tipo struct sin_addr... ;)
	Alla prossima,
		Marco