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;
Related
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;
I am trying to disconnect inactive clients that are connected to TIdTCPServer, whether those clients are disconnected from their Internet or for a period of inactive time.
I tried to set timeouts in the OnConnect event like the following:
procedure TservForm.TcpServerConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.ReadTimeout := 26000;
AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 15000);
end;
But it seems a disconnect is not triggered after the client connection is lost.
I tried to use SetKeepAliveValues(), but it takes too much time to get an inactive client disconnected.
Is there a more helpful way to disconnect inactive clients? So if the client did not receive or send anything, for example in 30 seconds, the server will disconnect it?
on execute event
procedure TservForm.TcpServerExecute(AContext: TIdContext);
var
Connection: TConnection;
cmd: String;
Cache, OutboundCmds: TStringList;
MS: TMemoryStream;
I: integer;
S: String;
begin
Connection := AContext as TConnection;
// check for pending outbound commands...
OutboundCmds := nil;
try
Cache := Connection.OutboundCache.Lock;
try
if Cache.Count > 0 then
begin
OutboundCmds := TStringList.Create;
OutboundCmds.Assign(Cache);
Cache.Clear;
end;
finally
Connection.OutboundCache.Unlock;
end;
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
begin
AContext.Connection.IOHandler.Writeln(OutboundCmds.Strings[I],
IndyTextEncoding_UTF8);
MS := TMemoryStream(OutboundCmds.Objects[I]);
if MS <> nil then
begin
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
AContext.Connection.IOHandler.LargeStream := true;
AContext.Connection.IOHandler.Write(MS, 0, true);
end;
end;
end;
finally
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
OutboundCmds.Objects[I].Free;
end;
OutboundCmds.Free;
end;
// check for a pending inbound command...
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
Exit;
end;
end;
cmd := AContext.Connection.Socket.ReadLn(IndyTextEncoding_UTF8);
...............
...............
The client does not disconnect because the ReadLn() is not reached during idle times, so the ReadTimeout does not have effect, and if you are not sending a lot of commands then the socket buffer is not filling up so SO_SNDTIMEO does not have an effect, either.
Since you are already doing some manual timeout handling, you can expand on it to handle an idle timeout as well, eg:
type
TConnection = class(TIdServerContext)
...
public
LastSendRecv: LongWord;
...
end;
...
procedure TservForm.TcpServerConnect(AContext: TIdContext);
var
Connection: TConnection;
begin
Connection := AContext as TConnection;
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.ReadTimeout := 30000;
AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 15000);
Connection.LastSendRecv := Ticks;
end;
procedure TservForm.TcpServerExecute(AContext: TIdContext);
var
Connection: TConnection;
cmd: String;
Cache, OutboundCmds: TStringList;
MS: TMemoryStream;
I: integer;
S: String;
begin
Connection := AContext as TConnection;
// check for pending outbound commands...
OutboundCmds := nil;
try
Cache := Connection.OutboundCache.Lock;
try
if Cache.Count > 0 then
begin
OutboundCmds := TStringList.Create;
OutboundCmds.Assign(Cache);
Cache.Clear;
end;
finally
Connection.OutboundCache.Unlock;
end;
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
begin
AContext.Connection.IOHandler.WriteLn(OutboundCmds.Strings[I]);
MS := TMemoryStream(OutboundCmds.Objects[I]);
if MS <> nil then
AContext.Connection.IOHandler.Write(MS, 0, true);
end;
Connection.LastSendRecv := Ticks;
end;
finally
if OutboundCmds <> nil then
begin
for I := 0 to OutboundCmds.Count - 1 do
OutboundCmds.Objects[I].Free;
end;
OutboundCmds.Free;
end;
// check for a pending inbound command...
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
if GetTickDiff(Connection.LastSendRecv, Ticks) >= 30000 then
AContext.Connection.Disconnect;
Exit;
end;
end;
cmd := AContext.Connection.Socket.ReadLn;
Connection.LastSendRecv := Ticks;
...
end;
I have to send an XML-File to a Website with a secure Connection. Delphi 2010, Indy 10.5.9. The Code used is as follows:
Params: TIdMultiPartFormDataStream;
ResponseStr: string;
begin
result := 0;
sRootCertFile := 'xxx\Digital.pem';
sCertFile := 'xxx\Digital.pem';
sKeyFile := 'xxx\Digital.pem';
with FAdminSetup.RDWSSLHandler do
begin
SSLOptions.VerifyMode := [];
SSLOptions.VerifyDepth := 0;
SSLOptions.RootCertFile := sRootCertFile;
SSLOptions.CertFile := sCertFile;
SSLOptions.KeyFile := sKeyFile;
end;
sURL := 'https://xxx/xxxservice';
begin
IdHttpVVO := TIdHttp.Create(nil);
try
// IdHttpVVO.Request.ContentType := 'multipart/form-data';
// IdHttpVVO.ProtocolVersion := pv1_1;
// IdHttpVVO.HTTPOptions := [hoKeepOrigProtocol,hoForceEncodeParams];
// IdHttpVVO.Request.Connection := 'Keep-Alive';
// IdHttpVVO.Request.CacheControl := 'no-cache';
// IdHttpVVO.Request.ContentLength := Length(sAnsiXML); // <-- new
IdHttpVVO.IOHandler := FAdminSetup.RDWSSLHandler;
Params := TIdMultiPartFormDataStream.Create;
try
with Params do
begin
AddFile('file', filename, GetMIMETypeFromFile(filename));
end;
resultStr := IdHttpVVO.Post(sURL, Params);
finally
Params.Free;
end;
ShowMessage(resultstr);
The result is always the same:
'HTTP/1.0 500 Error'
when doing the post part.
All the remarks have been tried and did not give any change. The Password for the connection is supplied as follows:
procedure TFAdminSetup.RDWSSLHandlerGetPasswordEx(ASender: TObject;
var VPassword: AnsiString; const AIsWrite: Boolean);
begin
VPassword := 'xxx';
end;
The Website is working, the certificates seem to be ok, as there is a small tool included written in C that works.
Where is my mistake? Thanks
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;
I've a sample ISAPI module hosts on IIS7 on Windows Vista Ultimate in Virtual Machine. According to the IIS 7.0 Editions and Windows, the simultaneous request execution limit is ten for Windows Vista Ultimate. I write a sample application for testing the IIS7 simultaneous request execution limit:
procedure TForm1.Button1Click(Sender: TObject);
const c_Max = 20;
var P: TProc<string>;
o: TThread;
i: integer;
A: array of TThread;
begin
P :=
procedure(aValue: string)
begin
Memo1.Lines.Add(aValue);
end;
SetLength(A, c_Max);
for i := Low(A) to High(A) do begin
o := TMyThread.Create(P, edHostName.Text, Format('test%d', [i + 1]), True);
o.FreeOnTerminate := True;
A[i] := o;
end;
for o in A do
o.Start;
end;
constructor TMyThread.Create(const aMethod: TProc<string>; const aHostName,
aValue: string; const aCreateSuspended: boolean);
begin
inherited Create(aCreateSuspended);
FMethod := aMethod;
FHostName := aHostName;
FValue := aValue;
end;
procedure TMyThread.Execute;
var C: TSQLConnection;
o: TServerMethods1Client;
S: string;
begin
C := TSQLConnection.Create(nil);
C.DriverName := 'DataSnap';
C.LoginPrompt := False;
with C.Params do begin
Clear;
Values[TDBXPropertyNames.DriverUnit] := 'Data.DBXDataSnap';
Values[TDBXPropertyNames.HostName] := FHostName;
Values[TDBXPropertyNames.Port] := IntToStr(80);
Values[TDBXPropertyNames.CommunicationProtocol] := 'http';
Values[TDBXPropertyNames.URLPath] := 'MyISAPI/MyISAPI.dll';
end;
S := Format('%d=', [ThreadID]);
try
try
C.Open;
o := TServerMethods1Client.Create(C.DBXConnection);
try
S := S.Format('%s%s', [S, o.EchoString(FValue)]);
finally
o.Free;
end;
except
on E: Exception do
S := S.Format('%s%s', [S, E.Message]);
end;
finally
Synchronize(
procedure
begin
FMethod(S);
end
);
C.Free;
end;
end;
When i click the Button1, the Memo1 displays 20 lines of string (ThreadID=Result of EchoString), even though i change the constant variable c_Max to 500, i still can't get any line of string (ThreadID=Exception message) displays on Memo1. My question is how to test IIS7 simultaneous request execution limit?