![]() |
Problem mit SendStream und ReceiveBuf bei großen Dateien
Wenn ich folgendermaßen mit TClientSocket und TServerSocket Dateien versende:
Client:
Delphi-Quellcode:
Server:
procedure TFileClient.SendData;
begin FClient.Socket.SendStream(FileStream); end;
Delphi-Quellcode:
bekomme ich die Meldung "Windows-Socket-Fehler: Eine vorhandene Verbindung wurde vom Remotehost geschlossen." Ich nehme mal an, das das so eine Art Timeout vom Server ist. Bin ich da richtig in der Annahme und wenn ja, wie kann man das ausschalten oder verlängern?
procedure TFileServer.ReceiveData(Sender: TObject; Socket: TCustomWinSocket);
var Buffer: AnsiString; Size: Integer; begin Size := Socket.ReceiveLength; SetLength(Buffer, Size); Socket.ReceiveBuf(Buffer[1], Size); FileStream.Write(Buffer[1], Size); end; |
Re: Problem mit SendStream und ReceiveBuf bei großen Dateien
also kommt der Fehler beim Server? Ich würde versuchen das ganze einfach stückenweise zu schicken wenn es nur bei großen Dateien auftritt. Ist das was du gepostet hast der vollständige Quelltext? Hast du bedacht das der Stream nicht vollständig ankommt sondern stückenweise (ReceiveData also mehrfach aufgerufen wird)?
|
Re: Problem mit SendStream und ReceiveBuf bei großen Dateien
Also ich habe das jetzt so umgeschrieben, das es Stückenweise verschickt wird, aber der Fehler tritt trotzdem noch bei großen Dateien (so um die 90 KB, also eigentlich nicht wirklich groß) auf. Also es tritt der Socket-Fehler 10053 auf, was woll so viel bedeutet, wie das der Server den Clienten gekickt hat, aber warum, ist das so ne art timeout?
Edit: Ok ich habe jetzt nochmal ein wenig rumprobiert und mitbekommen, der der Fehler immer auftritt, wenn ca. 65 KB versendet wurden, also scheint das nicht an der Zeit zu liegen, weiß jemand, woran das liegt, das der Server immer bei ca. 65 KB die Verbindung zum Server trennt. |
Re: Problem mit SendStream und ReceiveBuf bei großen Dateien
64 KB ist der interne Socket Buffer. Scheint mir so, als wenn der Server die Verbindung kickt, da der Buffer überläuft. Kann es sein, dass das Empfangsereignis nicht richtig verknüpft ist beim Server und daher der Buffer voll läuft?
|
Re: Problem mit SendStream und ReceiveBuf bei großen Dateien
Als Empfangsereignis nutze ich vom Server das OnClientRead-Ereignis, das müsste doch richtig sein, oder liege ich da falsch?
|
Re: Problem mit SendStream und ReceiveBuf bei großen Dateien
Ich poste jetzt einfach mal de gesamten Quellcode, vllt kann mir ja dann jemand sagen, was ich falsch mache, dass der Server den Clienten immer nach der Übertragung von 64 KB kickt.
Delphi-Quellcode:
unit uFileTransfer;
interface uses ScktComp, Classes, SysUtils, Windows; type TFileServer = class(TPersistent) protected FServer: TServerSocket; FFiles: TFiles; FTime: Longword; private procedure ReceiveData(Sender: TObject; Socket: TCustomWinSocket); function GetFileInfo(var Text: AnsiString): TFileInfo; function GetFileInfo2(Index: Integer): TFileInfo2; function CountFiles: Integer; public constructor Create; destructor Destroy; override; procedure Start; property Files[Index: Integer]: TFileInfo2 read GetFileInfo2; property FilesCount: Integer read CountFiles; end; TFileClient = class(TPersistent) protected FClient: TClientSocket; FFile: TSendFile; FTime: Longword; private procedure SendData(Sender: TObject; Socket: TCustomWinSocket); public constructor Create; destructor Destroy; override; procedure Start(ServerAdress: String); procedure SendFile(Filename: String); end; implementation constructor TFileServer.Create; begin inherited; FServer := TServerSocket.Create(nil); FServer.OnClientRead := ReceiveData; FServer.Service := 'ftp'; FFiles := TFiles.Create; end; destructor TFileServer.Destroy; begin FServer.Free; FFiles.Free; inherited; end; procedure TFileServer.ReceiveData(Sender: TObject; Socket: TCustomWinSocket); var Size: Integer; Text: AnsiString; FileInfo: TFileInfo; Time: LongWord; begin while Socket.ReceiveLength > 0 do begin Size := Socket.ReceiveLength; SetLength(Text, Size); Socket.ReceiveBuf(Text[1], Size); if Pos(#5+'header'+#6,Text) > 0 then begin FileInfo := GetFileInfo(Text); FFiles.Add(FileInfo.Filename, FileInfo.Size, Integer(Socket)); FFiles.Items[FFiles.IndexOf(Integer(Socket))].Write(Text[1], Length(Text)); FTime := GetTickCount; end else begin FFiles.Items[FFiles.IndexOf(Integer(Socket))].Write(Text[1], Length(Text)); end; Time := GetTickCount-FTime; if Time = 0 then Time := 1; FFiles.Items[FFiles.IndexOf(Integer(Socket))].Speed := Size*1000/Time; if FFiles.Items[FFiles.IndexOf(Integer(Socket))].FullSize = FFiles.Items[FFiles.IndexOf(Integer(Socket))].LoadedSize then begin FFiles.Save(FFiles.IndexOf(Integer(Socket))); end; FTime := GetTickCount; end; end; function TFileServer.GetFileInfo(var Text: AnsiString): TFileInfo; var I, ParamNo, Start:Integer; Substring: String; begin I := 0; ParamNo := 0; Start := 0; while (I < Length(Text)) and (Substring <> #6+'/header'+#7) do begin case Text[I] of #5:Substring := ''; #6:begin case ParamNo of 1:Result.Filename := copy(Text, Start, I-Start); 2:Result.Size := StrToInt(copy(Text, Start, I-Start)); end; inc(ParamNo); Start := I+1; Substring := ''; end; end; Substring := Substring+Text[I]; inc(I); end; if Text[I] = #7 then inc(I); Text := copy(Text, I, Length(Text)); end; function TFileServer.GetFileInfo2(Index: Integer): TFileInfo2; begin Result.FullSize := FFiles.Items[Index].FullSize; Result.LoadedSize := FFiles.Items[Index].LoadedSize; Result.Filename := FFiles.Items[Index].Filename; Result.Speed := FFiles.Items[Index].Speed; end; function TFileServer.CountFiles: Integer; begin Result := FFiles.Count; end; procedure TFileServer.Start; begin FServer.Open; end; constructor TSendFile.Create(Filename: String); begin inherited Create; FFile := TMemoryStream.Create; FFile.LoadFromFile(Filename); FFilename := Filename; end; constructor TFileClient.Create; begin inherited; FClient := TClientSocket.Create(nil); FClient.OnConnect := SendData; FClient.Service := 'ftp'; end; destructor TFileClient.Destroy; begin FClient.Free; FFile.Free; inherited; end; procedure TFileClient.SendData(Sender: TObject; Socket: TCustomWinSocket); var Header: AnsiString; begin if Assigned(FFile) then begin Header := ''; if FFile.Stream.Position = 0 then begin Header := #5+'header'+#6+ExtractFileName(FFile.Filename)+#6+IntToStr(FFile.Stream.Size)+#6+'/header'+#7; Socket.SendBuf(Header[1], Length(Header)); end; Socket.SendStream(FFile.Stream); end; end; procedure TFileClient.Start(ServerAdress: String); begin FClient.Host := ServerAdress; FClient.Open; end; procedure TFileClient.SendFile(Filename: String); begin FFile := TSendFile.Create(Filename); if FClient.Active then SendData(Self, FClient.Socket); end; end. |
Re: Problem mit SendStream und ReceiveBuf bei großen Dateien
Ok, das ganze liegt nicht am Quellcode sondern am Rechner oder Betriebssytem. Ich habe das ganze nähmlich jetzt auf meinem alten rechner mit XP getestet und der Fehler tritt nicht auf, bei meinem neuen mit Vista tritt der Fehler auf.
Edit: Ich habs, es liegt an der Windows-Firewall, die hab ich nämlich auf dem Vista-Rechner laufen, auf dem XP-Rechner hab ich ne andere. Als ich jetzt die Windows-Firewall deaktiviert habeging das ohne Probleme. Kennt jemand dieses Problem und weiß wie man das lösen kann ohne die Windows-Firewall zu deaktivieren. |
Re: Problem mit SendStream und ReceiveBuf bei großen Dateien
Folgendes zu deinem bisherigen Code:
- du müsstest definitv Warnungen vom Compiler bekommen, da du z.B. SubString bei GetFileInfo() nicht vor der ersten Abfrage initialisierst, etc (glücklicherweise macht das Delphi aufgrund der Referenzzählung) - Du suchst nach dem Teil string zur Teilung als ganzes. Was ist aber wenn in dem empfangenen Block gerade mal "#6/head" angekommen ist? Dann findest du es nicht, schreibst es so weg. Damit ist die empfangene Datei schon verändert und du bekommst niemals diese Trennung mehr raus. - Was ist, wenn in dem empfangen Block Restdaten vom File sind und ein Header vom nächsten File? Ich sehe nicht, wo du diesen Rest noch in die alte Datei schreibst... - Die Funktion GetFileInfo() ist recht unperformant, wenn du die Zeichen einzelnd kopierst anstatt als Block von Text[] zu SubString - Warum nutzt du die Service Eigenschaften der Sockets und belegst damit definierte Protkolle anstatt dir einfach einen freuen Port zu schnappen? - Warum ziehst du dir bei TSendFile die komplette Datei in einen Memorystream? Versende dann mal bitte ein 4,7 GB DVD Image mit der Komponente... Mit anderen Worten: Warum nicht allgemein TStream und du übergibst bei einer Datei explizit TFileStream Instanz? Nur mal so beim rüberfliegen. Kann natürlich sein, dass ich manches falsch bzw. nicht gesehen habe... |
Re: Problem mit SendStream und ReceiveBuf bei großen Dateien
Das der Code noch nicht ganz perfekt ist, ist mir schon klar, das habe ich noch nicht weiter gemacht gehabt, da der Fehler der aufgetretten ist gar nichts damit zu tun hatte. Ich wollte erst den Fehler behoben haben, bevor ich das dann weitermache. Aber trotzdem danke für den Hinweis.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:59 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