Newsgroup: it.comp.os.linux.development
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

 localhost?
Beh, finche' client e server girano sulla stessa macchina il byte order
e` lo stesso, e l'errore - ammesso che sia quello - non si manifesta...
Ciao
 David
-- 
<d.santinoli presso mi. flashnet. it>

 
Non so se il problema sia proprio questo, ma non controllare il valore
restituito da read() è un'errore, in quanto non è detto che i dati
effettivamente ricevuti siano sufficienti a valorizzare interamente la
struct msg (v. read(2)). E' possibile che servano più letture, soprattutto
quando i dati non provengono da localhost.
Max

 network order con htonl() / htons().
Scusa l'ignoranza ma non sapevo di poter decidere anche questa modalita`
di trasferimento.
Come fai a passare dati in binario o in ascii con la write? Voglio dire
devi per forza far precedere/seguire la write/read da qualche funzione
di conversione?
Grazie e ciao


 quando i dati non provengono da localhost.
In realta` nel codice vero controllo se la read da` 0 o <0 e` questo che
intendi?
Io pensavo che bastasse usare sizeof con la write/read per pararsi dal
problema.
Come gestire la read su piu` letture?


 Come gestire la read su piu` letture?
Ad esempio, usando la tecnica che ti suggerisce Simone Piccardi nel suo
post, magari integrandola opportunamente con select(2).
Max

 di conversione?
Che i dati siano ASCII o binari alla write() non fa ne' caldo ne'
freddo: trasmette quello che trova nel buffer che le passi. Sta a te
riempire il buffer con dati di un tipo o dell'altro; per "stampare" in
ASCII puoi usare sprintf().
Ciao
 David
[1] si tratta in realta` dell'ordinamento big endian.
-- 
<d.santinoli presso mi . flashnet . it>

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?


 garantisce di passare tutto?
Dipende dai campi. Se ti va male passi dei puntatori che per la macchina
che riceve non hanno alcun significato. Oppure si rovescia il byte
order... Insomma: non trasmettere *mai* le strutture binarie!! (ne
abbiamo già parlato...)


 abbiamo già parlato...)
ok, messaggio recepito! Questa parte e` meglio che me la studi per
benino...
