Problemi applicazione client/server - Visual C++

di il
3 risposte

Problemi applicazione client/server - Visual C++

Ciao a tutti,
ho sviluppato un'applicazione client/server per uno scambio di stringhe via internet con un amico. Lui lancia il client sul suo PC ed io avvio il server sul mio. Ho programmato tutto con VisualC++ 6.0.
Tutto sembra funzionare bene, ma quando spengo il server (chiudo brutalmente l'applicazione) al riavvio non riusciamo più a comunicare ed il programma si pianta, dandomi un errore 10053 che riporto: A connection abort was caused internal to your host machine. The software caused a connection abort because there is no space on the socket's queue and the socket cannot receive further connections.
Non è che il server vada proprio in crash, ma sembra che dia qualche probelma col socket.
Qualcuno mi può aiutare?

Grazie.

3 Risposte

  • Re: Problemi applicazione client/server - Visual C++

    I socket non chiusi correttamente possono non rispondere per un po' di tempo (retirement), che su win95/98 è impostato a 5 minuti.

    Tuttavia mi sembra che parte dell'applicativo client o host rimane in memoria e il socket non viene rilasciato. Inoltre, quando accetti una connessione (con accept), dovresti reindirizzare la chiamata a un nuovo socket e lasciare il primo in ascolto. Il nuovo socket elabora i dati finché c'è connessione e poi sarà abbandonato, o retired, se non chiuso per esplicito. Questo dovrebbe lavorare sulla porta 0, per evitare effetti collaterali, quali delle porte non (più) utilizzabili.

    In altre parole, con il primo socket sempre in ascolto, il server sarà in grado di accettare più chiamate (da diversi client) in modo trasparente, mentre l'accettazione di una chiamata al (primo) socket in ascolto, avrai una sola connessione a disposizione, finché non viene chiusa completamente e riaperta.

    La sequenza di ascolto, schematicamente è questa:

    - crea socket
    - imposta la porta, es. 1234
    - ascolta, finché riceve una richiesta di connessione
    - crea nuovo socket
    - imposta la porta a 0
    - imposta linger, se il timeout predefinito di 2 secondi è troppo poco
    - imposta altre caratteristiche, se desiderate (blocking, callback ecc)
    - accetta la chiamata con il nuovo socket

    Nota: per rifiutare una chiamata, accettala comunque con il nuovo socket e chiudi il socket immediatamante dopo aver ricevuto la connessione. Questo è un modo pulito per dire: non accetto altre chiamate. Il client sa subito che qualcosa non va, e il socket di ascolto non viene sovraccaricato con dei timeout.

    Giovanni
    ---
    http://www.y2ksw.com/vbulletin
  • Re: Problemi applicazione client/server - Visual C++

    Grazie mille! Faccio subito la prova.
    Mi spieghi ancora due cosette?
    1 - Uso WinXP: il tuo discorso vale lo stesso?
    2 - Cos'è il linger? E' importante impostarlo?
    3 - Per chiudere un socket basta fare mysocket.Close() o c'è bisogno di una procedura più complessa?

    Ti allego la tua soluzione come l'ho interpretata:

    Create:
    m_sListenSocket.Create(20000,SOCK_STREAM,
    FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT |
    FD_CONNECT | FD_CLOSE);

    m_sListenSocket.Listen();

    Accept:
    m_sExchangeSocket.Create(0,SOCK_STREAM,
    FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT |
    FD_CONNECT | FD_CLOSE);

    m_sExchangeSocket.Accept(m_sConnectSocket);


    E' corretto? In questo modo rilascio il listen socket in ascolto?

    Ancora Grazie,

    Antonio

    P.S.: Presto proverò ad estendere quest'applicazione per gestire un multithreading: posso contare su altri suggerimenti eventuali? ;-)
  • Re: Problemi applicazione client/server - Visual C++

    Ahhh, se tutti fossero così bravi!!!

    Esattamente così si fa!

    Il linger riguarda il periodo di sconnessione e cosa deve fare il socket per chiudere. Se il timeout è troppo breve, o zero, la sconnessione avviene immediatamente, ed eventuali dati in fase di trasmissione sono semplicemente persi. Con un linger normale o alto, si permette al client di ricevere tutti i dati come se fosse ancora aperta la connessione. Il valore di 2 secondi normalmente è sufficiente, ma su modem lenti, o linee intasate, parte dei dati potrebbe andare perso, che ha diversi effetti collaterali, soprattutto per i trasferimenti binari, come FTP.

    Un effetto è che si riceve un file non completo, che è fastidioso, perché si deve riprendere lo scaricamento, sperando che vada a buon fine. Alcuni provider, come il mio, troncano la connessione in questo modo, e file di grossi dimensioni (parlo di 10-20 MB) sono semplicemente da evitare.

    Per pagine web non è così grave, perché i tag di chiusura pagina sono opzionali, e di regola si vede il contenuto anche se manca un pezzo.

    Un linger timeout di 5 secondi è molto user-friendly, anche per modem da 9600 bps o meno, ma richiede maggiori risorse, e siti molto battuti potrebbero rallentarsi parecchio, però solo se si tratta di migliaia di utenti contemporanei. Quindi c'è da fare una scelta e trovare una buona via di mezzo.

    Per trovarla, normalmente si misura il traffico e/o la velocità di trasmissione e si aggiusta il linger al momento della chiusura con la funzione 'select', credo. Ma conosco ben pochi che si fanno la fatica di fare i conteggio dei dati in uscita e calcolare l'aggiustamento. Sono due righe di codice, e se lo facessero tutti, l'Internet sarebbe davvero bello da usare. Mi sa, molti non sanno neanche come si fa...

    Ed ecco come:

    la procedura che manda via i dati, legge il timer (GetTickCount) e salva il valore sullo stack. Poi scrive, e quando ha finito, legge di nuovo il tempo trascorso, e sottrae da questo il valore di prima. Risultato: millisecondi per la quantità di dati inviati. Poi calcoli il rapporto per 1024 byte e lo metti su una variabile globalmente accessibile, in quanto rispecchia la velocità attuale di invio. Non succede nulla di grave se questo viene sovrasscritto da altri thread nello stesso tempo, sarà +/- uguale.

    Alla chiusura del socket leggi quanti dati sono ancora in coda, ossia i dati che invii prima di chiudere, e imposti il linger prendendo: rapporto * kB = lingertimeout.

    Se hai un server grosso, puoi snellire tutto, inviando sempre blocchi di dati non superiori a 1 kB, quindi, con un thread dedicato aggiorni il lingertimeout per tutti i socket in circolazione, usando l'idle time del sistema, quindi a alla più bassa priorità che esiste. Questo richiede che tieni un array dove sono gli handle dei socket, e il thread spazzola questo array. Se questo array fosse poi un ring-buffer, la gestione sarebbe ottima.

    Ora ti ho messo una pulce nell'orecchio, e ti ho raccontato i trucchi del mestiere, ma per un semplice server 1:1 o 1:10, non vale la pena di pensare in grande. Comunque è un bellisimo studio, soprattutto del ring-buffer, che è anche ideale per inviare grandi quantità di dati senza problemi di collisione fra thread di diversa velocità, come ad esempio la lettura da disco e l'invio dati via Internet.

    Giovanni
    ---
    http://www.y2ksw.com/vbulletin
Devi accedere o registrarti per scrivere nel forum
3 risposte