Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Dateiaustausch zwischen clientsocket und serversocket..^^ (https://www.delphipraxis.net/100996-dateiaustausch-zwischen-clientsocket-und-serversocket-%5E%5E.html)

cRayZo 6. Okt 2007 17:56


Dateiaustausch zwischen clientsocket und serversocket..^^
 
(kurz vornweg, ja gesucht hab ich schon!)

Hi, wie kann ich eine beliebige Datei zwischen client und Server hin und herschicken?

Ich habe mir schon lange gedanken gemacht, aber ich komm einfach nciht auf die Lösung. Die suche hat mir auch nicht viel gebracht, da vieles mit indy erklärt wird. Soweit ich das aber verstanden habe, is das erst ab Delphi 7 verfügbar.. könnt ihr mir weiterhelfen? ich muss doch nur eine atei übertragen... ^^

thx schonmal :)

PS: Texte zu verschicken klappt problemlos. Ich weiß auch schon dass ich 'sendstream' nehmen sollte. Aber wie soll ich das mit sendstream verwirklichen?

DeddyH 6. Okt 2007 17:58

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Diesen Thread hast Du aber schon gesehen, oder?

SirThornberry 6. Okt 2007 17:59

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
wo genau ist dein problem? Ein Text ist auch nur eine ansamlung von Bytes. Genau wie eine Datei. Du kannst also eine Datei genau so versenden wie einen Text (vorrausgesetzt ich vermute dein Problem an der richtigen Stelle des Vorhabens). Vielleicht solltest du mal zeigen was du schon hast und wo es genau hängt.

cRayZo 6. Okt 2007 18:14

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
also hier meine "produktion" hüstel
ich hab mit type gearbeitet, ich weiß dass man das eig. nicht macht, aber ich hab iwie alles schon ausprobiert, und dann eben auch das.. naja

Client onklick
Delphi-Quellcode:
type
testrecord=record
var
screen:TBitmap;
begin
Varscreen: TBitmap; sendscreen:Testrecord;

with sendscreen do begin screen:=Varscreen; end;
Clientsocket1.Socket.SendBuf(Varscreen,SizeOf(Sendscreen));
Server onread
Delphi-Quellcode:
var
receivetext: string;
screnVar:TBitmap; incomScreen:testRecord;
begin
   QueryMemo.Lines.Add(DateTimeToStr(Now)+' Uhr: Bild wird geladen');
   socket.receivebuf(incomScreen, SizeOf(incomScreen)) ;
   with incomScreen do begin
   screnVar:=screen;
   end;
  // Image1.Picture.Assign(screnvar);
   image1.Picture.Bitmap:=screnVar;

   QueryMemo.Lines.Add(DateTimeToStr(Now)+' Uhr: Bild erhalten'); except showmessage('x'); end;

Muetze1 6. Okt 2007 18:32

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
TBitmap ist eine Klasse und diese sind intern nur Zeiger. Wenn du nun ein TBitmap versendest, dann sendest du nur die Adresse im Speicherbereich deiner Applikation, wo das Objekt liegt. Somit: Keine Daten und nichts. Der Empfänger kann damit nichts anfangen, da an der angegebenen Adresse in seinem Speicherbereich was ganz anderes liegt.

Vorschlag: schau dir mal in der Hilfe folgendes an und ich denke mal, du kommst auf die Idee, welche ich dir hier schmackhaft machen will: Delphi-Referenz durchsuchenTMemoryStream, Delphi-Referenz durchsuchenTBitmap.SaveToStream, Delphi-Referenz durchsuchenTCustomWinSocket.SendStream

cRayZo 6. Okt 2007 19:12

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
also dann sowas in der art oder wie?
(hier jetzt einfach mal am beispiel von einem Screenshot, den muss man nicht aus ner datei laden)

Client
Delphi-Quellcode:
procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Stream : TMemoryStream;
begin
  Stream := TMemoryStream.Create;
clientsocket1.Socket.ReceiveBuf(stream,sizeof(stream));
image1.Picture.Bitmap.LoadFromStream(stream);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ClientSocket1.Port := 270; //Festlegung des Ports
  ClientSocket1.Host := '127.0.0.1'; //IP des Zielrechners
  ClientSocket1.active := true; //Aufbau der Verbindung
end;
Server
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
begin
  ServerSocket1.Port := 270; //Festlegung des Ports
  ServerSocket1.active := true; //Aktivierung der ServerSocket
end;

{procedure MakeScreenShot(const ATarget: TBitmap);
var
  DesktopDC: HDC;
begin
  DesktopDC := CreateDC('DISPLAY', nil, nil, nil);
  try
    ATarget.PixelFormat := pfDevice;
    ATarget.Width := Screen.Width;
    ATarget.Height := Screen.Height;

    BitBlt(ATarget.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DesktopDC, 0, 0, SRCCOPY);
  finally
    DeleteDC(DesktopDC);
  end;
end;} <--irrelevant

procedure TForm1.Button1Click(Sender: TObject);
var
  Stream : TMemoryStream;
  bitmap:Tbitmap;
begin
  Stream := TMemoryStream.Create;
  MakeScreenShot(bitmap);
  bitmap.SaveToStream(Stream);
  Stream.Position := 0;
  Stream.Free;
  Serversocket1.Socket.Connections[0].SendStream(Stream);
end;

Muetze1 6. Okt 2007 19:22

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Zitat:

Zitat von cRayZo
also dann sowas in der art oder wie?

Ja, aber ich dachte da nun nur noch an Code, der funktioniert :wink: . Aber schauen wir mal, was du gemacht hast...

Delphi-Quellcode:
procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Stream : TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  clientsocket1.Socket.ReceiveBuf(stream,sizeof(stream));
  image1.Picture.Bitmap.LoadFromStream(stream);
end;
Das wird zu Problemen führen, weil die OnClientRead() Funktion mehrfach aufgerufen wird (werden kann), bis du die gesamten Daten empfangen hast. Somit musst du den Memorystream woanders erzeugen (so das du wirklich nur eine Instanz davon hast) und damit du mit jedem OnClientRead die neu empfangenen Teile direkt hinten an den MemoryStream anhängen kannst. Wenn du dann alle Daten empfangen hast, dann kannst du aus dem MemoryStream die Daten auslesen.

Folgende Fehler in deiner Routine:
1. Du erzeugst jedes mal eine neue Instanz von TMemoryStream - du brauchst nur eine
2. Du gibst die Instanz bei ReceiveBuf() an, aber der will einen Zwischenpuffer haben. Du kannst die Instanz niemals bei dieser Funktion direkt angeben.
3. Instanzen sind intern nur Zeiger auf das Objekt im Speicher - siehe mein Beitrag zuvor. Was für das Senden gilt, gilt hier genauso für das Empfangen, schliesslich sind es jedesmal Instanzen.
4. Nach dem Schreiben von Daten in den Stream, wird der Dateizeiger (Delphi-Referenz durchsuchenTStream.Position) auch um die jeweilige Byteanzahl weiter gesetzt und LoadFromStream() liest aber der aktuellen Dateiposition. Damit würde LoadFromStream() immer fehlschlagen, da er immer am Ende der Daten steht.
5. Du gibst nirgendwo den Stream wieder frei.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Stream : TMemoryStream;
  bitmap:Tbitmap;
begin
  Stream := TMemoryStream.Create;
  MakeScreenShot(bitmap);
  bitmap.SaveToStream(Stream);
  Stream.Position := 0;
  Stream.Free;
  Serversocket1.Socket.Connections[0].SendStream(Stream);
end;
1. Wenn du den Stream mit .Free wieder freigibst (und er somit weg ist), was soll er denn bei dem Aufruf danach noch versenden? Datenmüll? Naja, das macht er bestenfalls sogar.
2. Den Stream grundsätzlich nicht freigeben, weil SendStream() übernimmt diesen Part für dich (siehe Delphi-Referenz durchsuchenTCustomWinSocket.SendStream(), da nur SendStream weiss, wann er alles gesendet hat. SendStream macht dies im Hintergrund.
3. Du gibst nirgendwo das Bitmap wieder frei.

Am besten mal die Senderoutine aufräumen, da biste wohl am schnellsten bei einer funktionierenden Lösung...

cRayZo 6. Okt 2007 19:47

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
okay...^^
also erstmal echtn riesen Dank, dass du dir so viel Mühe gibst, mir das zu erklären! :cheers:
(bestimmt nicht leicht xD) Ich versteh auch fast alles.

Erstmal zum Server: stimmt das jetzt so (hoff mal schon, denk aber eher nicht^^)

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Stream : TMemoryStream;
  bitmap:Tbitmap;
begin
  Stream := TMemoryStream.Create;
  MakeScreenShot(bitmap);
  bitmap.SaveToStream(Stream);
  Stream.Position := 0;
  Serversocket1.Socket.Connections[0].SendStream(Stream);
  bitmap.Free;
end;
und zum Client nochmal: Dass ichs nicht im onread schreiben darf, leuchtet mir ein, logo! kann nicht klappen. Aber wo soll ich anders (wenn nicht im on read) programmieren, dass das Programm den Stream läd, wenn er doch nicht weiß, wann der stream kommt? (ich hoffe du verstehst was ich meine^^) Und ansonsten sind alle Fehler die ich beim Client gemacht habe durchaus logisch zu verstehen, ich weiß jetzt dass es Fehler sind, und warum, aber ich wüsste nicht, wie ich es anders machen soll (außer Punkt 5^^)

Muetze1 6. Okt 2007 20:15

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Zitat:

Zitat von cRayZo
Erstmal zum Server: stimmt das jetzt so (hoff mal schon, denk aber eher nicht^^)

Nö, alles in Ordnung. Vom Code her wird genau das erreicht, aber ein wenig besserer Stil ist es, wenn man Resourcen-Schutzblöcke einführt. Damit kann man, im Falle eines Fehlers, immernoch sicherstellen, dass alle angelegten Resourcen auch wieder freigegeben werden. Und wenn das Versenden und Empfangen klappt, dann können wir nochmal drüber reden über eine kleine Regel die besagt, dass man versuchen sollte Resourcen immer auf der gleichen Ebene anzulegen wie man sie auch freigibt. Und dagegen widerspricht die MakeScreenShot Routine. Aber das machen wir dann ein andermal.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Stream: TMemoryStream;
  bitmap: TBitmap;
begin
  Stream := TMemoryStream.Create;

  MakeScreenShot(bitmap);
  try
    bitmap.SaveToStream(Stream);
  finally
    bitmap.Free;
  end;

  Stream.Position := 0;
  Serversocket1.Socket.Connections[0].SendStream(Stream);
end;
Zitat:

Zitat von cRayZo
und zum Client nochmal: Dass ichs nicht im onread schreiben darf, leuchtet mir ein, logo!

Die Aussage ist so nicht richtig. Du kannst sonstwas im OnClientRead machen, aber das Problem ist einfach folgendes: OnClientRead() wird von dem Socket mehrfach aufgerufen, immer dann, wenn er mal wieder einen Teil der Daten empfangen hat. Du musst diese einzelnen Teile zusammenspielen in den MemoryStream. Von daher muss der Stream ausserhalb des OnClientRead Ereignishandlers angelegt werden (einmalig) und im Ereignishandler schreibst du einfach nur den eben empfangenen Datenteil in den Memorystream. Wenn du alle Daten empfangen hast (also alle Bilddaten), dann kannst du den Stream wieder auslesen lassen von einem Image/Bitmap. Und da haben wir schon ein neues Problem: Woher weiss der Empfänger, wieviel Bytes an Daten kommen bis das Bild komplett ist? Immer wenn er im Ereignishandler ist, weiss er ja nicht ob noch mehr Aufrufe kommen oder ob es der letzte war. Auch weiss er nicht, ob die Daten, die er gerade vom Socket ausgelesen hat, komplett für das aktuelle Bild sind oder ob da schon wieder neue Daten mit dranne hängen. Von daher merkt man schon, dass du vorher ein paar Daten noch übertragen musst - z.B. die Länge der Bilddaten. Damit weiss der Client, aja, es kommen ab nun (nach einlesen dieser Informationen) genau 24245 Bytes an Bilddaten. Mit dieser Information kann der Ereignishandler genau sagen: Ok, alle Daten in den Memorystream schreiben und soviel Bytes sind noch zu empfangen oder halt auch: hey, ich habe alles - ich kann das Bild anzeigen! Um diese Informationen zu übertragen kannst du einfach vorher die Grösse der Daten übermitteln und danach die Bilddaten. Im Endeffekt baust du dir damit aber schon ein kleines Protokoll und genau sowas brauchst du hier auch noch. Wenn du hier mal nach Hier im Forum suchenProtokoll suchst, findest du auch schon genug Beiträge von Leuten mit einem gleichen oder ähnlichen Problem, denen wir schon entsprechend geholfen haben. Dort kannst du dir dann sowas leicht mal abschauen.

cRayZo 6. Okt 2007 20:36

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
okay.. also das mit dem "außerhalb von Onread" erstellen müsste dann so gehen oder wie?

Delphi-Quellcode:
FStream := TFileStream.Create('c:\temp\test.stream.bmp', fmCreate or fmShareDenyWrite);
end;
und im On read soll dann das stehen?

Delphi-Quellcode:
procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var
  iLen: Integer;
  Bfr: Pointer;
begin
  iLen := Socket.ReceiveLength;
  GetMem(Bfr, iLen);
  try
    Socket.ReceiveBuf(Bfr^, iLen);
    FStream.Write(Bfr^, iLen);
  finally
    FreeMem(Bfr);
  end;
end;
das habe ich ich jetzt außerhalb von DP gefunden

Muetze1 6. Okt 2007 21:28

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Joa, zum Beispiel. Das kannst du ja auch leicht abändern um statt einem FileStream ein TMemoryStream zu nutzen, wenn du keine Datei erstellen willst sondern das Bild nur anzeigen willst.

Fehlt nur noch das Problem: wann hast du das Bild komplett empfangen und kannst das Bild aus dem Stream lesen und diesen freigeben?

cRayZo 7. Okt 2007 20:42

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
also ich habs jetzt eingefügt und er startet auch ohne Probleme und connected auch ohne Probleme. Wenn ich beim Server per button einen screenshot machen will und ihn zu schicken, dann kommt dieser Fehler:
Zitat:

Zitat von Fehlermeldung
Zugriffsverletzung bei Adresse 004223BB in Modul Projekt1.exe. Lesen von Adresse 5557565B

ich hab den Fehler abgefangen. Er meckert dreimal im Code

Server:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Stream : TMemoryStream;
  bitmap:Tbitmap;
begin
 Stream := TMemoryStream.Create;
 MakeScreenShot(bitmap); //Fehlermeldung! Wundert mich, der Code war aus ner Codelibrary O:-)
 bitmap.SaveToStream(Stream); //Fehlermeldung!
 Stream.Position := 0;
 Serversocket1.Socket.Connections[0].SendStream(Stream);
 bitmap.Free; //Fehlermeldung!
end;
//edit: um herauszufinden wie lang der stream ist: würde doch so gehen oder?:
sizeof(stream) //


der Vollständigkeit halber hier nochmal der abgeänderte Clienttext:
Delphi-Quellcode:
procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Stream : TFileStream;
begin
Stream := TFileStream.Create('c:\teststream.bmp', fmCreate or fmShareDenyWrite);
clientsocket1.Socket.ReceiveBuf(stream,sizeof(stream));
image1.Picture.Bitmap.LoadFromStream(stream);
end;

Progman 7. Okt 2007 20:55

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Stream : TMemoryStream;
  bitmap:Tbitmap;
begin
Stream := TMemoryStream.Create;

bitmap:=TBitmap.create //!!

MakeScreenShot(bitmap); //Fehlermeldung! Wundert mich, der Code war aus ner Codelibrary O:-)
bitmap.SaveToStream(Stream); //Fehlermeldung!
Stream.Position := 0;
Serversocket1.Socket.Connections[0].SendStream(Stream);
bitmap.Free; //Fehlermeldung!
end;
das Create vergessen :)

cRayZo 7. Okt 2007 21:10

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Zitat:

Zitat von Progman
das Create vergessen :)

hupsa^^ aber das semikolon bitte auch nicht ;-)

jo okay...jetzt hab ich zwei fehlermeldungen.

der CLient kann den stream nicht in den Pfad speichern und schon wieder eine Zugriffverletzung.
Delphi-Quellcode:
procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Stream : TFileStream;
begin
 Stream := TFileStream.Create('c:\teststream.bmp', fmCreate or fmShareDenyWrite); //hier der Fehler!
 clientsocket1.Socket.ReceiveBuf(stream,sizeof(stream));
 image1.Picture.Bitmap.LoadFromStream(stream);
end;

Muetze1 7. Okt 2007 21:58

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Zitat:

Zitat von cRayZo
//edit: um herauszufinden wie lang der stream ist: würde doch so gehen oder?:
sizeof(stream) //

Zitat:

Zitat von Muetze1
2. Du gibst die Instanz bei ReceiveBuf() an, aber der will einen Zwischenpuffer haben. Du kannst die Instanz niemals bei dieser Funktion direkt angeben.
3. Instanzen sind intern nur Zeiger auf das Objekt im Speicher - siehe mein Beitrag zuvor. Was für das Senden gilt, gilt hier genauso für das Empfangen, schliesslich sind es jedesmal Instanzen.

Zitat:

Zitat von Muetze1
TBitmap ist eine Klasse und diese sind intern nur Zeiger. Wenn du nun ein TBitmap versendest, dann sendest du nur die Adresse im Speicherbereich deiner Applikation, wo das Objekt liegt. Somit: Keine Daten und nichts. Der Empfänger kann damit nichts anfangen, da an der angegebenen Adresse in seinem Speicherbereich was ganz anderes liegt.

Und was machst du? Du gibst wieder eine Instanz an ReceiveBuf() - dabei hatten wir das nun schon 2x in diesem Thread. Und jedesmal hatte ich dir geschrieben, das dies nicht geht!

SizeOf() genauso wenig, weil damit kannst du sogar noch eher erkennen das es nicht geht: Lass dir mal das Ergebnis von SizeOf() ausgeben: es ist immer 4, also 4 Bytes. Somit kann dies nicht sein!

cRayZo 7. Okt 2007 22:24

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
ja okay hab ich vergessen zu ändern. Liegt aber auch vielleicht daran, dass ich nicht weiß, wie ich es besser machen soll.
Receivestream gibt es nicht beim Clientsocket, für receive gibt es nur den buf!
Ich weiß jetzt dass es falsch ist, aber wie es richtig gehen soll, weiß ich auch nicht..
ich arbeite noch nciht so lange mit Delphi also bitte erwartet nicht zuviel
Ich versuch ja alles zu verstehen, und verstehs ja auch, aber was bringt es mir, wenn ich den Fehler kenne, aber kA hab wie ich ihn lösen soll? (außer dass ich ihn nicht nochmal mache, aber auch dafür muss ich es richtig wissen)
wär also ganz nett, wenn du mir eine Hilfe geben würdest, oder (was es uns beiden leichter machen würde, und dir viele nerven erspart ( :-D ) wenn du mir den Fehler verbesserst und deine Verbesserung begründest. Daraus kann ich am meisten lernen (falls es dir darauf ankommt) ;)

schonmal thx für die antwort^^
lg cRayZo

//edit: was mir grad auffällt: wens dutzen stört, der sagts bitte^^//

Muetze1 8. Okt 2007 00:54

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Es gibt kein ReceiveStream, weil der Socket nicht weiss, wie lang ist denn der Stream. Er weiss nicht wieviele Daten er in den Stream reinschreiben soll und wann halt Schluss ist. Er selber ist reiner Datenüberträger. Alles was du ihm gibst, schickt er so unverändert über den Socket raus. Wenn du SendStream() aufrufst, dann schreibt er einfach alle Daten aus dem Stream in den Socket bis der Stream zu Ende ist. Aber: als Empfänger weiss er nicht was für Daten da ankommen, woher und wie lang. (Als Sender interessiert ihn das auch herzlich wenig). Langer Rede, kurzer Sinn: es gibt kein ReceiveBuf(), weil die Socket Komponenten das gleiche Problem beim empfangen haben wie du: wann fängt der Stream an und wie lang ist er? Wann ist der Stream fertig? Der Socket könnte natürlich vor dem Stream die Grösse des selbigen übertragen, aber das macht er nicht, weil er damit sonst eine Art Protokoll definieren würde - und das macht er nicht. Er soll nur rein Daten übermitteln und sich darin keine Ordnung festlegen. Das ist nun deine Aufgabe.

Und zu deinem Problem mit dem ReceiveBuf: ReceiveBuf() will einen Buffer sehen, in den er die Daten reinschreiben kann. Danach kannst du den Buffer in deinen Stream schreiben. Ich habe dir die gesamte Zeit keinen Quelltext dazu gegeben sondern nur die Fehler aufgezeigt, da du selber eine passende Routine zum empfangen mit Puffer schon gepostet hast: hier. Damit hast du das eine Problem schonmal gelöst: Daten empfangen und in einen Puffer bekommen und danach weiter in den Stream.

Das einzige was noch fehlt: wann den Stream anlegen und wann hast du ihn komplett empfangen so dass du das Bild aus dem Stream laden kannst? Irgendwelche Ideen?

cRayZo 8. Okt 2007 16:09

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
der sender (hier der server) könnte, nachdem er alle Daten gesendet hat, eine msg schicken, dass er fertig gesendet hat.

und wenn du mich jetzt nicht vollständig durcheinandergebracht hast, sollte das dann so aussehen?
Delphi-Quellcode:
procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var
  iLen: Integer;
  Bfr: Pointer;
  Stream : TFileStream;
begin
  iLen := Socket.ReceiveLength;
  GetMem(Bfr, iLen);
  try
    Socket.ReceiveBuf(Bfr^, iLen);
    FStream := TFileStream.Create('c:\teststream.bmp', fmCreate or fmShareDenyWrite); //edit: der müsste außerhalb von onread erstellt werden oder verwechsel ich jetzt wieder was?
    FStream.Write(Bfr^, iLen);

    //hier die msg, dass Bild da ist isowas wie if receivetext=bla then
    image1.Picture.Bitmap.LoadFromStream(Fstream);
  finally
    FreeMem(Bfr);
  end;
end;

Muetze1 8. Okt 2007 16:30

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Deine Kommentare sind richtig: Create des Streams ausserhalb von OnRead und das anzeigen, wenn er fertig is.

Aber zu deiner Idee mit der Message: Was ist - egal wie die Message nun im Endeffekt aussieht - wenn genau diese Bytefolge in dem übertragenen Bild enthalten ist? Dann bricht er zu früh ab und versucht ein unvollständiges Bild zu laden.

Wäre nicht die Übermittlung der Grösse der Bilddaten vor diesen sinnvoller?

Ein anderes Problem: ReceiveText weisst nicht ob die Daten im Socket Text oder andere Daten sind. Wenn du noch nicht fertig alles empfangen hast, dann würdest du mit ReceiveText sogar die Daten als string bekommen...

cRayZo 8. Okt 2007 16:51

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
also kann ich im server, wenn ich das Bild komplett geladen habe (sprich nach dem screenshot) einen sizeof machen? das müsste doch noch gehen oder?
was anderes könnte ich mir nicht vorstellen, bzw wüsste es nciht besser

Apollonius 8. Okt 2007 16:57

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Nein, nimm nicht SizeOf, da kommt sowieso immer vier raus. Nimm Stream.Size.

cRayZo 8. Okt 2007 17:11

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
okay
Delphi-Quellcode:
var size:integer;
(...etc)
bitmap.SaveToStream(Stream);
size:=stream.Size;
(senden...etc)
wenn ich die Länge vorher aber in einem Sendtext übertrage, gehts ja nicht, weil
Zitat:

Zitat von ---> Muetze1
Wenn du noch nicht fertig alles empfangen hast, dann würdest du mit ReceiveText sogar die Daten als string bekommen...

soll ich die länge des streams also in den stream selbst, an den Anfang setzen oder wie. Stell ich mir schwer umsetzbar vor

Apollonius 8. Okt 2007 17:27

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Du sendest auf Serverseite erstmal die Größe mit SendBuf und dann den eigentlichen Stream mit SendStream. Auf der Clientseite prüfst du, ob die Übertragung schon begonnen hat - wenn nein, dann liest du die Größe und speicherst sie global. Andernfalls liest du mit deiner eigenen Methode (Puffer allozieren, lesen und in den Stream speichern) die Daten aus und prüfst, ob du die Gesamtgröße schon erreicht hast.

Jens Schumann 8. Okt 2007 17:29

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Hallo,
ich habe das Gefühl das hier mit "nicht blockierenden Socktes" gearbeitet wird.
Ich empfehle Dir die Komponenten in den "blockierenden Modus" zu schalten und die Übertragung
mit TWinSocketStream zu realisieren. Dann lösen sich alle hier genannten Probleme in Luft auf.
Die speicherst alles was Du Üertragen möchtest in einen Stream der dann mit TWinSocketStream gesendet wird.
Das Bitmap könntest Du dann mit TBitmap.SaveToStream(aWinSocketStream) senden.

Für die serverseitige ClientExecute Methode gibt es sogar ein Beispiel in der OH.

Muetze1 8. Okt 2007 17:56

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Zitat:

Zitat von cRayZo
also kann ich im server, wenn ich das Bild komplett geladen habe (sprich nach dem screenshot) einen sizeof machen? das müsste doch noch gehen oder?

SizeOf() liefert die Grösse eines Typs/Variable, welchen diese belegt im Speicher. Eine Instanz ist intern ein Pointer... bla blubb, siehe oben ... und liefert von daher immer 4 - weil 4 Bytes ist ein Pointer auf einem 32 Bit System/Compiler gross. (auch hier: siehe zuvor).

Stream.Size ist schon die richtige Antwort von Apollonius. Und zu deinem Problem mit dem SendText: Sende die Grösse einfach nicht in Textform (also als String) sondern als Binäre Daten. Dann ersparst du dir einfach das Umwandeln in den String und zurück und vor allem: du hast eine definierte Länge. Die Grösse eines LongInt/Int64/Char/Byte etc sind immer gleich gross bei dem gleichen Compiler und Zielsystem, weil sonst könnte der Compiler schlecht beim Erstellen des Programmes den Speicherplatz für Variablen vorhalten. Von daher wäre das eine gute Lösung.

Soviel zur Theorie, nun zum Code: Die Grösse dieser Variablen bzw. des Typs ermittelst du - tada - mit SizeOf(). Dieses würde dir z.B. für LongInt 4 Bytes liefern und für Int64 sogar 8 Bytes. Damit hättest du die Gewissheit, dass du immer nur (bei Nutzung eines LongInt's) 4 Bytes vorne die Grösse sind und alles nachfolgende schon die Stream-Daten.

Delphi-Quellcode:
var
  lLen: LongInt;
begin
  ...
  // Bitmap sichern in Stream, etc
  ...
  lLen := Stream.Size
  Socket.SendBuf(lLen, SizeOf(lLen));
  Socket.SendStream(Stream);
end;
Auf der Gegenseite einfach die Länge auslesen und dann damit arbeiten. Und damit du z.B. unterscheiden kannst, ob du nun das erste Mal versuchst Daten zu lesen und somit erstmal die 4 Bytes Streamgrösse auslesen musst oder ob es schon Stream-Daten sind, kannst du dich ja an deinem Empfangsstream orientieren: ist er schon angelegt, dann weisst du um die Länge, wenn nicht, dann erstmal diese auslesen...

Und das mit dem Empfangen der Länge solltest du mit dem Codeschnipsel gut selbst hinbekommen.

@other: Ja, ich hätte auch alles einfach mit Integer erklären können, aber ich bezog mich darauf, dass die Datentypen bei den gleichen Compilern und Zielsystemen gleich gross sind. Leider wächst aber Integer (generischer Ganzzahl-Typ) mit und wird bei einem 64-Bit Compiler (Delphi "Commodore", Winter 2008) auch 64 Bit gross sein, von daher wollte ich diesen Fakt nicht auswälzen und habe LongInt genommen, weil das definitiv 32 Bit gross bleibt.

cRayZo 10. Okt 2007 17:38

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
okay, wieder scheiterts am Vokabular
ich hab mir versucht irgendwie trotzdem zu helfen :lol:
(trau mich ja fast ned das zu posten :? )

hier für den empfänger (Client)
Delphi-Quellcode:
begin
  iLen := Socket.ReceiveLength;
  GetMem(Bfr, iLen);
  try
    Socket.ReceiveBuf(Bfr^, iLen);
    FStream.Write(Bfr^, iLen);

    if FileExists('c:\teststream.bmp')
    then       begin
                if x=Fstream.Size
                then image1.Picture.Bitmap.LoadFromStream(Fstream)end
    else begin x:=strtoint(copy(inttostr(Socket.ReceiveBuf(Bfr^, iLen)),0,4)); // * weiter unten -->
    FStream := TFileStream.Create('c:\teststream.bmp', fmCreate or fmShareDenyWrite); end;
  finally
    FreeMem(Bfr);
  end;

//*bestimmt viel leichter^^ aber ich kenn nur copy und copy geht nunmal soweit ich weiß nur mit string^^
end;
kanns Programm übrigens ohne Fehler starten, aber beim sendeversuch kommt ein Fehler.. (iwie auch klar)

Muetze1 10. Okt 2007 19:10

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Ok, dann doch mal der Code beim Client:

1. Eine Variable vom Typ TStream im private Abschnitt der Form deklarieren (für die Daten) und dazu eine Variable vom Typ Integer, welche die Grösse der Daten hält.
Delphi-Quellcode:
  xxx = class(TForm)
  ...

  private
    FStream: TStream;
    fStreamDataSize: Integer;
  ...
  end;
2. Empfangsroutine: OnClientRead
Delphi-Quellcode:
procedure xxx.ClientSocket1ClientRead(...)
var
  iLen: Integer;
  Bfr: Pointer;
begin
  if not assigned(FStream) then // noch keine Übertragung bisher, Stream ist nil
  begin
      // Ok, dann müssten wir erstmal schauen und die Grösse des folgenden Streams
      // auslesen. Dazu erstmal schauen, ob wir die Grösse bisher überhaupt komplett empfangen haben...
    if Socket.ReceiveLength >= SizeOf(fStreamDataSize) then
    begin
        // dann nun die Grösse einlesen
      Socket.ReceiveBuf(fStreamDataSize, SizeOf(fStreamDataSize));

        // dann Stream anlegen
      FStream := TMemoryStream.Create;
    end;
//    else
//      Exit; // ok, dann weiter warten, bis wir die Grösse komplett empfangen haben (Code unnötig)
  end;

    // ok, hier nun, wenn der Stream existiert, einlesen der Daten die empfangen wurden und Schreiben in den Stream
  if assigned(FStream) then
  begin
    iLen := Socket.ReceiveLength;

      // Ok, nur die Daten auch empfangen, die zu unserem Stream gehören. Also schauen wieviel wir noch vom Stream einlesen
      // müssen...
    iLen := Min(fStreamDataSize, iLen);

    GetMem(Bfr, iLen);
    try
      Socket.ReceiveBuf(Bfr^, iLen);

//      fStreamDataSize := fStreamDataSize - FStream.Write(Bfr^, iLen); // beides gleich - schreiben der Daten
      Dec(fStreamDataSize, FStream.Write(Bfr^, iLen);                   // und Variable fStreamDataSize runterzählen um die
                                                                         // empfangenen und geschriebenen Daten...
    finally
      FreeMem(Bfr);
    end;

    if fStreamDataSize = 0 then // alles empfangen?
    begin
        // Positionszeiger zurücksetzen
      FStream.Position := 0;

        // Image einlesen
      Image1.Picture.Bitmap.LoadFromStream(FStream);
   
        // MemoryStream freigeben und auf nil setzen (wegen den If Assigned() Abfragen)
      FreeAndNil(FStream);
    end;
  end;

    // wenn er hier ankommt und ReceiveLength() ist immernoch > 0, dann sind noch Daten im Stream, die aber nicht
    // mehr zum Bild gehören - oder - wir haben die Grösse der Streamdaten noch nicht empfangen.
end;
3. Beim Server folgendes um die Grösse vorne weg zu senden:
Delphi-Quellcode:
var
  lLen: Integer;
  lStream: TStream;
begin
  lStream := TFileStream.Create('c:\testbild.bmp', fmOpenRead);

  lLen := lStream.Size; // grösse Stream ermitteln
  Socket.SendBuf(lLen, SizeOf(lLen)); // Grösse senden

  Socket.SendStream(lStream); // dann das Bild hinten dran...
end;
So, soweit zur Theorie. Alles hier im Forumfenster geschrieben, sollte aber so laufen...

cRayZo 10. Okt 2007 19:17

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
jo ist gebongt und jetzt?.. :?:

Muetze1 10. Okt 2007 19:27

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Zitat:

Zitat von cRayZo
jo ist gebongt und jetzt?.. :?:

Sorry, war noch beim tippen - hatte mich nur verdrückt mit ALT-S anstatt ALT-D...

cRayZo 10. Okt 2007 19:48

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
achso, kein Problem, ja vielen Dank auf jeden fall!!!
einmal nur ne klammer vergessen(is ja np ;) )
mein delphi gibt den Ausdruck 'min' nur als nicht definiert zurück.
Delphi-Quellcode:
iLen := Min(fStreamDataSize, iLen);
das versteh ich aber nicht, ist doch eine Funktion

Muetze1 10. Okt 2007 19:59

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Jo, und definiert in der Unit Math.pas. Somit die UsesListe um Math erweitern oder selbst schnell eine If Abfrage schreiben...

/EDIT: Ich habe die erste IF Abfrage nochmal auf >= geändert. > muss nicht sein, >= reicht schliesslich schon...

Und wo habe ich eine Klammer vergessen?

cRayZo 10. Okt 2007 20:23

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
okay, das klappt schonmal ;) thx

beim sendeversuch kommt allerdings noch (nach 4 sekunden warten, nach dem buttonklick) ein access violation im Clientprojekt und dieses CPU Fenster öffnet sich. hat da jemand eine idee??:(

//edit: die klammer hattest du bei
Delphi-Quellcode:
Socket.ReceiveBuf(fStreamDataSize, SizeOf(fStreamDataSize));
vergessen, als ich den text kopiert hatte. Jetzt ist sie ja da, gut^^

Progman 10. Okt 2007 20:33

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Unit math

...zu spät ;)

cRayZo 10. Okt 2007 20:50

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
okay also ich hab den access violation-Fehler mal weiter verfolgt.

er tritt nicht auf, wenn ich das Bitmap im Client nicht vom Stream lade sprich wenn ich folgendes aus dem Code herausnehme:
Delphi-Quellcode:
        // Image einlesen
      Image1.Picture.Bitmap.LoadFromStream(FStream);
also hat der scheinbar ein Problem mit dem laden des Streams würd ich mal sagen, aber was, weiß ich jetzt nicht, habt ihr da eine Idee?

Muetze1 10. Okt 2007 21:25

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Naja, gibt es denn auch ein Image1 auf deiner Form?

cRayZo 11. Okt 2007 06:44

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
natürlich, ansonsten würde ja schon vor dem Start ein Fehler kommen und das Prog. würde erst gar nicht starten.

Muetze1 11. Okt 2007 09:53

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Zitat:

Zitat von cRayZo
natürlich, ansonsten würde ja schon vor dem Start ein Fehler kommen und das Prog. würde erst gar nicht starten.

Warum sollte? Du kannst soviele member deklarieren wie du willst (deklarier einfach ein Button55: TButton als Member in deinem Form und dann schau mal ob er meckert). Die Frage ist halt nur, ob nicht vielleicht Image1 nil ist, weil von dem o.g. Code her sollte es klappen. Ansonsten wäre es schliesslicha uch kein Problem mit dem Debugger heraus zu finden, warum es genau kracht. Vom reinen Code her, finde ich nichts entsprechendes. Also: Debugger anschmeissen und mit Haltepunkt-Bedingungen und überwachten Ausdrücken mal nachschauen warum es wo kracht.

cRayZo 11. Okt 2007 13:51

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Fehler der Klasse EInvalidGraphic
Meldung: Bitmap ist ungültig´

//edit: hab auch nochmal gesucht, aber sowas hilft mir da auch jetzt nicht wirklich.

Muetze1 11. Okt 2007 14:23

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
Wenn das Bitmap wirklich ungültig sein sollte (was eine ganz andere Exception ist als eine Access Violation(!!)), dann kann folgendes sein:

1. Das Bild wird falsch beim Sender in den Stream gespeichert (welcher später versendet wird)
2. Das Bild ist einfach kein Bitmap
3. Das Bild wird zu früh geladen
4. Es werden noch andere Daten dazwischen gesendet (wie man dies auch immer hinbekommen sollte)

Aber um das mal zu untersuchen, wäre es zu empfehlen sich die empfangenen Daten anzuschauen. Also speicher mal den MemoryStream vor dem Anzeigen in dem Image (SaveToFile()) und dann schau mal, ob die Datei vom Inhalt die gleiche ist wie die gesendete...

cRayZo 11. Okt 2007 14:40

Re: Dateiaustausch zwischen clientsocket und serversocket..^
 
sorry, mein Fehler, bin selber drauf gekommen. Beim Server hab ich etwas mit reinkopiert, was schon drinstand. Ich hab die Länge zweimal gesendet, somit war im ankommenden stream nach der rausgefilterten Größe noch immer die Größe am Anfang, welche der stream natürlich nicht ins Bild laden konnte. Es klappt jetzt, nochmal VIELEN DANK!! :mrgreen:

PS: vll nurnoch eine kleine Frage: wie kann man ein Bild von der Größe verkleinern? also halbieren, dritteln..? Antwort muss nicht sein wär aber noch der Punkt auf dem i ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:27 Uhr.
Seite 1 von 2  1 2      

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