Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi ServerSocket1ClientRead (https://www.delphipraxis.net/77747-serversocket1clientread.html)

yildi 24. Sep 2006 12:37


ServerSocket1ClientRead
 
moin moin!

mein kleines serverprogramm kann mit dieser procedure binärdaten empfangen:

Delphi-Quellcode:
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  SizeOfPacket: Integer;
  pBuffer: Pointer;
begin

      SizeOfPacket := Socket.ReceiveLength;
      GetMem(pBuffer, SizeOfPacket);
      Socket.ReceiveBuf(pBuffer^, SizeOfPacket);
      MUpdateStream.Write(pBuffer^, SizeOfPacket);
      FreeMem(pBuffer);

      if () then
        begin
         

          MUpdateStream.SaveToFile('update2.exe');
     Log.Lines.Add('('+TimeToStr(Now)+') System: Ein Server-Update wurde erfolgreich empfangen und wird nun installiert.');

          MUpdateStream.Free;
          MUpdateStream := TMemoryStream.Create;

        end;

end;


allerdings hab ich damit ein kleines problemchen. die ClientRead procedure wird ja quasi wie eine schleife immer wieder so lange aufgerufen, bis keine daten mehr empfangen werden. die empfangenen daten werden dann immer an meim MUpdateStream angehängt. am ende möchte ich dann aus dem stream eine datei erstellen.

Nur ich weiss leider nicht, wie ich meine bedingung formulieren muss, dass dieser teil quasi nur nach dem letzten empfangenen paket aufgerufen wird. man könnte von seiten des clients vorher die dateigröße übermitteln, nur dafür müsste ich insgesamt mein programm zu sehr umbauen. hat jemand noch eine andere idee ?

danke!
yildi

Zacherl 24. Sep 2006 12:43

Re: ServerSocket1ClientRead
 
Die Datengröße ist leider erforderlich ... Du könntest vor dem Senden der Binärdaten mit SendText einfach z.B. "Data:27526" (länge in Byte) an den Server senden.

Florian

Muetze1 24. Sep 2006 13:03

Re: ServerSocket1ClientRead
 
Da dies schon eine Art Protokoll darstellt (was du hier brauchst) würde ich trotzdem diesen Weg nicht im Detail empfehlen. Er überträgt Binärdaten, warum sollte er also eine Mischung aus Text und Binärdaten machen? Vor allem: Wie weiss er, dass es noch eine Ziffer der Byteangabe ist oder schon das erste Zeichen des Binärstreams?

Empfehlung: Sende vor den Binärdaten einen INT64 direkt über den Stream, welche die Länge der nachfolgenden Binärdaten angibt. Dann hast du auf der Empfangsseite eine einfache Möglichkeit den kompletten Empfang zu ermitteln (Stream.Size und vorher gesendete Zahl). Und beachte, dass der letzte empfangene Block grösser sein kann, als der Vollendung des Streams nötige.

Zacherl 24. Sep 2006 13:23

Re: ServerSocket1ClientRead
 
Er kann prüfen, ob die ersten Zeichen DATA sind, dann weiß er, dass es sich um die Längenangabe handelt.

Ansonsten kannst du dir mal meinen OpenSource Chat angucken:
http://www.delphipraxis.net/internal...highlight=chat

Florian

yildi 24. Sep 2006 14:28

Re: ServerSocket1ClientRead
 
hmm danke für eure hilfe..

nur wie erkenn ich denn, wann die größenangabe nach DATA: zu ende ist ?

Muetze1 24. Sep 2006 18:45

Re: ServerSocket1ClientRead
 
Zitat:

Zitat von yildi
nur wie erkenn ich denn, wann die größenangabe nach DATA: zu ende ist ?

Diese Frage bleibt offen und genauso ist die Sache mit abprüfen ob vorne "DATA" steht, genauso schwachsinnig. Damit würdest du jede Binärdatei vor der Übertragung ausschliessen, welche mit "DATA" anfängt oder dieses Wort enthält. Schick mal den Text dieses Threads über den Socket und du wirst ein Problem bekommen.

@yildi:
Schick vor dem Stream einfach dessen Grösse mit:
Delphi-Quellcode:
Var
  lSize: Int64;
Begin
  lSize := Stream.Size; // Grösse der zu sendenen Datei
  Socket.SendBuf(lSize, SizeOf(lSize)); // Grösse vorneweg schicken
  Socket.SendStream(Stream);
End;
Beim Empfänger:
Delphi-Quellcode:
  fSize : Int64; // als Klassenmitglied deklarieren
  fReceiveStream: TStream; // dito
...

Procedure SocketEmpfangen(...)
Var
  lBuff: Pointer;
  lSize: Int64;
Begin
  If Not Assigned(fReceiveStream) And ( Socket.ReceiveLength >= SizeOf(Int64) ) Then
  Begin
    Socket.ReceiveBuf(fSize, SizeOf(fSize));
    fReceiveStream := {TMemoryStream}TFileStream.Create(...);
  End
  Else If Assigned(fReceiveStream) Then
  Begin
    lSize := Min(fSize - fReceiveStream.Size, Socket.ReceiveLength);
    lBuff := GetMem(lSize);
    Try
      Socket.ReceiveBuf(lBuff^, lSize);
      fReceiveStream.WriteBuffer(lBuff^, lSize);
    Finally
      FreeMem(lBuff);
    End;

    If ( fReceiveStream.Size = fSize ) Then
    Begin
      FreeAndNil(fReceiveStream);
      fSize := 0;
    End;
  End;
End;
ein kleiner Pseudocode um dies zu verdeutlichen...

Zacherl 24. Sep 2006 19:04

Re: ServerSocket1ClientRead
 
Zitat:

Zitat von Muetze1
Diese Frage bleibt offen und genauso ist die Sache mit abprüfen ob vorne "DATA" steht, genauso schwachsinnig. Damit würdest du jede Binärdatei vor der Übertragung ausschliessen, welche mit "DATA" anfängt oder dieses Wort enthält. Schick mal den Text dieses Threads über den Socket und du wirst ein Problem bekommen.

Meinen Beispielchat mal angeguckt?

Muetze1 24. Sep 2006 20:26

Re: ServerSocket1ClientRead
 
Zitat:

Zitat von Florian Bernd
Meinen Beispielchat mal angeguckt?

Ok, habe ich nun gemacht.
  • Warum nutzt du einen TMemoryStream beim versenden der Daten? Schick damit mal eine 4 GB Datei und schon kratzt er erstmal stundenlang seine Swap Datei voll. Und warum kein SendStream() der TCustomWinSockets nutzen? Dieser kehrt sofort zurück und wartet nicht, bis alles im Socket Buffer ist...
  • Warum definierst du eigene Typen für Objektmethoden die schon definiert sind? z.B. hättest du dir einfach
    Delphi-Quellcode:
    TOnRead = TNotifyEvent;
    definieren können.
  • Was sollen die Sleep()'s zwischen den Teilstrings? Dein Protokoll teilt die Teilstrings und nicht eine Pause zwischen dem absenden. Benutz dein Chat mal über ein wackliges Netzwerk und du wirst dich freuen, weil diese Teilung völlig nichtig ist.
  • Mit dem "StLG:" und der angehängten ASCII Zahl gehst du auch fest davon aus, dass du eine Teilung bekommst, wenn du die Strings mit einem Sleep() trennst beim senden. Dem ist nicht so - dafür gibt es überhaupt keine Handhabe. Da dies in deinem LAN funktioniert sagt nichts aus.
  • Dein Splitten des Strings beachtet keine Verschiebung, falls z.B. das Sleep() mal nicht gewirkt hat oder noch ein Zeichen vom letzten Kommando im Stream war.
  • OnFileReadF(): Du schaust nirgendwo, wieviel überhaupt empfangen wurde. Was ist, wenn du vom Socket aufgerufen wird aber dieser gerade mal "Date" empfangen hat? Dein Vergleich geht schief und er schaltet nie in den Datenmodus, da er dieses niemals erreicht.
  • OnFileReadF(): du beachtest überhaupt nicht wieviel eigentlich noch zur Datei gehört. Du vergleichst auf grösser gleich und schreibst alles was im Socket Buffer ist direkt in die Datei. Somit wird die Datei mit deinem Chat sogar grösser nach der Übertragung.

Das wolltest du bestimmt nicht hören, aber es sind viele Faktoren die gegen eine sichere Funktionsweise des Chats sprechen.

Zacherl 24. Sep 2006 21:13

Re: ServerSocket1ClientRead
 
Nagut, du hast mich überzeugt :wink: Wobei ich zu meiner Verteidigung erwähnen muss, dass dies eine meiner ersten Spielereien mit den Sockets war ...

Florian

Muetze1 24. Sep 2006 21:56

Re: ServerSocket1ClientRead
 
Zitat:

Zitat von Florian Bernd
Nagut, du hast mich überzeugt :wink: Wobei ich zu meiner Verteidigung erwähnen muss, dass dies eine meiner ersten Spielereien mit den Sockets war ...

Man kann auch nicht alles sofort wissen und können. Ich hatte mich mal zwangsweise mit den Sockets ein wenig auseinander setzen müssen. Man lernt eh mit der Zeit und den Projekten...

DelphiSourcer 26. Sep 2006 11:14

Re: ServerSocket1ClientRead
 
Hallo Delphi Freunde!

Bin grad dabei, mich mit WinSockets zu besch„ftigen. IN DSDT gibt es dazu ein Handbuch und ein
Programmbeispiel. Das habe ich mir runtergeladen, um jetzt damit experimentieren zu können.
Leider habe ich ein Problem, das ich selber nicht so schnell lösen kann. Hier zuerst der Quelltext:

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ScktComp, StdCtrls;

type
  TForm1 = class(TForm)
    btnStart: TButton;
    btnStop: TButton;
    btnSend: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    procedure btnStartClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure btnSendClick(Sender: TObject);
    procedure ServerClientRead(Sender: TObject; Socket: TCustomWinSocket);
    procedure ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    Client: TClientSocket;
    Server: TServerSocket;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnStartClick(Sender: TObject);
begin

  Server.Port:=44044;
  Client.Port:=44044;
  Client.Host:='127.0.0.1'; //Localhost
  Server.Open;
  Client.Open;

end;

procedure TForm1.btnStopClick(Sender: TObject);
begin

  Client.Close;
  Server.Close;

end;

procedure TForm1.btnSendClick(Sender: TObject);
begin

  if Client.Active = true then
  begin
     Client.Socket.SendText(Edit1.Text);
     Edit1.Text:='';
     ServerClientRead(Sender,Client);
  end;
end;

procedure TForm1.ServerClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
begin

  Memo1.Lines.Add(Socket.ReceiveText);

end;

procedure TForm1.ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
begin

  Memo1.Lines.Add('***Connected.')

end;

procedure TForm1.ClientDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin

  Memo1.Lines.Add('***Disconnected.');

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   Client := TClientSocket.Create(self);
   Server := TServerSocket.Create(self);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
   Client.Free;
   Server.Free;
end;

end.
Wenn ich das Ereignis Senden auslöse <btnSendClick>, will ich den gesendeten Text im Memo sehen.
Warum ist in diser Methode der Aufruf von ServerClientRead() so wie oben nicht richtig? Wenn ich
btnSend anklicke, erhalte ich eine EAccessViolation. Muss ich vielleicht noch eine Connect-Methode
aufrufen, die im Beispiel nicht erwähnt, aber in den Tiefen der Socket-Klasse verborgen ist?

DelphiSourcer

SirThornberry 26. Sep 2006 11:19

Re: ServerSocket1ClientRead
 
hast du dir den Quelltext nur kopiert oder die Methoden richtig angelegt und zugewiesen? An welcher Adresse kommt deine AV?

Muetze1 26. Sep 2006 11:42

Re: ServerSocket1ClientRead
 
1. Der Aufruf von ServerClientRead() sollte so lauten:
Delphi-Quellcode:
ServerClientRead(Sender, Client.Socket);
2. Dieser Aufruf bringt aber nicht das gewünschte Ergebnis, weil afaik kann man aus dem Client Socket das eben gesendete nicht mit ReceiveText wieder heraus lesen (was ja innerhalb vom ServerClientread() getan wird), sondern ReceiveText liest nur alles empfangene wieder aus.

3. Wie SirThornberry schon erwähnte: alles zugewiesen im Objektinspektor?

DelphiSourcer 26. Sep 2006 12:51

Re: ServerSocket1ClientRead
 
Hallo!

Zitat:

hast du dir den Quelltext nur kopiert oder die Methoden richtig angelegt und zugewiesen? An welcher Adresse kommt deine AV?
Ich habe das Beispiel aus dem Delphi Treff Forum unter Tutorials 1:1 übernommen, da ich über keinerlei Erfahrung mit
Netzwerkprogrammierung verfüge und erst mal ein funktionierendes Grundgerüst haben will, an dem ich
alles weitere erlernen kann. Ich habe vorausgesetzt, das das Beispiel erst mal funzt. Macht es aber nicht!

Wenn ich die Methode ServerClientRead() zur Übernahme des Textes aus Edit1 in Memo1 in btnSendClick weglasse, wird im Memo
gar nix angezeigt. Und wenn ich sie aufrufe, kriege ich die Exception!

Leider ist das zugehörige Tutorial diesbezüglich sehr knapp gehalten. Ich brauche wohl eines,
das beim Urschleim anfängt. Irgendwas wird hier an Kenntnis vorausgesetzt, was ich noch nicht kann.
An Delphi liegt es nicht, da hab ich genug Erfahrung. Es liegt an der Programmierung der Socket-Komponenten,
die ich noch nicht beherrsche, weshalb ich ja auch hier anfrage! Netzwerkprogrammierung muss doch spätestens seit
Turbo Delphi nicht mehr Spezialisten vorbehalten bleiben.

[OT]
Bin in Hartz IV und brauche daeshal dringend nen Job.

Warum nicht programmieren in der Zeit, in der ich jetzt nix zu tun habe. Ich brauche das Geld. Und ich habe den
Thread "Agenda 2010 - Menschenrechtsverletzung?" damals verfolgt. Und ich gehöre besimmt nicht zu denen,
die einfach nur zu faul zum Arbeiten sind, wie der Grundtenor dieses Threads, zumindest von der Mehrheit
der Diskutanten aussagt. Aber um kommerziell programmieren zu können, muss ich wohl oder übel Netzwerkprogrammierung
beherrschen, wenigstens, wenn ich die mir zur Verfügung stehenden Auftraggeber bedienen will.
[/OT]

DelphiSourcer

Muetze1 26. Sep 2006 13:55

Re: ServerSocket1ClientRead
 
Zitat:

Zitat von DelphiSourcer
Ich habe das Beispiel aus dem Delphi Treff Forum unter Tutorials 1:1 übernommen, da ich über keinerlei Erfahrung mit Netzwerkprogrammierung verfüge und erst mal ein funktionierendes Grundgerüst haben will, an dem ich
alles weitere erlernen kann. Ich habe vorausgesetzt, das das Beispiel erst mal funzt. Macht es aber nicht!

Wenn ich die Methode ServerClientRead() zur Übernahme des Textes aus Edit1 in Memo1 in btnSendClick weglasse, wird im Memo
gar nix angezeigt. Und wenn ich sie aufrufe, kriege ich die Exception!

Und nocheinmal: es wird keine spezielle Grundkenntnisse vorausgesetzt in Sachen Netzwerkprogrammierung sondern nur in Sachen Delphi. Wenn du in Sachen Delphi genügend Erfahrungen hast, dann solltest du mit dem integrierten Debugger umgehen können und uns sagen können bei welchem Zugriff es knallt. Genauso könntest du mit dem Debugger nachschauen, ob FormCreate() und FormDestroy() ordentlich aufgerufen werden.

Die Routinen können eigentlich nur dann eine AV verursachen, wenn die Instanzen von TServerSocket und TClientSocket (also die Klassenvariable Client und Server) nicht erstellt wurden und somit Client = Nil ist und Server genauso. Und dass bedeutet, dass der Form deines Projektes wahrscheinlich im Objektinspektor beim OnCreate nicht das FormCreate eingetragen wurde und im OnDestroy nicht das FormDestroy. Dieses könntest du im Objektinspektor nachschauen (und genau darauf wollte ich mit meinem 3. Punkt hinaus, somit meinte SirThomberry das gleiche). Und ich wette hier einfach mal frecherweise um einen Smiley, dass genau dies nicht gemacht wurde und genau dies nicht überprüft wurde.

Folgendes gilt, wenn ich den Smiley verloren habe:
Wenn du mit Delphi Erfahrung hast, sollten solche Dinge keine Hürde darstellen. Dann sollte der Umgang mit dem Debugger wenigstens grundlegend beherrscht werden und genauso der Umgang mit der IDE (hier vor allem Objekt Inspektor). Da nützt es nichts so eine OT Erklärung hier zu schreiben, weil das interessiert nicht. Es geht um das programmieren und da helfen wir gerne - egal ob Hartz IV, Festangestellter oder Freischaffend. Das ist unerheblich und tut nix zur Sache und hat nix damit zu tun wie hier einem geholfen wird.

flossinger 26. Sep 2006 20:33

Re: ServerSocket1ClientRead
 
@ delphisourcer

Du kannst eine Ereignisprozedur nicht selbst aufrufen, sondern must sie als solche definieren.
Dein Beispiel funktioniert, wenn du folgendes änderst (habe ich getestet):
Delphi-Quellcode:
procedure TForm1.btnSendClick(Sender: TObject);
begin
  if Client.Active = true then
  begin
     Client.Socket.SendText(Edit1.Text);
     Edit1.Text:='';
//     ServerClientRead(Sender,Client); Das ist sinnlos und zu entfernen
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   Client := TClientSocket.Create(self);
   Server := TServerSocket.Create(self);
   server.OnClientRead:=serverclientread; // so wird servercientread als die onread ereignisprozedur definiert
end;
grüsse,
der flossinger

DelphiSourcer 26. Sep 2006 20:40

Re: ServerSocket1ClientRead
 
Hallo flossinger!

Danke Dir, Das war der entscheidende Tipp. Habe die Zeile mit OnClintRead := ServerClientRead() in FormCreate ergänzt und erhalte das gewünschte Ergebnis.

Schönen Abend noch wünscht

DelphiSourcer

Muetze1 26. Sep 2006 21:30

Re: ServerSocket1ClientRead
 
Zitat:

Zitat von DelphiSourcer
Danke Dir, Das war der entscheidende Tipp. Habe die Zeile mit OnClintRead := ServerClientRead() in FormCreate ergänzt und erhalte das gewünschte Ergebnis.

Ok, hier der Smiley: :wall:

Mit anderen Worten, du hast nicht alle Ereignisse im Objektinspektor zugewiesen/überprüft. :roll:


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:10 Uhr.

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