Cannot connect to TIdHTTPServer from TIdHTTP in delphi - delphi

I have an application with TIdHTTPServer and TIdHTTP in delphi and I have this code :
// This is for activating the HTTPServer - works as expected
HTTPServer1.Bindings.Add.IP := '127.0.0.1';
HTTPServer1.Bindings.Add.Port := 50001;
HTTPServer1.Active := True;
This is the OnCommandGet procedure of my HTTPServer :
procedure TDataForm.HttpServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
AResponseInfo.ContentText := 'Hello, user';
end;
And I just don't know why this procedure isn't working :
procedure TDataForm.btnHTTPSendGetClick(Sender: TObject);
var
HTTPClient : TIdHTTP;
responseStream : TMemoryStream;
begin
HTTPClient := TIdHTTP.Create;
responseStream := TMemoryStream.Create;
try
try
HTTPClient.Get('http://127.0.0.1:50001', responseStream);
except on e : Exception do begin
showmessage('Could not send get request to localhost, port 50001');
end;
end;
finally
FreeAndNil(HTTPClient);
FreeAndNil(responseStream);
end;
end;
If I connect via browser I can see in the browser 'Hello, user', but if I try btnHTTPSendGetClick my program crashes with no exception or anything. Can anyone help me fix my code ?

HTTPServer1.Bindings.Add.IP := '127.0.0.1';
HTTPServer1.Bindings.Add.Port := 50001;
This is a common newbie mistake. You are creating two bindings, one bound to 127.0.0.1:DefaultPort, and one bound to 0.0.0.0:50001. You need one binding instead, that is bound to 127.0.0.1:50001 instead.
with HTTPServer1.Bindings.Add do begin
IP := '127.0.0.1';
Port := 50001;
end;
Or:
HTTPServer1.Bindings.Add.SetBinding('127.0.0.1', 50001, Id_IPv4);
Or:
HTTPServer1.DefaultPort := 50001;
HTTPServer1.Bindings.Add.IP := '127.0.0.1';
With that said, your server response is incomplete. Try this instead:
procedure TDataForm.HttpServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentType := 'text/plain';
AResponseInfo.ContentText := 'Hello, user';
end;

Related

TIpTCPServer and Client in one application

I make an application where the client and the server are in the same program. I use Delphi XE7 and components TIpTCPServer / ... Client. But when I try to close the server with the client connected (in the same window), the program stops responding. Perhaps this is something related to multithreading. How to implement a program with a client and server in one application and is this the right approach?
procedure TfrmMain.startClick(Sender: TObject);
begin
if (server.active) then stopServer()
else startServer();
end;
procedure TfrmMain.startServer();
var
binding: TIdSocketHandle;
begin
server.bindings.clear();
try
server.defaultPort := strToInt(port.text);
binding := server.bindings.add();
binding.ip := ip;
binding.port := strToInt(port.text);
server.active := true;
if (server.active) then begin
addToLog('Server started');
start.caption := 'Stop';
end;
except on e: exception do
addToLog('Error: ' + e.message + '.');
end;
end;
procedure TfrmMain.stopServer();
begin
server.active := false;
server.bindings.clear();
if (not(server.active)) then begin
addToLog('Server stopped');
start.caption := 'Start';
end
else addToLog('Server shutdown error.');
end;
procedure TfrmMain.serverConnect(AContext: TIdContext);
var
i: integer;
begin
addToLog('New client: ' + aContext.connection.socket.binding.peerIP + '.');
clients.clear();
for i := 0 to server.contexts.lockList.count - 1 do begin
with TIdContext(server.contexts.lockList[i]) do
clients.items.add(connection.socket.binding.peerIP);
end;
server.contexts.unlockList();
end;
procedure TfrmMain.serverDisconnect(AContext: TIdContext);
begin
addToLog('Client ' + aContext.connection.socket.binding.peerIP + ' disconnected from the server.');
end;
procedure TfrmMain.clientConnected(Sender: TObject);
begin
addToConsole('You connected to server successfully.');
end;
procedure TfrmMain.clientDisconnected(Sender: TObject);
begin
addToConsole('The connection to the server was interrupted.');
end;
and connection code:
client.host := ip;
try
client.connect();
except on e: exception do
addToConsole('Error: ' + e.message);
end;
I see a number of issues with this code.
How are addToLog() and addToConsole() implemented? Are they thread-safe? Remember that TIdTCPServer is a multi-threaded component, its events are fired in the context of worker threads, not the main UI thread, so any access to the UI, shared variables, etc must be synchronized.
What is clients? Is it is a UI control? You need to sync access to it so you don't corrupt its content when multiple threads try to access it at the same time.
Your use of the TIdTCPServer.Contexts property is not adequately protected from exceptions. You need a try..finally block so you can call Contexts.UnlockList() safely.
More importantly, you are calling Contexts.LockList() too many times in your serverConnect() loop (this is the root cause of your problem). LockList() returns a TIdContextList object. Inside your loop, you should be accessing that list's Items[] property instead of calling LockList() again. Because you do not have a matching UnlockList() for each LockList(), once a client connects to your server, the Contexts list becomes deadlocked, and can no longer be accessed once serverConnect() exits, which includes when clients connect/disconnect, and during TIdTCPServer shutdown (such as in your case).
serverDisconnect() is not removing any items from clients. serverConnect() should not be resetting clients at all. It should add only the calling TIdContext to clients, and then serverDisconnect() should remove that same TIdContext from clients later.
With that said, try something more like this:
procedure TfrmMain.addToConsole(const AMsg: string);
begin
TThread.Queue(nil,
procedure
begin
// add AMsg to console ...
end
);
end;
procedure TfrmMain.addToLog(const AMsg: string);
begin
TThread.Queue(nil,
procedure
begin
// add AMsg to log ...
end
);
end;
procedure TfrmMain.startClick(Sender: TObject);
begin
if server.Active then
stopServer()
else
startServer();
end;
procedure TfrmMain.startServer();
var
binding: TIdSocketHandle;
begin
server.Bindings.Clear();
try
server.DefaultPort := StrToInt(port.Text);
binding := server.Bindings.Add();
binding.IP := ip;
binding.Port := StrToInt(port.Text);
server.Active := True;
addToLog('Server started');
start.Caption := 'Stop';
except
on e: Exception do
addToLog('Error: ' + e.message + '.');
end;
end;
procedure TfrmMain.stopServer();
begin
try
server.Active := False;
server.Bindings.Clear();
addToLog('Server stopped');
start.Caption := 'Start';
except
on e: Exception do
addToLog('Server shutdown error.');
end;
end;
procedure TfrmMain.serverConnect(AContext: TIdContext);
var
PeerIP: string;
begin
PeerIP := AContext.Binding.PeerIP;
addToLog('New client: ' + PeerIP + '.');
TThread.Queue(nil,
procedure
{
var
i: integer;
list: TIdContextList;
}
begin
{
clients.clear();
list := server.Contexts.LockList;
try
for i := 0 to list.count - 1 do begin
clients.Items.Add(TIdContext(list[i]).Binding.PeerIP);
end;
finally
list.UnlockList();
end;
}
// I'm assuming clients is a UI control whose Items property
// is a TStrings object. If not, adjust this code as needed...
clients.Items.AddObject(PeerIP, AContext);
end;
);
end;
procedure TfrmMain.serverDisconnect(AContext: TIdContext);
begin
addToLog('Client ' + AContext.Binding.PeerIP + ' disconnected from the server.');
TThread.Queue(nil,
procedure
var
i: Integer;
begin
// I'm assuming clients is a UI control whose Items property
// is a TStrings object. If not, adjust this code as needed...
i := clients.Items.IndexOfObject(AContext);
if i <> -1 then
clients.Items.Delete(i);
end
);
end;
procedure TfrmMain.clientConnected(Sender: TObject);
begin
addToConsole('You connected to server successfully.');
end;
procedure TfrmMain.clientDisconnected(Sender: TObject);
begin
addToConsole('The connection to the server was interrupted.');
end;

Delphi IdHTTP server load html

I have created a VCL application and I need to create an HTTP server that runs in my network. I have created the code that you can see below:
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
a: TStringList;
count, logN: integer;
begin
if ARequestInfo.Document = '/' then
begin
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentText := IndexMemo.Lines.Text;
Memo1.Lines.Add(' Client: ' + ARequestInfo.RemoteIP);
end
else
begin
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>';
end;
end;
Now I have only a test case if ARequestInfo.Document = '/' then but later I'll need a lot of them. I have found this solution:
Drop a memo in the form
Add the html inside the memo
Load the text of the memo in the ContextText
I don't think that this is very efficient because I'd have to drop like 20 TMemo in my form and the HTML will be difficult to maintain. I thought that I could load the html pages with the Deployment manager.
In the same folder of the Delphi project I have created a folder called pages and it will contain the html files. I am not sure on how to load html pages with an indy HTTP server, so my questions are:
Do I have to store the html pages somewhere in a folder and then load them using indy?
Can I load html pages with indy that are included in the Deployment page?
Note: I would like to have a single exe (which is the http server) and not a folder with exe + html files. The solution that I have found works pretty well because I use a lot of TMemo to store the code, but this is not easy to maintain.
First, the code you have shown is not thread-safe. TIdHTTPServer is a multi-threaded component, the OnCommand... events are triggered in the context of worker threads. You must synchronize with the main UI thread in order to access UI controls safely, eg:
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
s: string;
begin
if ARequestInfo.Document = '/' then
begin
TThread.Synchronize(nil,
procedure
begin
s := IndexMemo.Lines.Text;
Memo1.Lines.Add(' Client: ' + ARequestInfo.RemoteIP);
end
);
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentText := s;
AResponseInfo.ContentType := 'text/plain';
end
else
begin
AResponseInfo.ResponseNo := 404;
AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>';
AResponseInfo.ContentType := 'text/html';
end;
end;
Do I have to store the html pages somewhere in a folder and then load them using indy?
Can I load html pages with indy that are included in the Deployment page?
If you want to serve files from the local filesystem, you have to translate the ARequestInfo.Document property value to a local file path, and then you can either:
load the requested file into a TFileStream and assign it to the AResponseInfo.ContentStream property:
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
str, filename: string;
begin
str := ' Client: ' + ARequestInfo.RemoteIP + ' requesting: ' + ARequestInfo.Document;
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add(str);
end
);
if TextStartsWith(ARequestInfo.Document, '/') then
begin
filename := Copy(ARequestInfo.Document, 2, MaxInt);
if filename = '' then
filename := 'index.txt';
// determine local path to requested file
// (ProcessPath() is declared in the IdGlobalProtocols unit)...
filename := ProcessPath(YourDeploymentFolder, filename);
if FileExists(filename) then
begin
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentStream := TFileStream.Create(filename, fmOpenRead or fmShareDenyWrite);
AResponseInfo.ContentType := IdHTTPServer1.MIMETable.GetFileMIMEType(filename);
Exit;
end;
end;
AResponseInfo.ResponseNo := 404;
AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>';
AResponseInfo.ContentType := 'text/html';
end;
pass the file path to the TIdHTTPResponseInfo.(Smart)ServeFile() method and let it handle the file for you:
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
str, filename: string;
begin
str := ' Client: ' + ARequestInfo.RemoteIP + ' requesting: ' + ARequestInfo.Document;
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add(str);
end
);
if TextStartsWith(ARequestInfo.Document, '/') then
begin
filename := Copy(ARequestInfo.Document, 2, MaxInt);
if filename = '' then
filename := 'index.txt';
// determine local path to requested file...
filename := ProcessPath(YourDeploymentFolder, filename);
AResponseInfo.SmartServeFile(AContext, ARequestInfo, filename);
Exit;
end;
AResponseInfo.ResponseNo := 404;
AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>';
AResponseInfo.ContentType := 'text/html';
end;
I would like to have a single exe (which is the http server) and not a folder with exe + html files.
In that case, save the HTML files into the EXE's resources at compile-time (using an .rc file, or the IDE's Resources and Images dialog. See Resource Files Support for more details) and then translate the ARequestInfo.Document into a resource ID/Name that you can load with TResourceStream for use as the AResponseInfo.ContentStream object:
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
str, resID: string;
strm: TResourceStream;
begin
str := ' Client: ' + ARequestInfo.RemoteIP + ' requesting: ' + ARequestInfo.Document;
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add(str);
end
);
if TextStartsWith(ARequestInfo.Document, '/') then
begin
// determine resource ID for requested file
// (you have to write this yourself)...
resID := TranslateIntoResourceID(Copy(ARequestInfo.Document, 2, MaxInt));
try
strm := TResourceStream.Create(HInstance, resID, RT_RCDATA);
except
on E: EResNotFound do
strm := nil;
end;
if strm <> nil then
begin
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentStream := strm;
AResponseInfo.ContentType := 'text/html';
Exit;
end;
end;
AResponseInfo.ResponseNo := 404;
AResponseInfo.ContentText := '<html><body><b>404 NOT FOUND</b></body></html>';
AResponseInfo.ContentType := 'text/html';
end;
You can read the Content from a file
procedure TForm2.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var Page : TStringStream;
begin
Page := TStringStream.Create;
Page.LoadFromFile('put the file path here');
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentStream := page;
end;
You can read the Content from a Resource, go to Project Menu, Resources and Imagens, add the resources that you need.
procedure TForm2.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var page : TResourceStream;
begin
//home is the resource name
page := TResourceStream.Create(HInstance, 'home', RT_RCDATA);
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentStream := page;
end;

Using Indy TCPClient/TCPServer to send picture from mobile XE8

I have a simple mobile application written in Delphi XE8 that allows the user to take a picture and then send the picture to a server using Indy TCPClient/TCP Server.
I have scoured the forums and found numerous examples to send the data in a variety of ways. Every method I try results in an access violation or corrupt data on the server side.
My ultimate goal is to send a record containing a unique identifier, description and a picture(bitmap) from the client to the server.
But I'm starting out be trying to simply send a record with some text from a windows client to the server. I will then try to implement the solution into my mobile app.
type
TSendRec = record
// SONo: string;
Text: string;
// Bitmap: TBitMap;
end
I have tried the following 3 methods as per the code below:
Send Using a Stream
Send using RawToBytes and TIDBytes.
Send a line of text using Writeln and Readln
When I try to send using a stream I get the following access violation:
Project memorystream_server.exe raised the exception class $C0000005 with message 'access violation at 0x00409e46: write of address 0x0065d1bc
The error occurs when I try to access the value of MiRec.Text on the server side.
Memo1.Lines.Add(MiRec.Text);
So I assume the read of the MIRec is failing for some reason:
When I send using RawToBytes, no error message occurs but the value of MIRec.Text is garbage.
When I just send a line of text using WriteLn, the server receives and displays the data correctly and no access violation occurs.
I tried to follow examples that I have found from other posts on this issue. I would greatly appreciate any insight into what I am doing wrong.
Following are my client and server side code snippets:
Client
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
Buffer: TIdBytes;
MIRec: TSendRec;
msRecInfo: TMemoryStream;
msRecInfo2: TIdMemoryBufferStream;
begin
IdTCPClient1.Connect;
MIRec.Text := 'Hello World';
if rbSendStream.Checked then
begin
msRecInfo := TMemoryStream.Create;
try
msRecInfo.Write(MIRec, SizeOf(MIRec));
IdTCPClient1.IOHandler.Write(msRecInfo, 0, False);
finally
msRecInfo.Free;
end;
{
msRecInfo2 := TIdMemoryBufferStream.Create(#MIRec, SizeOf(TSendRec));
try
IdTCPClient1.IOHandler.Write(msRecInfo2);
finally
msRecInfo.Free;
end;
}
end
else
if rbSendBytes.Checked then
begin
Buffer := RawToBytes(MIRec, SizeOf(MIRec));
IdTCPClient1.IOHandler.Write(Buffer);
end
else
if rbWriteLn.Checked then
begin
IdTCPClient1.Socket.WriteLn(Edit1.Text);
end;
IdTCPClient1.DisConnect;
end;
Server
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var sName: String;
MIRec: TSendRec;
Buffer: TIdBytes;
msRecInfo: TMemoryStream;
begin
if not chkReceiveText.Checked then
begin
try
if chkReadBytes.Checked then
begin
AContext.Connection.IOHandler.ReadBytes(Buffer, SizeOf(MIRec));
BytesToRaw(Buffer, MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
end
else
begin
msRecInfo := TMemoryStream.Create;
try
// does not read the stream size, just the stream data
AContext.Connection.IOHandler.ReadStream(msRecInfo, SizeOf(MIRec), False);
msRecInfo.Position := 0;
msRecInfo.Read(MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
finally
msRecInfo.Free;
end;
{
AContext.Connection.IOHandler.ReadStream(msRecInfo, -1, False);
msRecInfo.Position := 0;
msRecInfo.Read(MIRec, SizeOf(MIRec));
Memo1.Lines.Add(MiRec.Text);
}
end;
Memo1.Lines.Add('read File');
except
Memo1.Lines.Add('error in read File');
end;
end
else
begin
sName := AContext.Connection.Socket.ReadLn;
Memo1.Lines.Add(sName);
end;
AContext.Connection.Disconnect;
end;
TIdTCPServer is a multithreaded component. Its OnConnect, OnDisconnect, and OnExecute events are triggered in the context of a worker thread. As such, you MUST synchronize with the main UI thread when accessing UI controls, like your Memo.
Also, String is a compiler-managed data type, and TBitmap is an object. Both store their data elsewhere in memory, so you cannot write a record containing such fields as-is. You would be writing only the value of their data pointers, not writing the actual data being pointed at. You need to serialize your record into a transmittable format on the sending side, and then deserialize it on the receiving side. That means handling the record fields individually.
Try something more like this:
type
TSendRec = record
SONo: string;
Text: string;
Bitmap: TBitMap;
end;
Client
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
MIRec: TSendRec;
ms: TMemoryStream;
begin
MIRec.SONo := ...;
MIRec.Text := 'Hello World';
MIRec.Bitmap := TBitmap.Create;
...
try
IdTCPClient1.Connect;
try
IdTCPClient1.IOHandler.WriteLn(MIRec.SONo);
IdTCPClient1.IOHandler.WriteLn(MIRec.Text);
ms := TMemoryStream.Create;
try
MIRec.Bitmap.SaveToStream(ms);
IdTCPClient1.IOHandler.LargeStream := True;
IdTCPClient1.IOHandler.Write(ms, 0, True);
finally
ms.Free;
end;
finally
IdTCPClient1.Disconnect;
end;
finally
MIRec.Bitmap.Free;
end;
end;
Server
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
MIRec: TSendRec;
ms: TMemoryStream;
begin
MIRec.SONo := AContext.Connection.IOHandler.ReadLn;
MIRec.Text := AContext.Connection.IOHandler.ReadLn;
MIRec.Bitmap := TBitmap.Create;
try
ms := TMemoryStream.Create;
try
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.ReadStream(ms, -1, False);
ms.Position := 0;
MIRec.Bitmap.LoadFromStream(ms);
finally
ms.Free;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(MIRec.SONo);
Memo1.Lines.Add(MIRec.Text);
// display MIRec.Bitmap as needed...
end;
end;
finally
MIRec.Bitmap.Free;
end;
end;
Alternatively:
Client
procedure TfrmMemoryStreamClient.btnSendClick2(Sender: TObject);
var
MIRec: TSendRec;
ms: TMemoryStream;
procedure SendString(const S: String);
var
Buf: TIdBytes;
begin
Buf := IndyTextEncoding_UTF8.GetBytes(S);
IdTCPClient1.IOHandler.Write(Int32(Length(Buf)));
IdTCPClient1.IOHandler.Write(Buf);
end;
begin
MIRec.SONo := ...;
MIRec.Text := 'Hello World';
MIRec.Bitmap := TBitmap.Create;
...
try
IdTCPClient1.Connect;
try
SendString(MIRec.SONo);
SendString(MIRec.Text);
ms := TMemoryStream.Create;
try
MIRec.Bitmap.SaveToStream(ms);
IdTCPClient1.IOHandler.LargeStream := True;
IdTCPClient1.IOHandler.Write(ms, 0, True);
finally
ms.Free;
end;
finally
IdTCPClient1.Disconnect;
end;
finally
MIRec.Bitmap.Free;
end;
end;
Server
procedure TStreamServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
MIRec: TSendRec;
ms: TMemoryStream;
function RecvString: String;
begin
Result := AContext.Connection.IOHandler.ReadString(
AContext.Connection.IOHandler.ReadInt32,
IndyTextEncoding_UTF8);
end;
begin
MIRec.SONo := RecvString;
MIRec.Text := RecvString;
MIRec.Bitmap := TBitmap.Create;
try
ms := TMemoryStream.Create;
try
AContext.Connection.IOHandler.ReadStream(ms, -1, False);
ms.Position := 0;
MIRec.Bitmap.LoadFromStream(ms);
finally
ms.Free;
end;
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(MIRec.SONo);
Memo1.Lines.Add(MIRec.Text);
// display MIRec.Bitmap as needed...
end;
end;
finally
MIRec.Bitmap.Free;
end;
end;

Indy Dynamic Create Server Client connecting error 10061

I am having trouble dynamically creating some INDY communication. I have not used Indy before and I can't get the server set up right. I get the error 10061. I have added a rule to the fire wall to allow this application and the port. It works if I use TServerSocket and TClientSocket but I need to use Indy.
This code does not work
procedure TForm3.Button3Click(Sender: TObject);
var
temp : TIdSocketHandles;
begin
fIdTCPServer1 := TIdTCPServer.Create(nil);
fIdTCPClient1 := TIdTCPClient.Create(nil);
temp := TIdSocketHandles.Create(fIdTCPServer1);
temp.Add.IP := 'xxx.xx.xxx.xx';//'localhost'; //gave error when tryingn to use local host
temp.Add.Port := 20200;
fIdTCPServer1.Bindings := temp;
fIdTCPServer1.OnExecute := IdTCPServer1Execute;
fIdTCPServer1.StartListening();
fIdTCPClient1.Port := 20200;
fIdTCPClient1.Host := 'xxx.xx.xxx.xx';//'localhost';
fIdTCPClient1.Connect; //error 10061 here
fIdTCPClient1.IOHandler.WriteLn('Some message');
end;
procedure TForm3.IdTCPServer1Execute(AContext: TIdContext);
var
inStr : String;
begin
inStr := AContext.Connection.IOHandler.ReadLn;
ShowMessage('Server received : ' + inStr);
end;
This code works
procedure TForm3.Button1Click(Sender: TObject);
begin
serverSocket1 := TServerSocket.Create(nil);
clientSocket1 := TClientSocket.Create(nil);
serverSocket1.Port := 20200;
serverSocket1.OnClientRead := ServerSocket1ClientRead;
serverSocket1.Active := True;
clientSocket1.Port := 20200;
clientSocket1.Host := 'localhost';
clientSocket1.Active := True;
clientSocket1.Socket.SendText('Some message');
end;
procedure TForm3.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
inStr : String;
begin
inStr := Socket.ReceiveText();
ShowMessage('Server received : ' + inStr);
end;
You need to set both IP address and port on each binding.
// Here you're adding a binding and setting only the IP address
temp.Add.IP := 'xxx.xx.xxx.xx';
// Here you're adding another binding and setting only the port
temp.Add.Port := 20200;
Here's an easy way to do it properly:
with fIdTCPServer1.Bindings.Add do
begin
IP := '127.0.0.1'; // Use IP address, not host name. Localhost = 127.0.0.1
Port := 20200;
end;
Here's another, more verbose, way to do it (add IdSocketHandle to your uses clause):
var
socketHandle: TIdSocketHandle;
begin
socketHandle := fIdTCPServer1.Bindings.Add;
socketHandle.IP := '127.0.0.1';
socketHandle.Port := 20200;
end;
Also, to start it up, you just need to set Active to true like this:
fIdTCPServer1.Active := True;
There are a few problems with your code.
you are misusing the TIdTCPServer.Bindings, and you are not activating the server correctly.
you are calling ShowMessage() in the OnExecute event, but TIdTCPServer is multithreaded component (each connected client runs in its own thread) and ShowMessage() is not thread-safe.
you have some potential memory leaks in your code.
Try this instead:
procedure TForm3.Button3Click(Sender: TObject);
var
temp : TIdSocketHandle;
begin
if fIdTCPServer1 = nil then
begin
fIdTCPServer1 := TIdTCPServer.Create(Self);
temp := fIdTCPServer1.Bindings.Add;
temp.IP := 'xxx.xx.xxx.xx';//'localhost'; //gave error when tryingn to use local host
temp.Port := 20200;
fIdTCPServer1.OnExecute := IdTCPServer1Execute;
fIdTCPServer1.Active := True;
end;
if fIdTCPClient1 = nil then
begin
fIdTCPClient1 := TIdTCPClient.Create(Self);
fIdTCPClient1.Port := 20200;
fIdTCPClient1.Host := 'xxx.xx.xxx.xx';//'localhost';
end;
fIdTCPClient1.Connect;
fIdTCPClient1.IOHandler.WriteLn('Some message');
end;
procedure TForm3.IdTCPServer1Execute(AContext: TIdContext);
var
inStr : String;
begin
inStr := AContext.Connection.IOHandler.ReadLn;
Windows.MessageBox(0, PChar(inStr), PChar('Server received'), MB_OK);
end;

delphi 2010 - tidhttp post

i want to post data to the following url:
http://mehratin.heroku.com/personals/new
i write the following code but has problem:
procedure TForm1.Button3Click(Sender: TObject);
var
aStream: TMemoryStream;
Params: TStringList;
begin
aStream := TMemoryStream.Create;
Params := TStringList.Create;
try
with IdHTTP1 do
begin
Params.Add('fname=123');
Params.Add('lname=123');
Request.ContentType := 'application/x-www-form-urlencoded';
try
Response.KeepAlive := False;
Post('http://localhost:3000/personals/new', Params);
except
on E: Exception do
showmessage('Error encountered during POST: ' + E.Message);
end;
end;
how can i post data by TIDHtttp.post method in delphi 2010?
First things first, you'd need to read the http response code (it would have been useful to include that in your question).
In the absence of that I've used the Indy http object before as shown below. I included the parameters in my URL though. To troubleshoot, try running this with an http.Get to ensure the port is open, and you can actually connect to the server. Here's my example for completenes:
// parameters
params := format('export=1&format=%s&file=%s', [_exportType, destination]);
// First setup the http object
procedure TCrystalReportFrame.SetupHttpObject();
begin
try
IDHTTP1.HandleRedirects := TRUE;
IDHTTP1.AllowCookies := true;
IDHTTP1.Request.CacheControl := 'no-cache';
IdHTTP1.ReadTimeout := 60000;
_basePath:= GetBaseUrl;
except
on E: Exception do
begin
Global.LogError(E, 'SetupHttpObject');
end;
end;
end;
// Then have an onwork event
procedure TCrystalReportFrame.HttpWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
Http: TIdHTTP;
ContentLength: Int64;
Percent: Integer;
begin
Http := TIdHTTP(ASender);
ContentLength := Http.Response.ContentLength;
end;
// The actual process
procedure TCrystalReportFrame.ProcessHttpRequest(const parameters: string);
var
url : string;
begin
try
try
SetupHttpObject;
IdHTTP1.OnWork:= HttpWork;
url := format('%s&%s', [_basePath, parameters]);
url := IdHTTP1.Post(url);
except
on E: Exception do
begin
Global.LogError(E, 'ProcessHttpRequest');
end;
end;
finally
try
IdHTTP1.Disconnect;
except
begin
end;
end;
end;
end;

Resources