IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Web & réseau Delphi Discussion :

TCP et TIME_WAIT


Sujet :

Web & réseau Delphi

  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    148
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 148
    Par défaut TCP et TIME_WAIT
    Bonjour,

    je travaille sur un client TCP qui se connecte sur un serveur pour envoyer une data avec ( qsock.IOHandler.Write ) puis recuperer la reponse avec ( qsock.IOHandler.ReadStream )


    send et receive fonctionnent correctement, sauf que le socket (cote serveur) se met en TIME_WAIT (pendant plusieurs secondes).


    ce fameux TIME_WAIT n'apparait pas en utilisant les net.socket de Visual studio


    Comment faire pour eviter ce TIME_WAIT ???
    est ce qu'il y a une autre facon "plus propre" de fermeture du socket client ??


    code delphi XE2

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
     
     
     
     
    //------------------------------------------------------------------------------
    function c_send.sendrequete(qreq, qhost : string; var qrep : string;  qport, qtaillerep : integer) : boolean;
    var
      qsock : tidtcpclient;
      qtaille, compteur, qreqsize, intverif : integer;
      qbytes: tbytes;
      qread, tenvoie : tmemorystream;
      bbool : boolean;
      qrequete : string;
    begin
     
     
    result := false;
    qrep := '';
    c_error := '';
     
     
    tenvoie := tmemorystream.Create;
    qreqsize := 512;
     
     
    qrequete := qreq;
    intverif := usabine.private_cryptertmbss(qrequete,usabine.cryptage_tmbss_a,usabine.rempli_550_a,tenvoie,qreqsize);
     
     
    if c_read_time_out < 1  then  c_read_time_out := 3000;
    if c_connect_time_out < 1  then  c_connect_time_out := 3200;
    qsock := tidtcpclient.Create;
     
    qsock.Host := qhost;
    qsock.Port := qport;
    qsock.ReadTimeout := c_read_time_out;
    qsock.ConnectTimeout := c_connect_time_out;
     
     
    qread := tmemorystream.Create;
    qtaille := tenvoie.Size;
    setlength(qbytes,qtaille );
    tenvoie.Seek(0,soFromBeginning);
    tenvoie.ReadBuffer(qbytes[0],qtaille);
    tenvoie.Free;
     
     
     
    try
      begin   qsock.Connect; qsock.IOHandler.Write(qbytes,qtaille,0);  end;
     
    except
      begin
        qread.Free;
        c_error := '<send>'; setlength(qbytes,0);
        if e_debug <> nil then  e_debug.Lines.Add('error_send_private_sendstr_getstr_2') ;
        abort;
      end; //  except
    end;
     
     
    try
      begin qsock.IOHandler.ReadStream(qread,qtaillerep,true); end;
    except
     begin
        qread.Free;
        c_error := '<receive>'; setlength(qbytes,0);
        if e_debug <> nil then e_debug.Lines.Add('error_read_private_sendstr_getstr_3') ;
        abort;
      end; // except
    end;
     
     
    private_rep_fromstream(qread,qrep);
    qread.Free;
     
    qsock.IOHandler.CloseGracefully;
    qsock.Socket.Close;
    qsock.Free;
    result := true;
     
    end;
     
    //------------------------------------------------------------------------------
    //------------------------------------------------------------------------------
    //------------------------------------------------------------------------------







    code Visual studio 2005

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
     
     
     
     
        Function e__tcpconnect_test(ByVal qdomaine As String, ByVal qport As Integer, Optional ByVal qtimeout As Integer = 5000) As String
     
            Me.btnexe.Enabled = False
     
            Dim tmpbool As Boolean = False
            Dim _socket As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            Dim unpo As New Net.IPEndPoint(Dns.GetHostEntry(qdomaine).AddressList(0), qport)
            _socket.Ttl = 42
     
            _socket.ReceiveTimeout = 300000
            _socket.ReceiveBufferSize = 90000
     
     
            _socket.Connect(unpo)
     
            Dim OctetsEnvoyes As Integer = 0
            Dim stra As String = "" : Dim str1 As String = ""
     
     
            Try
     
                OctetsEnvoyes = _socket.Send(Me.bsends, SocketFlags.None)
     
     
                Dim Messager(10000) As [Byte]
     
                Dim Octetsrecu As Integer 
     
                    Do
                        Octetsrecu = _socket.Receive(Messager, 0, Messager.Length, 0)
                        stra = stra + Encoding.Default.GetString(Messager, 0, Octetsrecu)
                    Loop While Octetsrecu > 0
     
     
            Catch e As SocketException
     
                stra = "error : " + vbCrLf + stra
                Return stra
     
            Finally
                _socket.Close(0)
     
                Array.Resize(Me.bsends, 0)
                Me.btnexe.Enabled = True
                stra = stra.Replace(Chr(10), vbCrLf) : Me.edresultat.Text = str1
                Me.edresultat.Text = stra
            End Try
     
            Return stra
     
        End Function

  2. #2
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 040
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 040
    Par défaut
    Utilise plutôt Socket que IOHandler, juste question de lisibilité

    Je suis surpris par l'appel manuel à qsock.IOHandler.CloseGracefully; je pense que le qsock.Socket.Close(); est suffisant

    Attention, les Abort provoqueront une exception silencieuse mais le qsock.free ne sera pas exécuté, encadre tes libérations ainsi Create() try ... finally Free();J'ai plus pratiqué le TServerSocket, j'ai rarement écrit des clients, en asynchrone avec le TClientSocket, et avec des threads avec le TTCPClient, je n'ai jamais fait de Connect Write Read Close en un seul jet !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre actif
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    148
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 148
    Par défaut
    merci pour ta reponse.

    j'obtiens toujours des TIME_WAIT avec
    1- qsock.socket
    2- qsock.soket.close
    3- qsock.disconnect


    par contre pas de TIME_WAIT si je close avant le read

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
     
     
    ...
    ...
     
    try
      begin
      qsock.Connect;
      qsock.Socket.WriteDirect(qbytes,qtaille,0);
      end;
     
    except
      begin
        qread.Free;
        c_error := '<send>'; setlength(qbytes,0);
        if e_debug <> nil then  e_debug.Lines.Add('error_send_private_sendstr_getstr_2') ;
        abort;
      end; //  except
    end;
     
    qsock.Socket.Close;
    qread.Free;
    result := false;
    exit;
    qsock.socket.readbytes(...); produit aussi des TIME_WAIT

    si je ferme la connection avant la sequence de lecture , je n'ai plus de TIME_WAIT bizzare ????

    y a t'il pas une autre technique de lecture des bytes ??

  4. #4
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 040
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 040
    Par défaut
    As-tu testé avec Telnet ?
    Si tu as un installé les démo, cherche le NetChat en exemple, tu pourrais voir aussi son comportement ?

    TIME_WAIT, c'est vraiment gênant comme Etat ?
    Cela te bloque quelque chose ?


    Fermer avant de lire la Socket, c'est tout de même curieux !

    Faudrait voir les différences selon les valeurs de qtaillerep !
    Jouer AByteCount et AReadUntilDisconnect

    Tu attends d'avoir nombre précis d'octet, je n'ai jamais pratiqué cette méthode bloquante ! logique je ne fais quasiment que des servers !

    Dans mes servers, je lisais ce qu'il y avait, je ne pouvais pas anticiper la longueur de la réponse, en général, les messages était de la même forme
    Header (contenant, un type et\ou longueur de Data)
    Data
    Footer (parfois un CheckSum, parfois juste un caractère de fin)

    tout ce qui était reçu était accumulé dans un buffer, une fois le buffer dépassant la taille de header + footer, je regardais son contenu
    je cherchais le caractère de début de trame (souvent le 1er octet)
    je lisais les octets suivants, pour déterminer, la longueur, je vérifiais si j'avais assez de donneé, si oui, je vérifiais si le footer contenait ce qu'il fallait, une fois un message complet, je l'ajoutais à une FIFO pour traitement, je supprimais du buffer ce qui avait été mis dans la FIFO, il pouvait arriver d'avoir dans le buffer le début d'un autre message ou même plusieurs messages, je bouclais jusqu'à ce que j'arrive à vider le buffer ou jusqu'à la présence d'un message incomplet, lors du prochain tour, les nouvelles données étaient ajoutés à la suite du buffer !

    J'ai déjà eu des problèmes, des partenaires qui n'avait pas correctement géré le protocole était incapable de gérer des messages trop proche (parfois 20 message arrivant en un seul paquet) ... j'ai du parfois volontairement ralentir la FIFO d'envoie pour compenser ce défaut du tiers !

    Au lieu de ReadStream, j'attendrais avec CheckForDataOnSource puis
    je ferais une lecture via InputBufferToStream, un peu de la même façon que ton code VB !

    Voir ce que l'on peut faire avec OnWorkEnd avec AWorkMode à wmRead ?

    Qui connait ici, très bien le TIdTCPClient ?

    Pense qu'il existe le TTCPClient qui s'utilise autrement que celui de Indy !
    Je crois que les composants les plus utilisés pour le TCP reste ICS ?
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    148
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 148
    Par défaut
    Encore merci pour ta reponse.

    En fait le serveur est un serveur fait a la main ( en C sur une plateforme freebsd )
    qui ecoute sur un port,
    1- il lit la requete
    2- s'il ne comprend pas la requete -> ferme la connection
    3- il comprend la requete -> il repond et ferme la connection

    cote serveur : echange simple (lecteur -> reponse-> fermeture)


    -------------------------
    de temps a autre, je recode mon serveur, et j'utilise mon client pour mettre a jour mon serveur
    --------------------------


    pour mettre a jour mon serveur la plateforme freebsd j'utilise 3 serveurs identique qui ecoutent sur 3 ports differents (ces 3 serveurs sont up tout le temps)

    1- j'envoie le code.c sur la plateforme (avec mon client) -> (port-serveur1)
    2- j'envoie une commande shell pour le compiler -> (port-serveur1)
    3- j'envoie une commande shell pour stopper le serveur1 -> (port-serveur2)
    4- j'envoie une commande shell pour relancer le serveur1-modifié -> (port-serveur2)

    idem pour modifier le serveur2, et serveur3 en utilisant cette fois ci le serveur1-modifié

    et je fais cela automatiquement -> toutes les commandes sont envoyées les une apres les autres pour effectuer le travail, j'ai juste a cliquer une fois.

    le probleme c'est que les TIME_WAIT empeche le serveur-modifié (a la relance) d'ouvrir le port tant qu'il ya des TIME_WAIT en attente.


    solution :
    1- mettre 4 ou 5 serveurs en ecoute (sur differents ports) sur la plateforme pour ne pas creer trop de time_wait. ( depuis le temps que j'essaye d'effectuer une requete sans time_wait, je commence a desesperer.)

    2- mettre un delay de 10 a 20 secondes avant de relancer le serveur (le temps de voir disparaitre les (time_wait)

    3- trouver un moyen de faire ( comme a la vs2005) sans produire des time_wait (le top)



    -----------------------------------------------
    oui ttcpclient essayé -> idem -> time_wait
    ---------------------------------------------------

    ------------------------------
    je peux travailler avec mon VS2005, mais j'ai un faible pour delphi
    un jour j'y arriverais
    --------------------------------

  6. #6
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 040
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 040
    Par défaut
    C'est des connexions très courtes !

    Quoi qu'il arrive, le serveur ferme la connexion, réponse ou pas !
    As tu essayé 0, -1, -MaxInt comme valeur de AByteCount paramètre de ReadStream ?
    Selon la valeur AReadUntilDisconnect est géré différemment !
    Il semble qu'il interprète les premiers octets comme la taille du buffer à suivre, dingue, on dirait la technique Windev, c'est naze !
    Je ne connaissais pas les composants TCP de Indy, il ne m'ont pas l'air très pratique !
    As-tu essayé avec d'autres composants que TTCPClient, il ne doit pas être installé par défaut, tu as le vieux TClientSocket, perso, je le trouve parfait d'utilisation !

    Tout ça doit se jouer à un pauvre paramètre dans l'objet Client !

    Si tu as codé en C la partie Server, je pense que coder la partie cliente directement avec les API ne te fera pas peur ! Regarde les travaux du Paul TOTH à ce sujet !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  7. #7
    Membre actif
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    148
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 148
    Par défaut
    - C'est des connexions très courtes !
    rep : oui tres courtes, quand j'envoie un gros fichier, je le fais par morceau.



    - As tu essayé 0, -1, -MaxInt comme valeur de AByteCount paramètre de ReadStream ?
    rep : yes



    - Il semble qu'il interprète les premiers octets comme la taille du buffer à suivre, dingue, on dirait la technique Windev, c'est naze !
    rep : yes c'est naze jusqu'a preuve du contraire



    - As-tu essayé avec d'autres composants que TTCPClient, il ne doit pas être installé par défaut, tu as le vieux TClientSocket, perso, je le trouve parfait d'utilisation !

    rep : je suis entrain de travailler dessus, et j'ai un petit debut prometteur, avec ce code : ( je ne lis pas tout le buffer ) je laisse 1 octet dans le buffer, pas de time_wait

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
     
    // dans l'evenement on disconnect du client TCustomWinSocket
     
    i1 := 0;
    i2 := socket.ReceiveLength;
    Setlength(buffer, i1+i2);
     
    try
       socket.ReceiveBuf(Buffer[i1], i2-1);
    finally
    end;

    par contre quand je lis tout le buffer ( socket.ReceiveBuf(Buffer[i1], i2); )
    il y a production de time_wait, je vais travailler sur cette piste.



    - Tout ça doit se jouer à un pauvre paramètre dans l'objet Client !
    rep : tres probablement




    - Si tu as codé en C la partie Server, je pense que coder la partie cliente directement avec les API ne te fera pas peur ! Regarde les travaux du Paul TOTH à ce sujet !

    rep : en fait le C sur linux ou freebsd est beucoup plus simple que delphi sur windows.

  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    148
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 148
    Par défaut
    Citation Envoyé par mteirek_m Voir le message

    ... /

    le probleme c'est que les TIME_WAIT empeche le serveur-modifié (a la relance) d'ouvrir le port tant qu'il ya des TIME_WAIT en attente.

    / ...
    bon j'ai reglé le probleme, mais pas du cote client (delphi), c'est au niveau du serveur en utilisant l'option setsockopt :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
     
    int on = 1;
    setsockopt(desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));
    comme ca les time_wait n'empeche plus le serveur de redemarrer .

    reste a savoir comment dire a delphi pour qu'il envoie un message au serveur -> ("j'ai tout recu - tu peux fermer la connexion"), et ne pas le laisser en time_wait pendant un temps (l_linger), variable tres mal definie dans freebsd

Discussions similaires

  1. Ping sous protocole TCP (et non UDP)
    Par ovdz dans le forum Développement
    Réponses: 2
    Dernier message: 19/06/2003, 14h10
  2. [socket][tcp] jeu en reseau
    Par souris_sonic dans le forum Développement
    Réponses: 2
    Dernier message: 30/05/2003, 07h31
  3. [Concept]Concept d'un serveur TCP/IP
    Par Zc dans le forum Développement
    Réponses: 8
    Dernier message: 17/01/2003, 17h06
  4. Différence entre TCP, UDP, ICMP
    Par GliGli dans le forum Développement
    Réponses: 1
    Dernier message: 13/09/2002, 08h25
  5. transfert d'un fichier bitmap en socket tcp
    Par localhost dans le forum C++Builder
    Réponses: 5
    Dernier message: 29/07/2002, 00h40

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo