Announcement

Collapse
No announcement yet.

Logik beim Versenden mit Clientsocket

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Logik beim Versenden mit Clientsocket

    Bin absoluter Netzwerkanfänger. Habe bei D3 ein Beispiel für TCCP-Komponenten befunden. Bei D6 wohl Clientsocket.
    Weshalb kann man nicht auf Buttonclick die folgende Anweisung ausführen? tcpclient.open; .
    TCPClient.Socket.SendText('BlaBla');.
    TCPClient.Close;.

    Weshalb wird auf Buttonclick beim ersten Mal nur die Verbindung zum Server hergestellt? Wenn die Verbindung steht, wird beim zweiten Buttonclick auch der Text übertragen. Kann man dieses Verhalten umgehen?

    Habe das Tutorial im delphi-treff gelesen. Beantwortet aber leider nicht meine Frage.

    Wenn ich beim Programmstart bereits die Verbindung herstelle, blockiere ich damit die Zugriffe auf den Server von anderen Rechnern?

  • #2
    Hallo,

    als Socket bezeichnet man jede Kombination einer <b>IP-Adresse</b> mit einer <b>Portnummer</b>. Solange diese Kombination eindeutig ist, liegt ein gültiger Socket vor. Da ein Server im Worst Case auch gleichzeitig mehrere Clients bedienen muss, ist die Portnummer nur für den Zeitpunkt des Verbindungsaufbaus (Schritt 1) feststehend, danach teilt der Server dem Client seine Session-Portnummer mit.

    Ein auf Empfang geschalteter Socket (listen) hat die Remote-Adresse 0 sowie den Remote-Port 0. Ein verbundener Socket (connected) hat jedoch eine Remote-Adresse ungleich 0 sowie einen Remote-Port ungleich 0. Erhält der Prozess von Daten von außen, so werden diese zu dem Socket weitergeleitet, dessen IP- und Port-Nummer zu diesen Datenpaketen passt. Wird kein passender verbundener Socket gefunden, gehen die Daten zu Empfangs-Socket (listen).

    In der Praxis sieht das dann so aus:

    <p>a) ClientSocket beim Verbindungsaufbau:
    192.168.10.1,9000,'tcp',192.168.10.11,9000</p>
    <p>b) ClientSocket hat Verbindung zum ServerSocket:
    192.168.10.1,900<b>1</b>,'tcp',192.168.10.11,900<b>1</b></p>
    <p>c) ServerSocket ist für den nächsten Client wieder auf Empfang:
    192.168.10.1,9000,'tcp',192.168.10.1<b><font color="#ff0000">2</font></b>,9000</p>
    <p>d) Der 1. Client überträgt Daten auf dem 1. privaten Socket:
    192.168.10.1,9001,'tcp',192.168.10.11,9001</p>
    <p>e) Der 2. Client überträgt Daten auf dem 2. privaten Socket:
    92.168.10.1,9002,'tcp',192.168.10.1<b><font color="#ff0000">2</font></b>,9002</p>

    &gt;...blockiere ich damit die Zugriffe ...

    Der Socket-Server muss beim Verbindungsaufbau für diese Session sofort einen Thread abspalten und sofort wieder auf "Empfang" (listen) gehen, damit sich andere Client verbinden können. Das könnte so aussehen:
    <pre>
    <b>unit</b> SocketSrvFrm;
    <br>
    <b>interface</b>
    <br>
    <b>uses</b>
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    ComCtrls, ScktComp, StdCtrls;
    <br>
    <b>type</b>
    TFormServer = <b>class</b>(TForm)
    StatusBar1: TStatusBar;
    ServerSocket1: TServerSocket;
    ListBox1: TListBox;
    <b>procedure</b> ServerSocket1GetThread(Sender: TObject;
    ClientSocket: TServerClientWinSocket;
    <b>var</b> SocketThread: TServerClientThread);
    <b>procedure</b> ServerSocket1ClientConnect(Sender: TObject;
    Socket: TCustomWinSocket);
    <b>procedure</b> ServerSocket1ClientDisconnect(Sender: TObject;
    Socket: TCustomWinSocket);
    <b>procedure</b> ServerSocket1ThreadEnd(Sender: TObject;
    Thread: TServerClientThread);
    <b>procedure</b> ServerSocket1ThreadStart(Sender: TObject;
    Thread: TServerClientThread);
    <b>procedure</b> FormCreate(Sender: TObject);
    <b>procedure</b> FormDestroy(Sender: TObject);
    <b>private</b>
    <font color="#003399"><i>{ Private-Deklarationen }</i></font>
    <b>public</b>
    <font color="#003399"><i>{ Public-Deklarationen }</i></font>
    <b>end</b>;
    <br>
    TClientThread = <b>Class</b>(TServerClientThread)
    <b>private</b>
    <b>public</b>
    <b>procedure</b> ClientExecute; <b>override</b>;
    <b>end</b>;
    <br>
    <b>var</b>
    FormServer: TFormServer;
    iFileCount: Integer;
    <br>
    <b>implementation</b>
    <br>
    <font color="#003399"><i>{$R *.DFM}</i></font>
    <br>
    <font color="#003399"><i>{ TClientThread }</i></font>
    <br>
    <b>procedure</b> TClientThread.ClientExecute;
    <b>var</b>
    sFileName : <b>String</b>;
    szBuffer : <b>array</b>[0..1024] <b>of</b> Char;
    aWSStream : TWinSocketStream;
    iBytes : Integer;
    aFStream : TFileStream;
    <b>begin</b>
    FillChar(szBuffer, SizeOf(szBuffer), #0);
    <b>while</b> <b>not</b> Terminated <b>and</b> ClientSocket.Connected <b>do</b>
    <b>begin</b>
    <b>try</b>
    sFileName := Format(<font color="#9933CC">'C:\Temp\Socket%d.dat'</font>, [iFileCount]);
    <font color="#003399"><i>// Blockierende Sockets mit 60 Sekunden Timeout</i></font>
    aWSStream := TWinSocketStream.Create(ClientSocket, 60000);
    aFStream := TFileStream.Create(sFileName, fmCreate);
    <b>try</b>
    <font color="#003399"><i>// Client muss innerhalb von 5 Sekunden mit der Übertragung beginnen</i></font>
    <b>if</b> aWSStream.WaitForData(5000) <b>then</b>
    <b>repeat</b>
    iBytes := aWSStream.<b>Read</b>(szBuffer, SizeOf(szBuffer));
    <b>if</b> iBytes = 0 <b>then</b>
    <font color="#003399"><i>// Fehlschlag -&gt; Socket-Verbindung schliessen</i></font>
    ClientSocket.Close
    <b>else</b>
    aFStream.WriteBuffer(szBuffer, iBytes);
    <b>until</b> <b>not</b> aWSStream.WaitForData(2000);
    <b>finally</b>
    aWSStream.Free;
    ClientSocket.Close;
    aFStream.Free;
    <b>end</b>;
    <b>except</b>
    <b>end</b>;
    <b>end</b>;
    <b>end</b>;
    <br>
    <font color="#003399"><i>{ TFormServer }</i></font>
    <br>
    <b>procedure</b> TFormServer.FormCreate(Sender: TObject);
    <b>begin</b>
    iFileCount := 1;
    ServerSocket1.Active := True;
    <b>end</b>;
    <br>
    <b>procedure</b> TFormServer.FormDestroy(Sender: TObject);
    <b>begin</b>
    ServerSocket1.Active := False;
    Sleep(2000);
    Application.ProcessMessages;
    <b>end</b>;
    <br>
    <b>procedure</b> TFormServer.ServerSocket1GetThread(Sender: TObject;
    ClientSocket: TServerClientWinSocket;
    <b>var</b> SocketThread: TServerClientThread);
    <b>begin</b>
    SocketThread := TClientThread.Create(False, ClientSocket);
    <b>end</b>;
    <br>
    <b>procedure</b> TFormServer.ServerSocket1ClientConnect(Sender: TObject;
    Socket: TCustomWinSocket);
    <b>begin</b>
    ListBox1.Items.Add(<font color="#9933CC">'Client-Connect....'</font>);
    <b>end</b>;
    <br>
    <b>procedure</b> TFormServer.ServerSocket1ClientDisconnect(Sender: TObject;
    Socket: TCustomWinSocket);
    <b>begin</b>
    InterlockedIncrement(iFileCount);
    ListBox1.Items.Add(<font color="#9933CC">'Client-Disconnect'</font>);
    <b>end</b>;
    <br>
    <b>procedure</b> TFormServer.ServerSocket1ThreadEnd(Sender: TObject;
    Thread: TServerClientThread);
    <b>begin</b>
    ListBox1.Items.Add(<font color="#9933CC">'Socket-Thread endet....'</font>);
    <b>end</b>;
    <br>
    <b>procedure</b> TFormServer.ServerSocket1ThreadStart(Sender: TObject;
    Thread: TServerClientThread);
    <b>begin</b>
    ListBox1.Items.Add(<font color="#9933CC">'Socket-Thread startet....'</font>);
    <b>end</b>;
    <br>
    <b>end</b>.
    </pre>
    Der dazu passende Client sieht so aus:
    <pre>
    <b>unit</b> SocketCltFrm;
    <br>
    <b>interface</b>
    <br>
    <b>uses</b>
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    ComCtrls, StdCtrls, ScktComp;
    <br>
    <b>type</b>
    TFormClient = <b>class</b>(TForm)
    StatusBar1: TStatusBar;
    ClientSocket1: TClientSocket;
    OpenDialog1: TOpenDialog;
    ButtonSend: TButton;
    ListBoxFiles: TListBox;
    <b>procedure</b> ButtonSendClick(Sender: TObject);
    <b>private</b>
    <font color="#003399"><i>{ Private-Deklarationen }</i></font>
    <b>public</b>
    <font color="#003399"><i>{ Public-Deklarationen }</i></font>
    <b>end</b>;
    <br>
    <b>var</b>
    FormClient: TFormClient;
    <br>
    <b>implementation</b>
    <br>
    <font color="#003399"><i>{$R *.DFM}</i></font>
    <br>
    <b>procedure</b> TFormClient.ButtonSendClick(Sender: TObject);
    <b>var</b>
    aWSS : TWinSocketStream;
    aFS : TFileStream;
    <b>begin</b>
    <b>if</b> <b>not</b> OpenDialog1.Execute <b>then</b>
    Exit;
    StatusBar1.SimpleText := <font color="#9933CC">'Sockets sind offen'</font>;
    ClientSocket1.Open;
    aWSS := TWinSocketStream.Create(ClientSocket1.Socket, 60000);
    aFS := TFileStream.Create(OpenDialog1.FileName, fmShareDenyWrite);
    <b>try</b>
    aWSS.CopyFrom(aFS, 0);
    <b>finally</b>
    aWSS.Free;
    aFS.Free;
    ClientSocket1.Close;
    <b>end</b>;
    StatusBar1.SimpleText := <font color="#9933CC">'Sockets sind geschlossen'</font>;
    ListBoxFiles.Items.Add(OpenDialog1.FileName);
    <b>end</b>;
    <br>
    <b>end</b>.
    </pre&gt

    Comment


    • #3
      Hallo Andreas,<br>
      wie kann ich eigentlich dem Client antworten?<br>
      Bislang habe ich immer auch das o.g. Verfahren verwendet. Nur wenn ich dem Client antworten wollte, <br>
      habe ich auch eine TServerSocket auf das Clientformular gesetzt. Wenn der Server dem Client antwortet,<br>
      hat der Server ein Verbindung zum Client-TServerSocket aufgebaut. Mit einem dyn. erzeugten TClientSocket.<br>
      Warum dieser Aufwand?<br>
      Ich habe es bislang noch nicht geschafft über ClientSocket dem Client zu antworten. Aus der<br>
      Hilfe habe ich entnommen, dass ClientSocket (als Eigenschaft von TServerClientThread) den Endpunkt<br>
      der Verbindung mit dem Client beschreibt. Dann müsste ich doch mit TWinSocketStream (ich<br>
      verwende grundsätzlich blockierende Verbindungen) an den Client etwas senden können?<br>
      <br>
      <br>
      Auch das ClientSocket.Close kommt beim Client nicht als OnDisConnect an.<br>
      Das Event OnConnect auf der Serverseite wird auch nicht ausgelöst.<br>
      <br>
      <pre> try
      // Client muss innerhalb von 5 Sekunden mit der Übertragung beginnen
      if aWSStream.WaitForData(5000) then
      repeat
      iBytes := aWSStream.Read(szBuffer, SizeOf(szBuffer));
      if iBytes = 0 then
      // Fehlschlag -&gt; Socket-Verbindung schliessen
      <b>Break</b> // Damit ClientSocket offen bleibt
      else
      aFStream.WriteBuffer(szBuffer, iBytes);
      until not aWSStream.WaitForData(2000);
      // Hier möchte ich dem Client antworten. Klappt aber nicht. Beim Client kommt nix an
      // D.h. im Client wird kein TClientSocket.OnRead ausgelöst ????
      <font face="Verdana" size="2" color="#FF0000">aClientWSS:=TWinSocketStream.Creat e(ClientSocket,60000);
      Try
      aClientWSS.Write(... );
      Finally
      aClientWSS.Free;
      end;
      </font> finally
      aWSStream.Free;
      ClientSocket.Close;
      aFStream.Free;
      </pre>
      <br&gt

      Comment

      Working...
      X