Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   ICS TWSocket Eingehende Verbindung (https://www.delphipraxis.net/180762-ics-twsocket-eingehende-verbindung.html)

Captnemo 16. Jun 2014 11:58

ICS TWSocket Eingehende Verbindung
 
Hi,

ich versuche mich gerade mit den ICS-Komponenten. Explizit eine Eingehende Verbindung per TCP über TWSocket.
Laut Wiki soll ich im Event "SessionAvailable" folgendes machen:
Zitat:

The following example implements the OnSessionAvailable of a simple TCP-Server which transfers the accepted connection to another socket so that other incomming connections can be accepted as well. Since the socket the connection is transfered to is a global variable here only one connection can be accepted, but this example is simply for showing the basics.
var socket:TMySocket; // a descendant of TWSocket with overwritten OnDataAvailable

Delphi-Quellcode:
 procedure TMyServer.MyOnSessionAvailable(Sender: TObject; ErrCode: Word);
 var NewS:TSocket;
 begin
   if ErrCode <> 0 then exit;
 
   NewS:=Accept;
   socket:=TMySocket.create(nil);
   socket.dup(NewS);
 end;

Ich finde nur in XE4 nirgends die Klasse TSocket. Kann mir jemand auf die Sprünge helfen?

mkinzler 16. Jun 2014 12:01

AW: ICS TWSocket Eingehende Verbindung
 
Ist in der Unit WinSock

Captnemo 16. Jun 2014 13:01

AW: ICS TWSocket Eingehende Verbindung
 
Dann meckert er bei mir
[dcc32 FEhler] clt_main.pas(118): E2035 Nicht genügend wirkliche Parameter

bei dem
Delphi-Quellcode:
mysocket:=accept;


Aber das es im Wiki von ICS so beschrieben steht, dachte ich, dass ich das so verwenden kann.

mkinzler 16. Jun 2014 13:03

AW: ICS TWSocket Eingehende Verbindung
 
Wo/wie ist denn Accept deklariert?

ensaron 16. Jun 2014 13:26

AW: ICS TWSocket Eingehende Verbindung
 
Ich vermute mal, an dieser Stelle ist TSocket aus der Unit
Delphi-Quellcode:
OverbyteIcsWSocket
gemeint.

Captnemo 16. Jun 2014 15:46

AW: ICS TWSocket Eingehende Verbindung
 
ich habe es mal geändert in
Delphi-Quellcode:
mysocket:=(sender as TWSocket).Accept;
und dann klappt es (frag mich aber, wie die das dann mit dem Beispielcode im Wiki geschafft haben).

Leider komme ich aber immer noch nicht zu dem gewünschten Ergebnis.

Ich habe auf der Form 2 TWSocket's. Einer wird mit Listen als Server gestartet, und über den anderen kann man auf einen Port eine Nachricht schicken lassen. Entweder an sich selbst oder halt an eine zweite Instanz (dann natürlich mit einsprechenden Ports).

Ziel dieses Projekt's ist es für mich erst einmal, mir zu verdeutlichen, wie das mit ICS funktioniert. (Also kein Kundenprojekt).

Hier mal mein Übungsprojekt:
Delphi-Quellcode:
unit clt_main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, JvExControls, JvComCtrls,
  Vcl.ExtCtrls, OverbyteIcsLogger,
  OverbyteIcsWndControl, OverbyteIcsWSocket, JvExStdCtrls, JvEdit,
  JvValidateEdit, Vcl.ComCtrls, winsock;

type
  Tfrm_main = class(TForm)
    lbl1: TLabel;
    edt_ip1: TJvIPAddress;
    edt_text: TEdit;
    btn_send: TButton;
    lbl2: TLabel;
    ipserver: TWSocket;
    icslgr1: TIcsLogger;
    mmo_nachrichten: TMemo;
    edt_port: TJvValidateEdit;
    btn_start: TButton;
    stat1: TStatusBar;
    ipclient: TWSocket;
    edt_sendport: TJvValidateEdit;
    lbl3: TLabel;
    lbl4: TLabel;
    lbl5: TLabel;
    procedure FormShow(Sender: TObject);
    procedure btn_sendClick(Sender: TObject);
    procedure btn_startClick(Sender: TObject);
    procedure ipserverChangeState(Sender: TObject; OldState,
      NewState: TSocketState);
    procedure ipserverDataAvailable(Sender: TObject; ErrCode: Word);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ipserverSessionAvailable(Sender: TObject; ErrCode: Word);
    procedure ipclientSessionConnected(Sender: TObject; ErrCode: Word);
    procedure ipserverSessionClosed(Sender: TObject; ErrCode: Word);
    procedure ipclientSessionClosed(Sender: TObject; ErrCode: Word);
    procedure ipserverSessionConnected(Sender: TObject; ErrCode: Word);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    ServerSocket: TWSocket;
    SendStringBuffer: TStringList;
    function GetTWSocketState(Socket: TWSocket): string;
    { Public-Deklarationen }
  end;

var
  frm_main: Tfrm_main;

implementation

{$R *.dfm}

function Tfrm_main.GetTWSocketState(Socket: TWSocket): string;
begin
  Result:='';
  case Socket.state of
    wsInvalidState: Result:='wsInvalidState';
    wsOpened: Result:='wsOpened';
    wsBound: Result:='wsBound';
    wsConnecting: Result:='wsConnecting';
    wsSocksConnected: Result:='wsSocksConnected';
    wsConnected: Result:='wsConnected';
    wsAccepting: Result:='wsAccepting';
    wsListening: Result:='wsListening';
    wsClosed: Result:='wsClosed';
  end;
end;

procedure Tfrm_main.btn_sendClick(Sender: TObject);
begin
  SendStringBuffer.Add(edt_text.Text);
  edt_text.Text:='';
  edt_text.SetFocus;
  try
    ipclient.Addr:=edt_ip1.Text;
    ipclient.Port:=edt_sendport.Text;
    ipclient.Proto:='tcp';
    ipclient.Connect;
  except
    on E: Exception do
    begin
      mmo_nachrichten.Lines.Insert(0, 'Error: '+E.Message);
    end;
  end;
end;

procedure Tfrm_main.btn_startClick(Sender: TObject);
begin
  if btn_start.Caption='Start' then
  begin
    btn_start.Caption:='Stop';
    ipserver.Addr:='0.0.0.0';
    ipserver.Port:=edt_port.Text;
    ipserver.Proto:='tcp';
    ipserver.Listen;
  end else begin
    btn_start.Caption:='Start';
    ipserver.Shutdown(0);
  end;
end;

procedure Tfrm_main.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ipserver.Abort;
end;

procedure Tfrm_main.FormCreate(Sender: TObject);
begin
  SendStringBuffer:=TStringList.Create;
end;

procedure Tfrm_main.FormDestroy(Sender: TObject);
begin
  SendStringBuffer.Free;
end;

procedure Tfrm_main.FormShow(Sender: TObject);
begin
  mmo_nachrichten.Lines.Clear;
  edt_text.Text:='';
  edt_port.Value:=17001;
  edt_sendport.Value:=17001;
end;

procedure Tfrm_main.ipclientSessionClosed(Sender: TObject; ErrCode: Word);
begin
  mmo_nachrichten.Lines.Insert(0, 'Client Session Closed: '+IntToStr(ErrCode));
end;

procedure Tfrm_main.ipclientSessionConnected(Sender: TObject; ErrCode: Word);
begin
  mmo_nachrichten.Lines.Insert(0, 'Client Session Connected');
  while SendStringBuffer.Count>0 do
  begin
      ipclient.SendStr(SendStringBuffer[0]+#13#10);
      mmo_nachrichten.Lines.Insert(0, 'Gesendet: '+SendStringBuffer[0]);
      SendStringBuffer.Delete(0);
  end;
  ipclient.Close;
end;

procedure Tfrm_main.ipserverChangeState(Sender: TObject; OldState,
  NewState: TSocketState);
begin
  case newstate of
    wsInvalidState: mmo_nachrichten.Lines.Insert(0, 'ServerState: Invalid State');
    wsOpened: mmo_nachrichten.Lines.Insert(0, 'ServerState: Öffnet');
    wsBound: mmo_nachrichten.Lines.Insert(0, 'ServerState: Gebunden');
    wsConnecting: mmo_nachrichten.Lines.Insert(0, 'ServerState: Verbinden');
    wsSocksConnected: mmo_nachrichten.Lines.Insert(0, 'ServerState: Socks verbunden');
    wsConnected: mmo_nachrichten.Lines.Insert(0, 'ServerState: Verbunden');
    wsAccepting: mmo_nachrichten.Lines.Insert(0, 'ServerState: Akzeptiert');
    wsListening: mmo_nachrichten.Lines.Insert(0, 'ServerState: Wartet...');
    wsClosed: mmo_nachrichten.Lines.Insert(0, 'ServerState: Geschlossen');
  end;
end;

procedure Tfrm_main.ipserverDataAvailable(Sender: TObject; ErrCode: Word);
var
  s: string;
begin
  mmo_nachrichten.Lines.Insert(0, 'Server: DataAvailable');
  if ErrCode<>0 then Exit;
  s:=ipserver.ReceiveStr;
  mmo_nachrichten.Lines.Insert(0, 'Empfangen: '+s);
end;

procedure Tfrm_main.ipserverSessionAvailable(Sender: TObject; ErrCode: Word);
var
  mysocket: TSocket;
begin
  if ErrCode <> 0 then exit;
  mysocket:=ipserver.Accept;
  ServerSocket:=TWSocket.Create(nil);
  ServerSocket.Dup(mysocket);
  mmo_nachrichten.Lines.Insert(0, 'Server Session Available: '+ServerSocket.PeerPort);
end;

procedure Tfrm_main.ipserverSessionClosed(Sender: TObject; ErrCode: Word);
begin
  mmo_nachrichten.Lines.Insert(0, 'Server Session Closed: '+IntToStr(ErrCode));
end;

procedure Tfrm_main.ipserverSessionConnected(Sender: TObject; ErrCode: Word);
begin
  mmo_nachrichten.Lines.Insert(0, 'Server Session Connected');
end;

end.
Mein Problem ist jetzt, dass er wohl connected und auch sendet, aber das OnDataAvailable von der Server-Komponente wird nie ausgelöst.

Hier mal das Ergebnis im Memo, was ich beim Senden eines Textes erhalte:

Code:
Client Session Closed: 0
Gesendet: TestTestTest
Client Session Connected
Server Session Available: 49946
ServerState: Wartet...
ServerState: Gebunden
Achso: Und ipserver.LineEnd=#13#10 und ipserver.LineMode=True ist gesetzt.

Captnemo 17. Jun 2014 07:21

AW: ICS TWSocket Eingehende Verbindung
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ein neuer Tag, und kaum geht man neu ausgeruht an die Sache heran, fallen einem gleich die Fehler auf :-)
Nun funktioniert's.

Zur Erklärung: Im OnSessionAvailable wird eine neuen Instanz für die ankommende Session erzeugt
Delphi-Quellcode:
procedure Tfrm_main.ipserverSessionAvailable(Sender: TObject; ErrCode: Word);
var
  mysocket: TSocket;
begin
  if ErrCode <> 0 then exit;
  mysocket:=ipserver.Accept;
  ServerSocket:=TWSocket.Create(nil);
  //Da hier einen neue Instanz von TWSocket erzeugt wird,
  //muß natürlich auch das OnDataAvailable-Ereignis zugeordnet werden,
  //da sonst der Datenempfang nicht festgestell werden kann.
  ServerSocket.OnDataAvailable:=ipserverDataAvailable;
  ServerSocket.OnSessionClosed:=ipserverSessionClosed;
  ServerSocket.OnSessionConnected:=ipserverSessionConnected;
  ServerSocket.OnChangeState:=ipserverChangeState;
  //LineMode,IP und Ports übernehme ich von der Komponente, die auf
  //der Form liegt, da ich dort Veränderungen vornehme, wenn nötig.
  ServerSocket.LineMode:=ServerSocket.LineMode;
  ServerSocket.LineEnd:=ServerSocket.LineEnd;
  ServerSocket.LocalAddr:=ipserver.LocalAddr;
  ServerSocket.Port:=ipserver.Port;
  ServerSocket.Proto:=ipserver.Proto;
  ServerSocket.Dup(mysocket);
  Writelog(False, 'Server Session Available: '+ServerSocket.PeerPort);
end;
dieser müssen natürlich die Ereignisse zugewiesen werden. Das war mein Denkfehler, denn ich dachte er übernimmt diese von der Komponente. Aber klar, woher denn auch. Und letztlich ist das natürlich genial, denn in Abhängigkeit der in OnSessionAvailable zur Verfügung stehenden Daten (z.B. Peer-IP) könnte man unterschiedliche Ereignisbehandlungsroutinen und andere Eigenschaften zuweisen.

Danach empfing ich aber leider nur leere Strings.
Auch hier liegt die Erklärung wieder in der Instanz. Da ich zu Anfang die Ereignisprozeduren für OnDataAvailable nur der Komponente auf der Form zugewiesen habe, habe ich mich beim Auswerten des ReceiveStr diesen auch von dieser geholt. Richtigerweise muss man sich die aber von Sender holen, da es sich ja um einen neue Instanz handelt.
Delphi-Quellcode:
procedure Tfrm_main.ipserverDataAvailable(Sender: TObject; ErrCode: Word);
var
  s: string;
begin
  Writelog(False, 'Server: DataAvailable');
  if ErrCode<>0 then Exit;
  //Wichtig: Mit dem Sender arbeiten
  s:=(Sender as TWSocket).ReceiveStr;
  s:=StringReplace(s,(Sender as TWSocket).LineEnd, '', [rfReplaceAll]);
  if s<>'' then
  begin
    //Hier den Emfangenen Text auswerten
    Writelog(True, 'Empfangen: '+s);
  end;
end;
Und schon funktioniert es wie gewünscht. Ich habe ein bischen gebraucht, bis dahinter kam, da der Einstieg auf Grund wenig vorhandener Dokumentation schwierig war.

Für alle die es interessiert und die das nachvollziehen wollen, hab ich mal den Source angehängt.
Vielen Danke für eure Unterstützung.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:02 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz