Entwickler-Ecke

Internet / Netzwerk - CLient / Host Anwendung schreiben


alexschultze - Fr 25.10.02 17:04
Titel: CLient / Host Anwendung schreiben
hiho

ich hab gleich noch ne Frage :)

also: ich habe vor ein komplexes Programm zu schreiben.

Darin kommt vor, das ein CLient mit einem Host kommunizieren soll. (über Internet). Es werden hauptsächlich Strings und Integer gesendet.

Was für eine Komponente (für Komponenten) sollte ich nehmen? Was ist am Besten?

Ich dachte so an UDP oder TCPIP.


Es handelt sich um keinen Egoshooter, welcher unbedingt UDP benötigt, aber die Pakete sollten nicht ewig unterwegs sein, und mit möglichst großer Sicherheit (korrekt) ankommen.
Ein Mix aus UDP und TCP/IP ?

Oder was ganz anderes?

Warte gespannt auf eure Vorschläge, Alex ;)


Hagbard Celine - Fr 25.10.02 17:17

Schau Dir doch als erstes mal die ganzen Diskussionen über Chat programmierung an! Da sollten doch all deine Fragen beantwortet werden. Da Du nur Strings hin und her jagen möchtest ist das ja auch nur eine Form eines Chats!


alexschultze - Fr 25.10.02 21:35

und Integer?
Es wäre super wenn ich eine Art Record durchschleifen könnte!!!!!!

irgendein einzelner String, oder auch schon Stringliste wäre nur umständlich .. :/

(dauert lange bis mir 'record' einfällt *g*)
Strings und Integer gemixt! ;)

alex


Hagbard Celine - Sa 26.10.02 10:38

alexschultze hat folgendes geschrieben:
und Integer?
Es wäre super wenn ich eine Art Record durchschleifen könnte!!!!!!

irgendein einzelner String, oder auch schon Stringliste wäre nur umständlich .. :/


Wäre mir neu! Bin aber auch kein Meister in Sockets! Aber soviel mir bekannt ist kommst Du nicht darum herrum! Stringlists sind aber sehr praktisch! Da kannst Du beliebig viele Werte in einem Zug übertragen! Ich erzeuge mir grundsätzlich eigene Objekte und übertrage deren Daten per XML! Ist sehr praktisch!
Da kannst Du wirklich alle Datenstrukturen abbilden!


alexschultze - Sa 26.10.02 11:29

??????


Hagbard Celine - Sa 26.10.02 11:42

alexschultze hat folgendes geschrieben:
??????

Ein Record ist eine Datenstruktur!

Sockets schicken aber nur Zeichenketten!
Du musst also deine Datenstruktur in eine Zeichenkette formatieren!
XML ist eine Möglichkeit Datenstrukturen abzubilden!


alexschultze - Sa 26.10.02 14:22

ok, das hab ich kapiert ;) klingt gut ;)

und wie wird das realisiert?


O'rallY - Sa 26.10.02 15:34

Hagbard Celine hat folgendes geschrieben:
Bin aber auch kein Meister in Sockets!

Das stimmt wohl! :twisted: :wink:
Hagbard Celine hat folgendes geschrieben:

Sockets schicken aber nur Zeichenketten!

..., denn Sockets verschicken sehr wohl auch Records, und zwar mittels SendBuf und ReceiveBuf! Ein Beispiel zur Veranschaulichung:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
{der Record}
type
   TMyRecord = record
      s1, s2: string;
      i1, i2, i3: integer;
   end;

...
var
   MyRecord: TMyRecord;
...

{zum senden:}
//Daten des Records "MyRecord" werden verschickt
Socket.SendBuf(MyRecord, SizeOf(TMyRecord)); {auf das T achten, da es sich hierbei um den Datentyp handelt}

{zum empfangen}
//empfangene Daten werden in "MyRecord" gespeichert
Socket.ReceiveBuf(MyRecord, SizeOf(TMyRecord));


Es muss also nicht der "unkonservative" (:wink:) Weg über eine Stringlist sein.


alexschultze - Sa 26.10.02 16:16

prima! prima!

Welche Komponente soll ich also genau nehmen?
TSocketServer / Client ? (ist das INDY?)

kann ich da noch ein String 'davor' schreiben?
Das also vor dem Buffer mit dem Record immer noch ein String kommt?
Es werden mehrere User eingeloggt sein, und ich glaub es wäre am besten wenn immer bei jedem paket der Benutzername 'vorn' steht.

alex


alexschultze - So 27.10.02 10:24

also, ich hab mich jetzt mal reingearbeitet, in ServerSocket und CLientSocket.

Sehr praktisch.
Man kann Streams, Buffer und Lines (Strings) versenden.

Aber: wie kann man die beim empfangen unterscheiden?
P.s: Wie sieht es mit der Verteilung der Sockets aus? Wird immer hochgezählt? Wenn z.b. 1-2-3 einloggen und 1 ausloggt, würde der 4.te der einloggt auf socket 1 oder 4 landen?


alex


O'rallY - So 27.10.02 10:34

Zitat:
Aber: wie kann man die beim empfangen unterscheiden?

Das ist eine schon oft gestellte Frage im Forum, doch hat bisher noch keiner bestätigen könne, das dies geht. Man kann dem Problem aber durch einen Umweg begegnen, indem man vor dem Senden von Records eine String sendet, um anzukündigen, was jetzt gleich kommt. (so in der Art 'ich bin ein Record, und ich komm jetzt').


alexschultze - So 27.10.02 11:29

okay, da muss ich aber noch ein bisschen mehr über die sockets und deren Verteilung erfahen!
Wie ist das mit den Socket? (1-2-3-4 wie oben)


O'rallY - So 27.10.02 13:39

Das weiß ich ehrlich gesagt nicht so genau, doch ich vermute, das wenn sich einer ausloggt, die anderen Connections nachrücken. Wenn sich also 1 ausloggtt ist 2 dann 1, 3 dann 2, usw.
Demnach würde der 4. der sich einloggt wieder auf dem letzten Platz landen.


alexschultze - So 27.10.02 14:54

Also, rausgefunden:

Sobald bei 1-2-3-4 der User 1 rausgeht, 'rutschen' alle nach.
2 wird 1, 3 wird 2, 4 wird 3 etc.


alexschultze - Di 29.10.02 19:35

wie kann ich ein Paket direkt an einen User (von mehreren) verbundenen senden?
Kann sein das connections[0] als 'Alle Connections' gilt?

alex


Hagbard Celine - Di 29.10.02 20:01

alexschultze hat folgendes geschrieben:
wie kann ich ein Paket direkt an einen User (von mehreren) verbundenen senden?
Kann sein das connections[0] als 'Alle Connections' gilt?

alex


Die Indizes beginnen wie bei den meisten (fast allen) Arrays mit 0, das ist also kein verweis auf alle Clients! Um die User zu unterscheiden verwende ich den Zeiger Data des Socket Objekts. Da hänge ich einfach ein Objekt ran in welchem ich alle Identifikationsmerkmale speichere!

Um also an einen bestimmten Client zu senden brauchst Du nur den Index des Clients und los gehts!


Hagbard Celine - Di 29.10.02 20:04

O'rallY hat folgendes geschrieben:
..., denn Sockets verschicken sehr wohl auch Records, und zwar mittels SendBuf und ReceiveBuf!


Geht das auch mit Objekten????

Wenn ja, welche vorraussetzung muss gegeben sein?????
Wie verarbeite ich das auf der anderen Seite????


alexschultze - Di 29.10.02 21:13

ich hab hier noch nicht den Durchblick:

Wie kann ich mithilfe des Sockets den User eindeutig indentifizieren?!?


O'rallY - Di 29.10.02 21:38

Hagbard Celine hat folgendes geschrieben:
Geht das auch mit Objekten????

Ich hab's ehrlich gesagt noch nicht ausprobiert, behaupte aber einfach mal, das sich dies genau wie mit Records verhält (logischerweise werden die Methoden wohl nicht mitgeschickt, doch wie es sich mit den Methoden genau verhält weiß ich nicht, musst du halt mal ausprobierten). Lasse mich aber gern eines besseren belehren...

alexschultze hat folgendes geschrieben:
Wie kann ich mithilfe des Sockets den User eindeutig indentifizieren?!?

Wie meinst du das?

Wenn beim Server z.B. das OnClientRead-Event ausgeführt wird, kann man, wenn man einfach nur Socket. {...} schreibt, ohne Connections, auf die aktuelle Verbindung zugreifen (das kannst du z.B. mal so testen, dass du jedesmal die Socket.RemoteAddress ausgibst :wink: ).
Um aber auf einen, zwar verbundenen, aber nicht aktiven Client zu zugreifen, benutzt man die Eigentschaft Socket.Connections[n].

Ich hoffe du hasts jetzt verstanden, ansonsten, frag einfach nochmal nach.

P.S.: Du kennst nicht zufälligerweise einen Alexander Schultze aus Lennestadt? (Wenn dir der Ort nix sagt, auch gut, würde meine Frage beantworten *g* )


alexschultze - Mi 30.10.02 14:16

sorry, kenn ich nicht.

ich komm aus le.. le.. leipzig ;)


O'rallY - Mi 30.10.02 21:27

Ok, auch gut! Dann weißt du jetzt, dass du nen Namensvetter hast (<- nennt man doch so?) :mrgeen:


alexschultze - Di 05.11.02 16:02

also, folgendes:

ich habe jetzt experimentiert und experimentiert, und wenn jetzt der Server aus ist oder der Port nicht stimmt, kommt immer der Ausnahmefehler 'Asynchroner Socketfehler 10061'.
Ich will nett unbedingt das die Benutzer das sehen, sondern eher 'Fehler beim Zugriff'.

Was soll ich tun? :cry:


O'rallY - Mi 06.11.02 15:15

Guck mal in meinen FAQ-Eintrag über Fehlerbehandlung oder nutze einfach das OnError-Event, um den Fehler abzufangen:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TForm1.ClientSocket1OnError(..., ErrorCode: integer);
begin

case ErrorCode of
   10062: ShowMessage('Verbindung fehlgeschlagen!");
end;

Abort;

end;


alexschultze - Mi 06.11.02 15:49

danke! ich hab schon kurz mit onerrorevent rumgespielt aber immer so ein abort vergessen.

Danke!!!!! :lol:


ebi - Mi 13.11.02 17:08

Schaut bitte mal in http://www.auq.de/viewtopic.php?p=19754#19754...

Es geht um OnError.

Thx


alexschultze - Sa 23.11.02 12:57

ehm, ich hab hier noch ein Problem mit Records. Wenn ich

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var
a:integer;
   MyRecord: TMyRecord;

begin

type
   TMyRecord = record
      s1, s2: string;
      i1, i2, i3: integer;
   end;


Clientsocket1.Socket.SendBuf(MyRecord, SizeOf(TMyRecord));

versuche, meckert er, das er TMyRecord nicht kennt. Was tun?!!?


ebi - Sa 23.11.02 13:43

Ganz einfach!!

8)


Du mußt erst den TMyRecord definieren und dann kannst du erst mit einer Variable drauf zugreifen.


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
type 
   TMyRecord = record 
      s1, s2: string; 
      i1, i2, i3: integer; 
   end; 


[...]


var 
   a:integer; 
   MyRecord: TMyRecord;



Soo einfach! :wink:


alexschultze - Sa 23.11.02 14:16

ehm, noch ein kleines Problem:
ich sende immer in einem Zuge:

Quelltext
1:
2:
3:
Clientsocket1.Socket.SendText('Daten');

Clientsocket1.Socket.SendBuf(MyRecord, SizeOf(TMyRecord));



Nun kann ich das ganze empfangen, beim Server kommt dann immer bei ReceiveText
Daten,]E

D.h. also er hat beide Pakete in eines zusammengepresst. Kann ich jetzt einfach die ersten 5 Buchstaben (alswie den Text) aus dem ReceiveBuf wieder rauslöschen? (sonst krieg ich den Record ja nicht wieder hin)


ebi - Sa 23.11.02 21:27

Ne, das geht nicht

du mußt schon vorgeben, was empfangen werden soll, d.h. ein RECORD!!!


Schreibe doch in den Record, dass das Daten sind!
Und schicke nur einen RECORD!!!!


Viel Spass noch


alexschultze - Sa 23.11.02 23:20
Titel: Eh eh eh
ehm?

Du willst mir damit sagen dass mein Programm ausschließlich Records verwenden soll, oder das, wenn ein Record gesendet wird, ein String vorhanden ist = 'Daten', und mein Empfänger, egal ob es ein String oder ein Record ist, versucht dieses String aus dem Record zu lesen, und wenn der String ='Daten' das ganze als Record behandeln?

Was nun?


Savage - So 24.11.02 11:48

Hi,

verwende einfach 2 Datenkanäle, den einen für Befehle und den anderen für deine Records, damit verhinderst du auf jedemfall, dass sich verschiedene Daten zusammenschließen. Dadurch lassen sich deine Daten dann besser "steuern".

MfG
Savage


alexschultze - So 24.11.02 12:02

das kann ich doch nieeee koordinieren!


Savage - So 24.11.02 12:23

hmm, dann darfst du die 2 Daten nicht hintereinander senden sondern getrennt.

z.B.
Du sendest zuerst den String 'Daten', der Empfänger empfängt diesen String, bereitet sich auf die Daten vor und sendet zB ein 'DatenOK' zurück, nachdem du diesen String wieder empfangen hast, sendest du erst den Record rüber. Es entsteht also eine nette kleine Kommunikation. Die Datenkommunikation bei SMTP Servern oder POP Servern ist ähnlich aufgebaut.

Savage


alexschultze - So 24.11.02 12:46

aber bei 100 Clienten ist das recht schwer zu koordinieren (dann immer die richtigen Daten zu senden). Ich schreib hier mal ein Programm das ich gefunden habe. Es sendet einen Screenshot, und zwar mit nur EINER Prozedur:


Quelltext
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:
87:
88:
89:
90:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button2: TButton;
    ServerSocket1: TServerSocket;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure ServerSocket1ClientConnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ServerSocket1ClientDisconnect(Sender: TObject;
      Socket: TCustomWinSocket);
  private
    { Private-Deklarationen }
    FBitmap: TBitmap;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FBitmap := TBitmap.Create;
  FBitmap.Width := 800;
  FBitmap.Height := 600;
  FBitmap.PixelFormat := pf8bit;
end;

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

procedure TForm1.Button2Click(Sender: TObject);
var
  dc: hdc;
  dx, dy: integer;
  i: integer;
  FStream: TMemoryStream;
  Buf: Pointer;
begin
  dc := CreateDC('DISPLAY', Nil, Nil, Nil);
  dx := screen.Width;
  dy := screen.Height;
  BitBlt(FBitmap.Canvas.Handle, 0, 0, dx, dy, DC, 0, 0, SRCCopy);
  deletedc(dc);
  Image1.Picture.Assign(FBitmap);
  FStream := TMemoryStream.Create;
  FBitmap.SaveToStream(FStream);
  Buf := FStream.Memory;
  with ServerSocket1.Socket do
  begin
    for i := 0 to ActiveConnections - 1 do
    begin
      Connections[i].SendText('Daten'
        + IntToStr(FStream.Size));
      Connections[i].SendBuf(Buf^, FStream.Size);
    end; {for i := 0 to ActiveConnections do}
  end; {with ServerSocket1.Socket do}
end;

procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  Caption := 'Server '
    + IntToStr(ServerSocket1.Socket.ActiveConnections);
end;

procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  Caption := 'Server '
    + IntToStr(ServerSocket1.Socket.ActiveConnections - 1);
end;

end.


Connections[i].SendText('Daten'
+ IntToStr(FStream.Size));
Connections[i].SendBuf(Buf^, FStream.Size); in einer Prozedur:

und der andere:


Quelltext
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:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Image1: TImage;
    ClientSocket1: TClientSocket;
    MainMenu1: TMainMenu;
    Connect1: TMenuItem;
    Disconnect1: TMenuItem;
    procedure ClientSocket1Connect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Connect1Click(Sender: TObject);
    procedure Disconnect1Click(Sender: TObject);
  private
    { Private-Deklarationen }
    FStream: TMemoryStream;
    FPos: integer;
    FSize: integer;
    FDaten: boolean;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ClientSocket1Connect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  Caption := 'Verbunden';
end;

procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var
  a: array[0..4096] of Byte;
  Buf: Pointer;
  i: integer;
  s: string;
begin
  if FDaten = false then
  begin
    s := Socket.ReceiveText;
    if Copy(s, 1, 5) = 'Daten' then
    begin
      Delete(s, 1, 5);
      try
        FSize := StrToInt(s);
        FDaten := true;
        Image1.Picture.Bitmap := nil;
        FStream.Clear;
      except

      end;
    end; {if Copy(s, 1, 5) = 'Daten' then}
  end {if FDaten = false then}
  else
  begin
    Buf := @a;
    i := Socket.ReceiveBuf(Buf^, 4096);
    FStream.Write(Buf^, i);
    FPos := FPos + i;
    if FPos >= FSize then
    begin
      FDaten := false;
      FStream.Position := 0;
      FPos := 0;
      Image1.Picture.Bitmap.LoadFromStream(FStream);
    end;
  end; {else FDaten = false then}
end; {procedure TForm1.ClientSocket1Read}

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ClientSocket1.Close;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FStream := TMemoryStream.Create;
end;

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

procedure TForm1.Connect1Click(Sender: TObject);
var
  s: string;
begin
  if InputQuery('Server', 'Host-Name', s) = true then
  begin
    ClientSocket1.Host := s;
    ClientSocket1.Open;
  end;
end;

procedure TForm1.Disconnect1Click(Sender: TObject);
begin
  ClientSocket1.Close;
end;

end.

Empfangen mit einer Prozedur mit einem mal.

Ich blicke hier nur nicht ganz durch:
Er sendet zwei Sachen auf einmal, kriegt die wieder auseinander.. ??


alexschultze - So 01.12.02 19:30

antwortet mal :(


Savage - So 01.12.02 20:28

nun ja,

den Source, den du gepostet hast, funktioniert im Prinzip so:

Sender:

sendet mit einen Rutsch den String 'Daten' + Größe der File
also z.B. "Daten234534' und gleich danach als Buffer die File bzw. Nutzlast.

Empfänger:

Und hier aber meines erachtens das gleiche Problem wie bei dir. Der Empfänger geht davon aus, dass der String und die Nutzlast getrennt ankommen. Das Sieht man an der If Klausel, die heist:
IF FDaten = false then {nach String 'Daten' prüfen,...} else {Nutzlast empfangen und schreiben}

Und jetzt kann es meines erachtens wieder je nach Netzwerkspeed zu fehlern kommen. Es dürfte als keine wirkliche alternative sein. Ich lass mich jedoch gern eines besseren belehren.



Mein Vorschalg:

Dein Sendevorgang wird immer mit einen bestimmten String angekündigt.
Deshalb kannst du ja mal folgendes versuchen: Definiere einen bestimmten Bereich mit z.B. der Länge 10 Byte (10 Zeichen für deine Befehlsstring müsste reichen). Danach schreibst du diesen Bereich VOR deinen Record (wenn du als einen Stream zum senden verwenden solltest, dann schreib zuerst in den Stream die 10 Zeichen und danach die Nutzlast, und dann sende den STream.

Der Empfänger empfäng die Daten und schneidet von diesen Paket die ersten 10 Zeichen ab, wertet diese aus und bearbeitet den rest nach deinen Wünschen weiter.

Mehr fällt mir dazu leider nicht ein.

MFG

Savage


alexschultze - So 01.12.02 20:35

Genau diese Idee hatte ich auch!!!!

Ich wusste nur nicht wie ich dann beim Empfänger beim Paket den Anfang (die 10 Byte) wegschneiden und dann immer noch das Paket weiterverwenden kann!


Savage - So 01.12.02 20:45

hm,

schreib den gesamten empfangenen Buffer in einen TMemorystream,
lies z.B. die ersten 10 Byte aus und speichere diese in einen String --> wegen weiterer Auswertung.

Danach würde ich den Memorystream auf Position 10 setzten und den Rest (memorystream.size-10) so speichern, wie ich ihn brauch. Wenn es ne File werden soll, dann

z.B filestream.copyfrom(memorystream,memorystream.size-10)

So in etwa würd ich es machen, viel Glück!


Mfg
Savage


alexschultze - Mo 02.12.02 14:12

okay, klingt gut.


alexschultze - Fr 06.12.02 22:02

also,

ich habe folgendes vor:
Ich schicke Text 'Record' plus gleich den Record der hinterher kommt.
WIe lese ich den Record ohne die ersten 6 Zeichen wieder aus?
Meine erfolglosen Versuche (hab schon mehr oder weniger rumgemanscht *g*)
Zitat:

Memo ist ein TmemoryStream
Record selbstklärend der Zielrecord.

Quelltext
1:
2:
3:
socket.ReceiveBuf(memo, sizeof(TMemoryStream)); 
//memo.copyfrom(,memorystream.size-6) 
memo.write(RecRecord, SizeOf(TMyRecord));


Alles mehr oder weniger erfolglos.

Ob man über einen weiteren Buffer, ein MemoryStream geht oder sonstwas ist egal, hauptsache der Record kommt wieder :(

Wie mach ich das ?


alexschultze - Sa 07.12.02 12:29

ich hab jetzt wieder ausprobiert:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
 memostr:=Tmemorystream.create;

 memostr2:=Tmemorystream.create;

socket.ReceiveBuf(memostr, sizeof(TMemoryStream));
memostr2.copyfrom(memostr,memostr.size-6);


memostr2.write(RecRecord, SizeOf(TMyRecord));

anstelle von memostr.size-6 hatte ich schon:
memostr.size+6
sizeof(tmemorystream)+6
sizeof(tmemorystream)-6

alles ohne Erfolg :(((

bin ratlos :(


alexschultze - Sa 07.12.02 20:06

ich hab rausgefunden, dass:

Server:

Quelltext
1:
2:
3:
sendrecord.s1:='Zonk';
sendrecord.s1:='Döner Kebab';
Socket.SendBuf(Sendrecord, SizeOf(TMyRecord));

Client:

Quelltext
1:
2:
3:
4:
socket.ReceiveBuf(recrecord, sizeof(TMyRecord));

showmessage(RecRecord.s1);
showmessage(RecRecord.s2);

schon nicht geht!!!!!!!

Was ist falsch? Was ist zu verbessern?
*help* ist dringend :((


Savage - So 08.12.02 10:19

Hallo,

ich habe mir das ganze nochmal angeschaut.
Ich würde das was ich sende und empfange zuerst in einen Memorystream packen. Also ich würde nicht SendBuf verwenden sondern sendstream.

Ich Zeige dir im unteren Teil, wie man aus einen String mit fester länge und einen Record einen Memorystream macht und ihn wieder auseinander nimmt.
Wie man den empfangenen Buffer in einen Stream umwandelt.

Also ein paar Grundlagen, die du zB verwenden kannst.

Bsp1:
Ich habe einen Record mit 2 ShortString's geschrieben und verwende diesen als myrec. Ich erstelle einen Memorystream und speichere dort zuerst einen Befehl mit der länge 5 und danach den Record. Danach lese ich aus den Stream den Befehl und den Record. Verwende also den Stream als Zwischenstation.


Quelltext
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:
type
   myrec = record
    rec1 : ShortString;
    rec2 : Shortstring;
 end;

...

var ms : TMemorystream;
    mrec,mrec2 : Myrec;
    i : Integer;
    s,s2 : Shortstring;
begin

mrec.rec1 := 'Zeile 1';
mrec.rec2 := 'Zeile 2';
s := 'Hallo';
i := Sizeof(mrec);

ms := TMemorystream.Create;

// String + Record in Stream schreiben
ms.WriteBuffer(s,6);         // ! länge des Strings + 1
ms.writeBuffer(mrec,i);

//String + Record wieder extrahieren
ms.Position := 0;             // !
ms.ReadBuffer(s2,6);      // ! länge des Strings + 1
ms.position := 6;            // !
ms.Readbuffer(mrec2,i);

//Testen
memo1.Lines.Add(mrec2.rec1);
memo1.Lines.Add(mrec2.rec2);
memo1.lines.add(s2);

end;



Bsp 2:
Bei OnRead empfange ich einen Buffer, bei receivebuf glaube ich einen Pointer. Man muss also ersteinmal den Buffer in den Stream laden.


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var i : Integer;
    p : PChar;
    ms : TMemorystream;
begin

i := socket.ReceiveLength;
 getmem(p,i);                //Speicher reservieren
 socket.ReceiveBuf(p^,i);    //Puffer in den PCHar legen

 ms := TMemorystream.Create;
 ms.Write(p^,i);             // Puffer in einen Stream laden
 freemem(p);

end;


Danach hast du also den empfangen Buffer wieder in einen Stream und kannst ihn wie in Bsp2 extrahieren.

Das war die Variante, die ich immer verwende. Es geht bestimmt auch noch anders (vieleicht sogar einfacher), aber das müsste jetzt eigentlich funktionieren.

Viel spaß beim probieren :wink:

MfG
Savage


alexschultze - So 08.12.02 10:49

ich probiere schon seit Ewigkeiten ;)

ich hab jetzt noch ein paar Sachen herausgefunden:
wenn man zweimal hintereinander sendText macht, kommt bei receivetext beides zusammengepresst.

Weiter: wenn man sendText und SendBuf direkt hintereinander macht, werden das 2 Pakete.


Und wenn man ein receiveText o.ä. macht, dann werden der receivetext, als auch receivebuf geleert!

Ich glaub ich hab den Dreh raus ;)

ich probier noch ein bissl. aber deine Methode ist in mein momentan schon mit Strings arbeitenten Programm schwerlich zu integrieren. Aber ich schau mal.


Savage - So 08.12.02 10:59

aber du musst eines noch beachten. Wenn das Paket "sehr groß" ist, kann es auch passieren, dass ein Paket geteilt ankommt, sprich hintereinander in 2 Paketen.

Mir passiert das z.B., wenn ich eine HTML-Seite (ca. 40 kb) aus dem Inet über einer TClientsocket-Kompo downloade. Diese kommt bei mir öffters mal mit 2 oder gar 3 Stücken an. Ob das jetzt nur im HTTP-Verkehr so ist oder generel so, weis ich nicht, aber du kannst ja mal darauf achten.

MfG
Savage


alexschultze - So 08.12.02 11:10

das ist definitiv so und von der Buffergröße Abhängig (im langen Codebeispiel ist das ziemlich gut dargestellt, er nimmt die Pakete a 4096 Pakete ab, und zwar mehrmals hintereinander.
Meine Pakete werden die 4 KB-Größe nicht überschreiten, sind nur kleine Records!

also,
ich hab hier aktuell:

Server:

Quelltext
1:
2:
socket.SendText('Daten'+inttostr(sizeof(sendrecord)));
Socket.SendBuf(Sendrecord, SizeOf(sendrecord));


Client:

Quelltext
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:
procedure TForm4.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
if AwaitRecord=true then begin
 socket.ReceiveBuf(recrecord, awaitrecordsize);
showmessage(RecRecord.s1);
showmessage(RecRecord.s2);
awaitrecord:=false;
end;




received:=socket.ReceiveText;
form3.Label1.Caption:='Connected !';



if copy(received, 1, 5) = 'Daten' then begin
if (MatchesMask(received,'*´ÉM')) then begin
 clientsocket1.Socket.SendText('Record');
 abort;
end;


//showmessage(received);
memo1.Lines.Add(received);
Delete(received, 1, 5);
AwaitrecordSize := StrToInt(received);




Awaitrecord:=true;
end;


aber auch in dieser Reihenfolge bringt das immer ein leeres Ergebnis!!!
:(
Nun bin ich wirklich am verzweifeln. Ich experimentiere mal schnell mit statischen Strings, aber so wie das aktuell ist, gefällt mir das gar nett. Wieso geht das nett? :(

ach so, das (MatchesMask(received,'*´ÉM')) ist hiermit zu erklären:

Ab und zu (meist am Anfang des Programms) sendet der Server an den Clienten bei Text+Buf folgendes:
Daten20*´ÉM

fragt micht nicht wieso. Habt ihr ne Idee?
Wie ihr seht hab ich das umgangen in dem ich das Paket beim Server erneut anfordere, bis es fehlerfrei ist.


alexschultze - So 08.12.02 11:18

okay, ich arbeite jetzt erfolgreich mit statischen Records. Aber des öfteren kommen auch hier irgendwelche Zusammengepappten Informationen...

na, mal schaun


DataCool - Mo 09.12.02 23:59

Also, ich hab mir jetzt das gesamte Thema durchgelesen und muss sagen :
Das kommt mir sehr bekannt vor :wink:

Ich habe nämlich gerade erst vor 2 Monaten eine nicht kleine Client/Server Anwendung fertiggestellt, die verschiedene Datenbank miteinander abgleicht.

Aber zurück zum Thema :
Weg mit TClientSocket u. TServerSocket :twisted:

Glaubt mir ich habe fast zwei Wochen am Anfang meines Projekts damit verschwenden :bawling: bis ich dann auf Leidensgenossen in diversen Foren gestossen bin.
Die Lösung ist die Verwendung von den Indy-Komponten 8)

Im allgemeinen würde bei unterschiedlichen Daten zwei Ports zur kommunikation nutzen.
1. Port um Informationen auszutauschen, wie sende gleich Filestream mit der Größe 5MB oder File erfolgreich erhalten usw. ! Einfach halt ein kleines Nachrichten Protokoll entwerfen.

2. Port = Datenport
Dieser wird immer nur für Daten der Länge von xxxx Bytes geöffnet, wenn dies vorher auf Port 1 angekündigt wurde.

Das war auf jeden Fall im meinem Projekt notwendig, sonst hätte ich die Übersicht verloren, da das ganze Multi-Threaded abläuft. :mrgreen:


Wenn man mit einem Port auskommen möchte würde ich wie auch schon weiter oben erwähnt, Deine Daten in einer XML-Datenstruktur ablegen und diese über einen MemoryStream verschicken(aber mit Indy). :wink:

So, das wärs fürs erste. :mahn: :beer:

Sonst noch jemand irgentwelche anderen Erfahrungen ? :nut:


alexschultze - Mo 16.12.02 19:30

sagt mal, hat ein array of pchar mit 5000 Elementen eine Größe von 5000 Byte alswie ca. 5 kb?