I have the following code in my OnExecute in C++ Builder XE:
void __fastcall Test::TestExecute( TIdContext* AContext )
{
try
{
// get the command directive
DWORD startTime = timeGetTime( );
UnicodeString DBCommand = AContext->Connection->IOHandler->ReadChar();
DWORD endTime = timeGetTime();
UnicodeString log;
log.printf( L"getting command %d ms", endTime - startTime );
Log( log );
...
The log starts at getting command 100 milliseconds and creeps to 300 where it sits for the rest of the application run. I thought that OnExecute was called once data was in the buffer, so why would it take 100 to 300 ms for the first read to succeed?
After this first read in the same OnExecute all other data is read very very quickly (millisecond to sub millisecond).
What could be going wrong?
EDIT:
at method launch: AContext->Connection->IOHandler->InputBuffer->Size is 0. After the first read returns AContext->Connection->IOHandler->InputBuffer->Size contains whats left int he buffer after the read. So this implies that OnExecute is called before any data is actually available to the caller. So the 100-300 ms is the amount of time its taking Indy to fetch the data from the socket and place it in the Buffer after it get notification that data is arriving. That seems way too long.
EDIT:
removed do{ as it was implying a loop that was not there.
The OnExecute event is not tied to the socket buffer at all. TIdTCPServer begins calling OnExecute immeidately after the OnConnect event is called, and continues calling OnExecute in an endless loop until the client disconnects (in other words, you should NOT be looping yourself inside of your OnExecute handler. Read a packet, process, exit, wait for the next event, repeat).
You are correct that the InputBuffer can grow larger than what you are asking for in code. All of the IOHandler's reading methods get their data from the InputBuffer only, not the socket directly. If the InputBuffer does not have enough bytes cached to satisfy a read request, the IOHandler will then wait for bytes to be available on the socket, and will then read all of the bytes into the InputBuffer for later use. This minimizes how often the socket needs to be accessed, and help keep the socket responsive to new data.
Related
I am still confused about the NumberOfConcurrentThreads parameter within CreateIoCompletionPort(). I have read and re-read the MSDN dox, but the quote
This value limits the number of runnable threads associated with the
completion port.
still puzzles me.
Question
Let's assume that I specify this value as 4. In this case, does this mean that:
1) a thread can call GetQueuedCompletionStatus() (at which point I can allow a further 3 threads to make this call), then as soon as that call returns (i.e. we have a completion packet) I can then have 4 threads again call this function,
or
2) a thread can call GetQueuedCompletionStatus() (at which point I can allow a further 3 threads to make this call), then as soon as that call returns (i.e. we have a completion packet) I then go on to process that packet. Only when I have finished processing the packet do I then call GetQueuedCompletionStatus(), at which point I can then have 4 threads again call this function.
See my confusion? Its the use of the phrase 'runnable threads'.
I think it might be the latter, because the link above also quotes
If your transaction required a lengthy computation, a larger
concurrency value will allow more threads to run. Each completion
packet may take longer to finish, but more completion packets will be
processed at the same time.
This will ultimately affect how we design servers. Consider a server that receives data from clients, then echoes that data to logging servers. Here is what our thread routine could look like:
DWORD WINAPI ServerWorkerThread(HANDLE hCompletionPort)
{
DWORD BytesTransferred;
CPerHandleData* PerHandleData = nullptr;
CPerOperationData* PerIoData = nullptr;
while (TRUE)
{
if (GetQueuedCompletionStatus(hCompletionPort, &BytesTransferred,
(PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
// OK, we have 'BytesTransferred' of data in 'PerIoData', process it:
// send the data onto our logging servers, then loop back around
send(...);
}
}
return 0;
}
Now assume I have a four core machine; if I leave NumberOfConcurrentThreads as zero within my call to CreateIoCompletionPort() I will have four threads running ServerWorkerThread(). Fine.
My concern is that the send() call may take a long time due to network traffic. Hence, I could be receiving a load of data from clients that cannot be dequeued because all four threads are taking a long time sending the data on?!
Have I missed the point here?
Update 07.03.2018 (This has now been resolved: see this comment.)
I have 8 threads running on my machine, each one runs the ServerWorkerThread():
DWORD WINAPI ServerWorkerThread(HANDLE hCompletionPort)
{
DWORD BytesTransferred;
CPerHandleData* PerHandleData = nullptr;
CPerOperationData* PerIoData = nullptr;
while (TRUE)
{
if (GetQueuedCompletionStatus(hCompletionPort, &BytesTransferred,
(PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
{
switch (PerIoData->Operation)
{
case CPerOperationData::ACCEPT_COMPLETED:
{
// This case is fired when a new connection is made
while (1) {}
}
}
}
I only have one outstanding AcceptEx() call; when that gets filled by a new connection I post another one. I don't wait for data to be received in AcceptEx().
I create my completion port as follows:
CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 4)
Now, because I only allow 4 threads in the completion port, I thought that because I keep the threads busy (i.e. they do not enter a wait state), when I try and make a fifth connection, the completion packet would not be dequeued hence would hang! However this is not the case; I can make 5 or even 6 connections to my server! This shows that I can still dequeue packets even though my maximum allowed number of threads (4) are already running? This is why I am confused!
the completion port - is really KQUEUE object. the NumberOfConcurrentThreads is corresponded to MaximumCount
Maximum number of concurrent threads the queue can satisfy waits for.
from I/O Completion Ports
When the total number of runnable threads associated with the
completion port reaches the concurrency value, the system blocks the
execution of any subsequent threads associated with that completion
port until the number of runnable threads drops below the concurrency
value.
it's bad and not exactly said. when thread call KeRemoveQueue ( GetQueuedCompletionStatus internal call it) system return packet to thread only if Queue->CurrentCount < Queue->MaximumCount even if exist packets in queue. system not blocks any threads of course. from another side look for KiInsertQueue - even if some threads wait on packets - it activated only in case Queue->CurrentCount < Queue->MaximumCount.
also look how and when Queue->CurrentCount is changed. look for KiActivateWaiterQueue (This function is called when the current thread is about to enter a wait state) and KiUnlinkThread. in general - when thread begin wait for any object (or another queue) system call KiActivateWaiterQueue - it decrement CurrentCount and possible (if exist packets in queue and became Queue->CurrentCount < Queue->MaximumCount and threads waited for packets) return packet to wait thread. from another side, when thread stop wait - KiUnlinkThread is called. it increment CurrentCount.
your both variant is wrong. any count of threads can call GetQueuedCompletionStatus(). and system of course not blocks the execution of any subsequent threads. for example - you have queue with MaximumCount = 4. you can queue 10 packets to queue. and call GetQueuedCompletionStatus() from 7 threads in concurrent. but only 4 from it got packets. another will be wait (despite yet 6 packets in queue). if some of threads, which remove packet from queue begin wait - system just unwait and return packet to another thread wait on queue. or if thread (which already previous remove packet from this queue (Thread->Queue == Queue) - so active thread) again call KeRemoveQueue will be Queue->CurrentCount -= 1;
My Environment:
Windows 7 Pro (32bit)
C++ Builder XE4
I would like to know about wait after WriteLn();
Following is my sample code.
void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
UTF8String rcvdStr;
rcvdStr = AContext->Connection->IOHandler->ReadLn(
IndyTextEncoding(TEncoding::UTF8) );
TList *threads;
TIdContext *ac;
threads = IdTCPServer1->Contexts->LockList();
ac = reinterpret_cast<TIdContext *>(threads->Items[0]);
UTF8String sendStr;
sendStr = "send:" + rcvdStr;
ac->Connection->IOHandler->WriteLn(sendStr);
for(int idx=0; idx<10; idx++) {
Sleep(100);
Application->ProcessMessages();
}
ac->Connection->Disconnect();
IdTCPServer1->Contexts->UnlockList();
}
//---------------------------------------------------------------------------
I am putting wait (for(int idx=0;...) after WriteLn() so that the sending should be completed before Disconnection. However, I am not sure whether this is a correct way to wait. Also I have no idea how long should I wait (in this sample, I wait 1000 msec).
Question: Are there any function to know completion of WriteLn()?
You don't need to wait at all. WriteLn() is a blocking function. It does not exit until the entire string has been placed into the socket's outbound buffer. By default, a socket's LINGER option is enabled, which means a closed socket will attempt to send pending outbound data in the background before fully closing the port, even after your code has moved on.
Refer to MSDN For more details:
Graceful Shutdown, Linger Options, and Socket Closure
For the record, Indy's Disconnect() does use both shutdown() and closesocket().
I'm new to delphi and this is my first project.
Here's a little bit of code:
procedure TForm1.Button2Click(Sender: TObject);
responseStringFromServer:TStringStream;
begin
try
if IdTCPClient1.Connected then
begin
dataSentToDevice:= 'http/1.0 content-length: 344 content-type: text/xml <?xml version="1.0" encoding="UTF-8" ?> ...'
IdTCPClient1.IOHandler.WriteLn(dataSentToDevice);
responseStringFromServer := TStringStream.Create;
IdTCPClient1.IOHandler.ReadStream(responseStringFromServer);
...
I have a device connected to local network. I manage to connect to it succesfully. Sending commands is working too, but when i do
IdTCPClient1.IOHandler.ReadStream(responseStringFromServer);
then it waits til device is done processing and an exception occurs: "Connection Closed Gracefully". So i'm not able to read data the device is supposed to send me. Device is not shutting down. I've read other posts and i understand that device itself drops connection.
I have a demo program that communicates with it and it works fine. I need to get response xml before it drops the connection.
Also the request is http and i am using IdTCPClient (i need to use xml request, i don't know how to do it with TidHTTP).
May it be that after device is sending response it drops the connection, so that my tcpclient gets connection dropped flag before receiving data.
Any help would be appreciated!
Also the request is http and i am
using IdTCPClient (i need to use xml
request, i don't know how to do it
with TidHTTP).
Working with IdHTTP is simple...
Drop an instance of it on your form,
select it.
In Object Inspector, go to
ProtocolVersion property, and set it
to pv1_0, then open its
Request property set, and set
Request.ContentType to text/xml, and
Request.ContentEncoding to UTF-8,
and set other properties if
required.
Add a button to your form and
double-click on it.
In your code, create an instance of
TStringStream, and load your XML
content into it.
Assign your stream to
IdHttp.Request.Source.
Call IdHttp.Get() method by giving
it a host address.
IdHttp.Get() returns a string which
is the response the server sent you.
The way you are calling ReadStream(), it will interpret the first 4 bytes (or 8 bytes if the TIdIOHandler.LargeStream property is True) as an Integer (or Int64) in network-byte order that specifies the length of the data, and then it will try to read that many bytes. Is the device actually sending such a length value? If not, then ReadStream() will attempt to read the wrong number of bytes. An EIdConnClosedGracefully exception means the device is closing the socket on its end. So either the device is closing the connection immediately after sending its data, or it is timing out waiting for you to send the next command, which you cannot do since you are blocked waiting for the wrong data from the previous command.
As said by #Remy Lebeau and the documentation (press F1 when your edit caret is over the ReadStream sentence):
AByteCount indicates the number of bytes from the IOHandler to be read into AStream. When AByteCount contains -1 and AReadUntilDisconnect contains False, the byte count is read as an Integer value from the IOHandler. The size of AStream is adjusted to match the size expected from the IOHandler.
This is very useful when you're connecting to servers written also in INDY WriteStream in one end, ReadStream in the other, or any other language if you send that byte cound as expected by INDY client.
If you're reading from a device, be sure the device is sending that info up-front the stream or change the way you read the data. If you know the size of the stream, just pass the second parameter: AByteCount. If the device will close the channel when the stream ends, pass -1 as the second parameter and True as the third parameter: AReadUntilDisconnect.
If the device send text of unknown length but using a known terminator (like CR/LF), you better use ReadLn method of IOHandler to get that string.
Take a look at if neither fits your needs, take a look on Read* methods of TIOHandlerClass and RTFM for each to find the correct one to get the data sent by the device.
I have come to the point of thinking that the onWrite Event of a ClientSocket is redundant when I directly write bytes into the socket connection via SendBuf().
Is my point of thinking somewhere in the desert?
The Delphi Documentation is also somewhat bad because it just sais: "Write a routine for the onWrite event to write into the socket connection."
OnWrite is used when you're using asynchronous IO (when you have ClientType = ctNonBlocking, in other words). It's called when the socket's ready for you to send data.
Thus, your thinking's only half in the desert: if you're using ctBlocking, then don't bother with OnWrite at all. If you need that thread to send data and get on with other stuff at the same time, then use ctNonBlocking and write to the socket in OnWrite.
When you use async sockets, Windows will send your socket a CM_SOCKETMESSAGE, handled by TCustomWinSocket.CMSocketMessage. When that message has its SelectEvent property set to FD_WRITE, the OnWrite's (ultimately) invoked.
The magic ingredient here is the call to WSAAsyncSelect in TCustomWinSocket.DoSetAsyncStyles.
I have been doing overlapped serial port communication in Delphi lately and there is one problem I'm not sure how to solve.
I communicate with a modem. I write a request frame (an AT command) to the modem's COM port and then wait for the modem to respond. The event mask of the port is set to EV_RXCHAR, so when I write a request, I call WaitCommEvent() and start waiting for data to appear in the input queue. When overlapped waiting for event finishes, I immediately start reading data from the queue and read all that the device sends at once:
1) write a request
2) call WaitCommEvent() and wait until waiting finishes
3) read all the data that the device sends (not only the data being in the input queue at that moment)
4) do something and then goto 1
Waiting for event finishes after first byte appears in the input queue. During my read operation, however, more bytes appear in the queue and each of them causes an internal event flag to be set. This means that when I read all the data from the queue and then call WaitCommEvent() for the second time, it will immediately return with EV_RXCHAR mask, even though there is no data to be read.
How should I handle reading and waiting for event to be sure that the event mask returned by WaitCommEvent() is always valid? Is it possible to reset the flags of the serial port so that when I read all data from the queue and call WaitCommEvent() after then, it will not return immediately with a mask that was valid before I read the data?
The only solution that comes to my mind is this:
1) write a request
2) call WaitCommEvent() and wait until waiting finishes
3) read all the data that the device sends (not only the data being in the input queue at that moment)
4) call WaitCommEvent() which should return true immediately at the same time resetting the event flag set internally
5) do something and goto 1
Is it a good idea or is it stupid? Of course I know that the modem almost always finishes its answers with CRLF characters so I could set the comm mask to EV_RXFLAG and wait for the #10 character to appear, but there are many other devices with which I communicate and they do not always send frame end characters.
Your help will be appreciated. Thanks in advance!
Mariusz.
Your solution does sound workable. I just use a state machine to handle the transitions.
(psuedocode)
ioState := ioIdle;
while (ioState <> ioFinished) and (not aborted) do
Case ioState of
ioIdle : if there is data to read then set state to ioMidFrame
ioMidframe : if data to read then read, if end of frame set to ioEndFrame
ioEndFrame : process the data and set to ioFinished
ioFinished : // don't do anything, for doc purposes only.
end;