![]() |
ServerSocket/TcpServer als Webserver -> Schwierigkeiten
Hiho DP'ler,
Ich habe vor einen kleinen Webserver zu schreiben der einfach auf Anfrage eines Browsers eine sich ständig ändernde Webseite ausgibt. Ich dachte mir also ich nehme mir die ServerSocket Komponente oder alternativ die TcpServer Komponente und reagiere einfach immer wenn ein Browser eine Anfrage sendet damit drauf das ich dem Browser simpel mit SendText den Quellcode der Seite rüberschiebe. Das funktioniert aber mit beiden Komponenten nur halb.
Delphi-Quellcode:
Das war der sehr unsaubere Code um auf die Anfrage des Browsers mittels ServerSocket zu reagieren. Wie ihr sehr es soll nicht schön sein sondern einfach nur funktionieren :-D. Problem dabei, der Browser würde unendlich lang die Webseite laden wenn ich nicht mittels ServerSocket1.Active die Verbindung kurz unterbrechen würde. Wenn ich das tue wird die Webseite dann auch Problemlos im Browser angezeigt. Das ist aber selbst mir zu unsauber :) - sobald mehrere Leute versuchen die Seite aufzurufen bekomme ich Probleme, da fliegen mir teils Exceptions um die Ohren und die Browser zeigen immerwieder an das die Verbindung abgebrochen/zurückgesetzt ect. ist.
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket); var i:Integer; begin s:=Socket.ReceiveText; Memo1.Lines.Add(s); For i:=0 to ServerSocket1.Socket.ActiveConnections -1 do begin ServerSocket1.Socket.Connections[i].SendText('<html><head><meta http-equiv="refresh" content="1; URL=/"></head><body>It Works? Or? ' +IntToStr(Random(10000)) +'</body></html>'); //ServerSocket1.Active:=False; //ServerSocket1.Active:=True; end; end; Beim TcpServer habe ich es ähnlich versucht... - empfangen der Browser Anfrage - die in Memofeld schreiben - *Browser das Stückchen rohes Fleisch hinwerf* (Per SendText den Quellcode rübersenden) Also imprinzip wie oben. Problem ist hier das der Browser nur bei jedem 5ten Versuch die Webseite anzeigt - dann auch korrekt. Bei den anderen 4 Versuchen kommt ein "Verbindung zurückgesetzt". Beim TcpServer habe ich übrigens nicht das Problem mit dem "unendlichen Laden" der Seite, wenn die Seite angezeigt wird dann richtig. Wie bekomme ich das ganze also zuverlässiger hin? Wo hängts? Weis da jemand Rat :?: |
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Ich kenne micht mit http nicht so aus, aber willst du nicht wenigstens einen http-Header mitschicken?
Edit: Und ob die Verbindung geschlossen werden muss, steht doch in der Anfrage drin, oder? |
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Wenn dadurch mein Problem nicht hervorgerufen wird dann eigentlich nicht. Ich bekomme ja (zwar unzuverlässig aber es geht) den Quelltext den ich an den Browser zurücksende angezeigt. Nur halt mit oben genannten Problemen. Würde ein Header das lösen?
Ich kenne mich mit dem Protokoll leider auch nicht aus und hatte gehofft das es reicht dem Browser einfach Text zurückzusenden und er sich damit zufrieden gibt. Das wiegesagt klappt ja auch (teilweise) ^^. Edit: Danke für die schnelle Antwort ^^. |
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Egel ob http oder ein anderes Anwendungsprotokoll: Ein Header ist insofern wichtig, als dass er mindestens die Größe/Länge des Inhalts angibt. Ohne diese Angabe ist es dem Empfänger unmöglich, das Gesendete korrekt zusammenzusetzen. TCP/IP kann diese Funktionalität nicht liefern. Es zerhackt dir deinen Text in mehrere Pakete und irgendiwe muss der Empfänger ja wissen, wie lange er noch warten muss.
==> Mindestens eine Längenangabe o.ä#. muss in so ein Anwendungsprotokoll. Und http hat das IMHO auch enthalten. Sollte man auch dringend nutzen. Und evtl. gibt es noch andere wichtige Angaben im Header, keine Ahnung. |
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Und wie sieht so ein Http Header in Delphi aus? Send ich das einfach als Text vorne weg oder wie funktioniert das? Hab mir so ein Http Header grad mal angesehen, da kommt so eine Längenangabe in Byte vor, aber wie kann ich die Angeben wo ich doch nur Texte (SendText) und keine Dateien mit festen Dateigrößen in Streams versende?
|
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Hi,
schau mal in den Demos nach, da gibt es einen HTTPserver als Beispiel. Ausserdem solltest Du die Antwort an den "Socket" = TCustomWinSocket zurückschicken, und diesen schliessen. Das wäre die richtige Vorgehensweise. Gruss |
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Hi Darkface,
nimm doch den IdHttpServer(Indy-Komponenten) der regelt für Dich alles. Du must nur auf die Events: - OnCommandGet und - OnCommandOther entsprechend die Antwortvariablen füllen, das war. Greetz Data |
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Zitat:
Delphi-Quellcode:
Edit: @Datacool: Ich vermute der TE möchte das Prinzip hinter einem http-Server verstehen. Da hilft keine fertige Komponente.
//mit meinen bescheidenen http-Kentnissen:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, scktcomp, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; procedure FormCreate(Sender: TObject); private { Private-Deklarationen } FServer:TServersocket; FIndex:Integer; public { Public-Deklarationen } procedure ClientConnect(Sender:TObject; Socket: TCustomWinSocket); procedure ClientDisconnect(Sender:TObject; Socket: TCustomWinSocket); procedure ClientRead(Sender:TObject; Socket: TCustomWinSocket); procedure ClientWrite(Sender:TObject; Socket: TCustomWinSocket); procedure ClientError(Sender:TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); end; TClientData=class Constructor Create; Destructor Destroy; override; private FCanWrite: boolean; //ist socket bereit zum senden FIndex: Integer; //laufender Index FSocket: TCustomWinSocket; //Parent Socket merken, für die Antwort FReceivedText:TStringList; //Stringlist wegen zu erwartenden http-Header procedure SetCanWrite(const Value: boolean); procedure SetIndex(const Value: Integer); procedure SetSocket(const Value: TCustomWinSocket); protected procedure SendNotImpl; procedure SendOK(const content:String); public property CanWrite:boolean read FCanWrite write SetCanWrite; property Index:Integer read FIndex write SetIndex; property Socket:TCustomWinSocket read FSocket write SetSocket; procedure Receive(const Text:string); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.ClientConnect(Sender: TObject; Socket: TCustomWinSocket); var ClientData:TClientData; begin ClientData:=TClientData.Create; ClientData.CanWrite:=false; inc(FIndex); ClientData.Index:=FIndex; ClientData.Socket:=Socket; Socket.Data:=ClientData; memo1.lines.add(format('%d connected',[ClientData.Index])); end; procedure TForm1.ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin assert(TObject(Socket.Data) is TClientData); memo1.lines.add(format('%d disconnected',[TClientData(Socket.Data).Index])); TObject(Socket.Data).Free; end; procedure TForm1.ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); var s:String; begin assert(TObject(Socket.Data) is TClientData); case ErrorEvent of eeGeneral: s:='General'; eeSend: s:='Send'; eeReceive: s:='Receive'; eeConnect: s:='Connect'; eeDisconnect: s:='Disconnect'; eeAccept: s:='Accept'; eeLookup: s:='Lookup'; else s:='Empty'; end; memo1.Lines.Add(format('%d: Error on %s',[TClientData(Socket.Data).index,s])); ErrorCode:=0; end; procedure TForm1.ClientRead(Sender: TObject; Socket: TCustomWinSocket); var s:string; begin assert(TObject(Socket.Data) is TClientData); s:=Socket.ReceiveText; memo1.lines.add(format('%d Read: %s',[TClientData(Socket.Data).index,s])); TClientData(Socket.Data).Receive(s); end; procedure TForm1.ClientWrite(Sender: TObject; Socket: TCustomWinSocket); begin assert(TObject(Socket.Data) is TClientData); memo1.lines.add(format('%d: CanWrite',[TClientData(Socket.Data).index])); TClientData(Socket.Data).CanWrite:=true; end; procedure TForm1.FormCreate(Sender: TObject); begin FServer:=TServersocket.Create(self); FServer.Port:=80; FServer.Service:=''; FServer.ServerType:=stNonBlocking; FServer.OnClientConnect:=ClientConnect; FServer.OnClientDisconnect:=ClientDisconnect; FServer.OnClientWrite:=ClientWrite; FServer.OnClientRead:=ClientRead; FServer.OnClientError:=ClientError; FServer.Open; FIndex:=0; end; { TClientData } constructor TClientData.Create; begin FReceivedText:=TstringList.Create; end; destructor TClientData.Destroy; begin FReceivedText.Free; end; //neu erhaltenen TExt speichern und überprüfen ob breits eine Leerzeile vorhanden ist //Leerzeile == Ende HTTP-Anfrage procedure TClientData.Receive(const Text: string); var i:Integer; Endpos:Integer; s:String; Req:String; begin FReceivedTExt.Text:=FReceivedTExt.Text+Text; while FReceivedTExt.count>0 do begin EndPos:=-1; for i:=0 to FReceivedTExt.count-1 do begin if FReceivedText.Strings[i]='' then //auf http-Header Ende warten/suchen begin EndPos:=i; //Leerzeile gefunden break; end; end; if Endpos>0 then //wenn leerzeile und noch etwas mehr gefunden begin //eigentlich hier noch auf CanWrite überprüfen, ansonsten iust unser socket noch nicht fertig s:=FReceivedText.Strings[0]; if Copy(s,1,4)='GET ' then //erste Zeile auseinanderbauen begin Delete(s,1,4); i:=pos(' ',s); Req:=copy(s,1,i-1); Delete(s,1,i); if copy(s,1,5)='HTTP/' then begin //antworten (eigentlich müsste man noch den Rest des Headers analysieren) SendOK(req); end else SendNotImpl; end else SendNotImpl; end; for i:=0 to Endpos do FReceivedText.Delete(0); //alles Bearbeitete löschen end; end; //"Fehler" senden procedure TClientData.SendNotImpl; begin FSocket.SendText('HTTP/1.1 501 Not Implemented'#13#10#13#10); end; //Antwort senden procedure TClientData.SendOK(const Content:String); var Ans:TStringList; HTML:String; begin ans:=TStringList.Create; try HTML:='<html><head><meta http-equiv="refresh" content="1; URL=/"></head><body>It Works? Or? ' +inttostr(FIndex) +' '+Content+'</body></html>'#13#10; ans.Add('HTTP/1.1 200 OK'); ans.add('Server: myDelphi'); ans.add('Content-Length: '+inttostr(length(HTML))); ans.add('Content-Language: de'); ans.add('Content-Type: text/html'); ans.add('Connection: close'); FSocket.SendText(Ans.Text+#13#10+HTML); finally ans.free; end; end; procedure TClientData.SetCanWrite(const Value: boolean); begin FCanWrite := Value; end; procedure TClientData.SetIndex(const Value: Integer); begin FIndex := Value; end; procedure TClientData.SetSocket(const Value: TCustomWinSocket); begin FSocket := Value; end; end. |
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Zitat:
Danke für eure schnelle Hilfe... Darkface EDIT: Ach sirius danke für deinen Code ich habs grad erst gesehen, ich werde mich zeitnah damit beschäftigen! :-D |
Re: ServerSocket/TcpServer als Webserver -> Schwierigkeit
Total genial sirius, vielen Dank für deine Hilfe!
Hab mit deinem Code den mini Webserver umgebaut und es hat wunderbar hingehauen! :-D MfG Darkface |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:48 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