 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Bo Berglund Guest
|
Posted: Sat Feb 04, 2006 12:29 am Post subject: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
I have been using TClientSocket for ages since the Delphi 4 or 5 days
but I'm now using Delphi 7 where this component is depreciated in
favour of Indy, which is installed by default with D7.
So I tried to use the TIdTCPClient in my new project instead, but I
have real trouble understanding it. :-(
What I want to accomplish is simply to hook up two applications over
the network from one location to the other. One side will basically be
a port server where an RS232 channel is established with an external
system and the task is to relay *all* of the data sent by the external
system to the listening application.
There is absolutely no knowledge of when data will start streaming
from the equipment, we may even be shutting down the connection
without receiving a single byte.
This is to me a perfect case of having *event driven* code like this:
SERVER SIDE
------------
The ApdComport component generates an event when data arrives on the
RS232 line. In this event the server will receive the data and send it
to the socket if it is connected at the time.
(using TClientSocket.Socket.SendText)
That's all.
CLIENT SIDE
------------
When the client starts up it establishes a socket connection to the
server. Then in the OnRead event the data arriving over the network is
put into a buffer which is later processed.
When there are no data arriving the client is idle.
This was easy to do using the TClientSocket component but seems
completely impossible with TIdTCPClient...
The only way I have found to get data from the TIdTCPClient is to use
its ReadString method. But how can I know *when* to call ReadString???
There is no OnReceive event to hook it to! If I just call it randomly
then my application locks up inside the method...
And another problem, ReadString has a parameter telling it how many
bytes to read, but that is unknown! How can the client know how many
bytes the server is going to send??? The data are normally packaged
into packets delimited by STX and ETX but these can also appear inside
the contents and are then doubled. There is no other limtation,
packets can be of arbitrary length for example.
Unfortunately in true Delphi style of course there are no examples
referenced in the help either....
Can anyone explain this?
/Bo |
|
| Back to top |
|
 |
Ciaran Costelloe Guest
|
Posted: Sat Feb 04, 2006 2:22 am Post subject: Re: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
Bo Berglund wrote:
| Quote: | SERVER SIDE
------------
The ApdComport component generates an event when data arrives on the
RS232 line. In this event the server will receive the data and send it
to the socket if it is connected at the time.
(using TClientSocket.Socket.SendText)
That's all.
|
Fine.
| Quote: | CLIENT SIDE
------------
When the client starts up it establishes a socket connection to the
server. Then in the OnRead event the data arriving over the network is
put into a buffer which is later processed.
When there are no data arriving the client is idle.
This was easy to do using the TClientSocket component but seems
completely impossible with TIdTCPClient...
|
Someone else may well suggest something better, but one way is to just
create a separate thread for TIdTCPClient and get it to continuously
read data a byte at a time until you have a full "frame", and send it
to the main thread. Don't worry about the fact that you are blocking
for most of the time.
| Quote: | The only way I have found to get data from the TIdTCPClient is to use
its ReadString method. But how can I know when to call ReadString???
There is no OnReceive event to hook it to! If I just call it randomly
then my application locks up inside the method...
|
...because it is not in a separate thread.
| Quote: | And another problem, ReadString has a parameter telling it how many
bytes to read, but that is unknown! How can the client know how many
bytes the server is going to send??? The data are normally packaged
into packets delimited by STX and ETX but these can also appear inside
the contents and are then doubled. There is no other limtation,
packets can be of arbitrary length for example.
|
Build it up a byte at a time.
Ciaran |
|
| Back to top |
|
 |
Don Guest
|
Posted: Sat Feb 04, 2006 4:34 am Post subject: Re: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
| Quote: | This is to me a perfect case of having *event driven* code like this:
|
Rather than trying to bend a blocking socket library to your will, why not
use an asynchronous socket library for the outset? |
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Sat Feb 04, 2006 6:04 am Post subject: Re: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
"Bo Berglund" <bo.berglund (AT) telia (DOT) com> wrote in message
news:jea7u1d0e7phn4il8u9petonmc897u9uvh (AT) 4ax (DOT) com...
| Quote: | I tried to use the TIdTCPClient in my new project instead, but
I have real trouble understanding it.
|
Did you read Indy's documentation yet? Did you look at the demos on Indy's
website?
| Quote: | There is absolutely no knowledge of when data will start streaming
from the equipment, we may even be shutting down the connection
without receiving a single byte.
|
You should use a dedicated thread that continuously reads from the RS323
port, and then forwards any received data to the rest of the application for
processing.
In fact, Indy can help you there. Because its IOHandler system is
pluggable, you can write your own TIdIOHandler descendant class to handle
reading/writing the RS323 port, and then you can use access the RS323 port
using the TCP components like you would with sockets.
| Quote: | SERVER SIDE
------------
The ApdComport component generates an event when data arrives on
the RS232 line. In this event the server will receive the data and send it
to the socket if it is connected at the time.
(using TClientSocket.Socket.SendText)
|
You can use the TIdTCPConnection.WriteBuffer() (Indy 9) or
TIdIOHandler.Write(TIdBytes) (Indy 10) method for that. I would not
recommend sending RS323 data as Text, not even when using TClientSocket and
TServerSocket. Unless you are wrapping the data into your own string
beforehand.
Also, why would you be using a client socket on a server application? That
does not fit with your description below. You would need a server socket
instead in order for the below pieces to operate.
| Quote: | CLIENT SIDE
------------
When the client starts up it establishes a socket connection to the
server. |
That will only work if your use a server socket inside the server
application rather than a client socket.
| Quote: | Then in the OnRead event the data arriving over the network is put into
a buffer which is later processed. When there are no data arriving the
client is idle.
|
Indy is not an asynchronous library. There is no OnRead event for client
components. If you need asynchronous reading for a client component then
you have to do the reading inside a timer or thread (I recommend using a
thread so that the main thread is never blocked). Look at the source code
for Indy's TIdTelnet component for an example of using a reading thread.
For Indy server components, they are multi-threaded internally, and do offer
events for asynchronous operations. Keep in mind that your event handlers
have to contain thread-safe code, and the event handlers can (and usually
should) perform blocking operations.
| Quote: | This was easy to do using the TClientSocket component but seems
completely impossible with TIdTCPClient...
|
It is not impossible. It just has to be implemented differently then you
are used to. In fact, there is a whole chapter in the "Indy in Depth" eBook
(http://www.atozed.com/indy/book/) that explains the difference in Indy's
methodology compared to other vendors' components.
| Quote: | The only way I have found to get data from the TIdTCPClient is to use
its ReadString method.
|
Not true. There is almost a dozen different reading methods available, for
different types of data (integers, strings, streams, buffers, etc).
| Quote: | If I just call it randomly then my application locks up inside the
method... |
As well it should be, because that is the way Indy is disigned to work -
when you perform an operation, it blocks the calling thread until the
operation actually finishes. If you are doing this in the context of the
main thread, then you are going to be blocking the message queue.
| Quote: | ReadString has a parameter telling it how many bytes to read, but
that is unknown!
|
Then obviously cannot use ReadString() in this situation. You will have to
use one of the other reading methods. Since you don't know ahead of time
how many bytes are available, you can use the CurrentReadBuffer() (Indy 9)
or InputBufferAsString() (Indy 10) method to return whatever is actually
available.
CurrentReadBuffer/InputBufferAsString() returns a String, though, which is
not well suited for binary data. You can alternatively call the
ReadFromStack() (Indy 9) or ReadFromSource() (Indy 10) method to fill in the
InputBuffer property, and then use the ReadBuffer() (Indy 9) or ReadBytes()
(Indy 10) method to read however many bytes are currently in the
InputBuffer.
| Quote: | How can the client know how many bytes the server is going to send???
|
The best way is to have the server send the data size prior to the actual
data. That way, the client can read the size first and then know how many
additional bytes to read, if any. That is easily accomplished using the
Read/WriteInteger() and Read/WriteBuffer() methods.
| Quote: | The data are normally packaged into packets delimited by STX and ETX
but these can also appear inside the contents and are then doubled.
|
I have never seen a protocol that allows STX/ETX inside the packet data,
since those bytes are supposed to be used to indicate the start/end of data
(hense their names: STX = "start transmission", ETX = "end transmission").
If they really do not appear inside the packet data, then it is easy to wait
for those bytes to arrive and to process what appears in between them. If
they really do appear inside the packet data, though, then I suggest
replacing them with other bytes, so that the packets never contain them.
Otherwise, send the packet size before each pacekt so that the contents of
the packets is irrelevant.
| Quote: | There is no other limtation, packets can be of arbitrary length for
example. |
That is fine. Indy handles variable-length data fairly well. Although the
preceeding-size approach is the best way to handle such data, when possible.
Gambit |
|
| Back to top |
|
 |
Bo Berglund Guest
|
Posted: Sat Feb 04, 2006 4:02 pm Post subject: Re: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
On Fri, 3 Feb 2006 17:04:05 -0800, "Remy Lebeau \(TeamB\)"
<no.spam (AT) no (DOT) spam.com> wrote:
Thanks for taking your time to respond to this and my other thread!
Appreciated!
/Bo
| Quote: |
"Bo Berglund" <bo.berglund (AT) telia (DOT) com> wrote in message
news:jea7u1d0e7phn4il8u9petonmc897u9uvh (AT) 4ax (DOT) com...
I tried to use the TIdTCPClient in my new project instead, but
I have real trouble understanding it. :-(
Did you read Indy's documentation yet?
Where is the documentation? I just pressed F1 in Delphi7 when I had |
put an Indy component on the form and then tried to follow the help
text. But I did not find the events I expected at all.
| Quote: | Did you look at the demos on Indy's website?
|
Not really, I am using what came with my Delphi7 install.
| Quote: |
There is absolutely no knowledge of when data will start streaming
from the equipment, we may even be shutting down the connection
without receiving a single byte.
You should use a dedicated thread that continuously reads from the RS323
port, and then forwards any received data to the rest of the application for
processing.
|
The part that has the RS232 port would actually be a separate
application that has one single purpose:
Send all received characters from RS232 on to the TCP socket.
It can even be a hardware port server with the same functionality or a
machine tool that will output data at unknown times.
So it is very simply this for the server application case:
In the RS232 OnRxData event I collect all bytes waiting and put them
into the socket using the SendText method. Nothing more or less and
this is the entire application working part. All the rest is
initialization code only.
| Quote: |
In fact, Indy can help you there. Because its IOHandler system is
pluggable, you can write your own TIdIOHandler descendant class to handle
reading/writing the RS323 port, and then you can use access the RS323 port
using the TCP components like you would with sockets.
SERVER SIDE
------------
The ApdComport component generates an event when data arrives on
the RS232 line. In this event the server will receive the data and send it
to the socket if it is connected at the time.
(using TClientSocket.Socket.SendText)
|
Mistyped by me, the socket is not from the TClientSocket, it is the
socket created when the client connected!!!
| Quote: |
You can use the TIdTCPConnection.WriteBuffer() (Indy 9) or
TIdIOHandler.Write(TIdBytes) (Indy 10) method for that. I would not
recommend sending RS323 data as Text, not even when using TClientSocket and
TServerSocket. Unless you are wrapping the data into your own string
beforehand.
|
Why is that? I have all my data in an internal string and I supply
that to the SendText method. I have assumed that the socket has no
assumptions on the data I am sending because these strings may be
binary containing all possible bytes.
| Quote: |
Also, why would you be using a client socket on a server application?
|
You read me literally above where I mistyped the stuff! I have 2
applications, one the server working as I described above and the
other the client, which is the one I am having trouble doing with
Indy. The client has no RS232 port at all, it receives the data via
TCP.
| Quote: | That
does not fit with your description below. You would need a server socket
instead in order for the below pieces to operate.
|
In fact I was simplifying things a bit here, in actual fact I don't
have the server application at all. It is a hardware port server that
has a socket interface to wich my client connects. After connection
the port server will send all incoming RS232 data via the socket to my
client and my client will send control data via the socket that are
then transferred by the hardware device to the RS232 lines.
I just thought that mentioning a Delphi server application would
simplify the discussion..
| Quote: |
CLIENT SIDE
------------
When the client starts up it establishes a socket connection to the
server.
That will only work if your use a server socket inside the server
application rather than a client socket.
Yse, yes, yes, but that is not the problem.... |
| Quote: |
Then in the OnRead event the data arriving over the network is put into
a buffer which is later processed. When there are no data arriving the
client is idle.
Indy is not an asynchronous library. There is no OnRead event for client
components. If you need asynchronous reading for a client component then
you have to do the reading inside a timer or thread (I recommend using a
thread so that the main thread is never blocked). Look at the source code
for Indy's TIdTelnet component for an example of using a reading thread.
|
This is what I feared! So Borland removed the sockets thta supplied
asynchronous handling and put in a synchronous suite (Indy) instead
then??
I have yet to write a thread application, it seems very complex and I
avoid it as much as I can....
| Quote: |
For Indy server components, they are multi-threaded internally, and do offer
events for asynchronous operations. Keep in mind that your event handlers
have to contain thread-safe code, and the event handlers can (and usually
should) perform blocking operations.
This was easy to do using the TClientSocket component but seems
completely impossible with TIdTCPClient...
It is not impossible. It just has to be implemented differently then you
are used to. In fact, there is a whole chapter in the "Indy in Depth" eBook
(http://www.atozed.com/indy/book/) that explains the difference in Indy's
methodology compared to other vendors' components.
The only way I have found to get data from the TIdTCPClient is to use
its ReadString method.
Not true. There is almost a dozen different reading methods available, for
different types of data (integers, strings, streams, buffers, etc).
|
But they are all blocking....
| Quote: |
If I just call it randomly then my application locks up inside the
method...
As well it should be, because that is the way Indy is disigned to work -
when you perform an operation, it blocks the calling thread until the
operation actually finishes. If you are doing this in the context of the
main thread, then you are going to be blocking the message queue.
|
But my whole problem is I cannot know beforehand *when* to call the
method! The data may be there now or in an hour's time or even never.
I cannot know. So I need an event when data do arrive...
If I call a method that never returns then effectively my application
is locked up and cannot even be closed normally (must use Task
Manager, I assume). Not nice at all.
Probably Indy was designed for situations like a web server access
when the client sends a command that will get a reply in a short time,
reads this and disconnects. But that is simply not possible for me.
| Quote: |
ReadString has a parameter telling it how many bytes to read, but
that is unknown!
Then obviously cannot use ReadString() in this situation. You will have to
use one of the other reading methods. Since you don't know ahead of time
how many bytes are available, you can use the CurrentReadBuffer() (Indy 9)
or InputBufferAsString() (Indy 10) method to return whatever is actually
available.
|
OK, so the trick is to create my own event like this then? Maybe in a
timer:
OnTimer:
if CurrentReadBuffer > 0 then
begin
RetrieveSocketData(s);
DoSomethingWithRetrievedData(s);
end;
| Quote: |
CurrentReadBuffer/InputBufferAsString() returns a String, though, which is
not well suited for binary data.
|
Why is that? A string can hold any character including #0, which is
not possible in C of course, but this is ObjectPascal, right?
| Quote: | You can alternatively call the
ReadFromStack() (Indy 9) or ReadFromSource() (Indy 10) method to fill in the
InputBuffer property, and then use the ReadBuffer() (Indy 9) or ReadBytes()
(Indy 10) method to read however many bytes are currently in the
InputBuffer.
How can the client know how many bytes the server is going to send???
The best way is to have the server send the data size prior to the actual
data. That way, the client can read the size first and then know how many
additional bytes to read, if any. That is easily accomplished using the
Read/WriteInteger() and Read/WriteBuffer() methods.
|
Except I am dealing with a hardware port server that just sends
whatever is coming in on the RS232 port....
| Quote: |
The data are normally packaged into packets delimited by STX and ETX
but these can also appear inside the contents and are then doubled.
I have never seen a protocol that allows STX/ETX inside the packet data,
since those bytes are supposed to be used to indicate the start/end of data
(hense their names: STX = "start transmission", ETX = "end transmission").
If they really do not appear inside the packet data, then it is easy to wait
for those bytes to arrive and to process what appears in between them. If
they really do appear inside the packet data, though, then I suggest
replacing them with other bytes, so that the packets never contain them.
Otherwise, send the packet size before each pacekt so that the contents of
the packets is irrelevant.
|
Again an over-simplification on my part for this forum. The protocol
is rather involved but it can send binary data inside the payload and
therefore the STX and ETX characters may well be part of the payload
and we need to ensure they are not mistaken for packet delimiters.
| Quote: | There is no other limtation, packets can be of arbitrary length for
example.
That is fine. Indy handles variable-length data fairly well. Although the
preceeding-size approach is the best way to handle such data, when possible.
Gambit
|
|
|
| Back to top |
|
 |
Bo Berglund Guest
|
Posted: Sat Feb 04, 2006 4:02 pm Post subject: Re: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
On Sat, 4 Feb 2006 12:57:22 -0000, "Martin James"
<mjames_falcon (AT) dial (DOT) pipex.com> wrote:
| Quote: |
The only way I have found to get data from the TIdTCPClient is to use
its ReadString method. But how can I know *when* to call ReadString???
There is no OnReceive event to hook it to! If I just call it randomly
then my application locks up inside the method...
Read Ciaran's post.
|
OK
| Quote: |
And another problem, ReadString has a parameter telling it how many
bytes to read, but that is unknown! How can the client know how many
bytes the server is going to send??? The data are normally packaged
into packets delimited by STX and ETX but these can also appear inside
the contents and are then doubled. There is no other limtation,
packets can be of arbitrary length for example.
Again, Ciaran has a good response. Use a seperate read thread and push all
received bytes one-by-one through a state-machine that handles your
protocol. When a complete, checked Protocol Unit has been received, send it
to the server.
|
Will check.
| Quote: | Incidentally, your protocol seems to have holes, as Rudy says. An
interruption of the serial stream, (eg. cable falls out and is then
replaced), is likely to result in an erroneous protocol unit being sent to
the server, either a truncated single unit or two concatenated partial
units.
|
Yes, and the network cable may also be disconnected or the power may
be switched off at the server side. All will of course potentially
result in packets of data being corrupted. But tha packeting is
designed just for the purpose of being able to detect this and take
corrective actions (or no action).
/Bo |
|
| Back to top |
|
 |
Bo Berglund Guest
|
Posted: Sat Feb 04, 2006 4:02 pm Post subject: Re: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
On Fri, 3 Feb 2006 18:34:43 -0500, "Don"
<blacknapkin (AT) twistandfruge (DOT) com> wrote:
| Quote: | This is to me a perfect case of having *event driven* code like this:
Rather than trying to bend a blocking socket library to your will, why not
use an asynchronous socket library for the outset?
|
But thta is what I *was* doing, only the sockets I used are
depreciated by Borland and I don't want to use stuff that is not going
to have a future. Borland replaced the Client and Server sockets with
Indy....
/Bo |
|
| Back to top |
|
 |
Martin James Guest
|
Posted: Sat Feb 04, 2006 4:02 pm Post subject: Re: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
| Quote: | What I want to accomplish is simply to hook up two applications over
the network from one location to the other. One side will basically be
a port server where an RS232 channel is established with an external
system and the task is to relay *all* of the data sent by the external
system to the listening application.
|
OK
| Quote: | There is absolutely no knowledge of when data will start streaming
from the equipment, we may even be shutting down the connection
without receiving a single byte.
|
OK
| Quote: |
The only way I have found to get data from the TIdTCPClient is to use
its ReadString method. But how can I know *when* to call ReadString???
There is no OnReceive event to hook it to! If I just call it randomly
then my application locks up inside the method...
|
Read Ciaran's post.
| Quote: | And another problem, ReadString has a parameter telling it how many
bytes to read, but that is unknown! How can the client know how many
bytes the server is going to send??? The data are normally packaged
into packets delimited by STX and ETX but these can also appear inside
the contents and are then doubled. There is no other limtation,
packets can be of arbitrary length for example.
|
Again, Ciaran has a good response. Use a seperate read thread and push all
received bytes one-by-one through a state-machine that handles your
protocol. When a complete, checked Protocol Unit has been received, send it
to the server.
Incidentally, your protocol seems to have holes, as Rudy says. An
interruption of the serial stream, (eg. cable falls out and is then
replaced), is likely to result in an erroneous protocol unit being sent to
the server, either a truncated single unit or two concatenated partial
units.
Rgds,
Martin |
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Tue Feb 07, 2006 2:02 am Post subject: Re: How do I use TIdTCPClient in nonblocking mode using D7? |
|
|
Bo Berglund <bo.berglund (AT) telia (DOT) com> wrote:
| Quote: | Where is the documentation?
|
You can download it from Indy's website (http://www.indyproject.org).
| Quote: | The part that has the RS232 port would actually be
a separate application that has one single purpose:
Send all received characters from RS232 on to the
TCP socket.
|
What I said earlier stil applies under that scenerio.
| Quote: | So it is very simply this for the server application
case: In the RS232 OnRxData event I collect all bytes
waiting and put them into the socket using the SendText
method.
|
Again, I do not recomend using SendText() for non-textual data. Especially since SendText() itself is not very good to use anyway in this case. Rather than invoking the overhead of converting the RS232 bytes into a string just to have it turned back into bytes internaly, you should send the original RS232 bytes directly using SendBuf().
Also, whether you use SendBuf() or SendText(), make sure that you pay attention to the return value. If the vlue is less them the specified size, then you have to call the function again to resent any unsent bytes. The best way to handle that is to call the function in a loop until all bytes have been sent.
| Quote: | I have all my data in an internal string and I
supply that to the SendText method.
|
Why? That is not a very good way to manage binary data.
| Quote: | I have assumed that the socket has no assumptions
on the data I am sending because these strings may
be binary containing all possible bytes.
|
The socket itself only operates at the binary level, so why involve the overhead of managing binary data as strings in between?
| Quote: | So Borland removed the sockets thta supplied
asynchronous handling and put in a synchronous
suite (Indy) instead then??
|
TClientSocket and TServerSocket are still available. They are just deprecated, but still work. Borland is not replacing them with Indy. Indy is just a third-party library that is being bundled with the IDE, just like Borland used to bundle the NetMasters components. If anthing, the NetMasters components were the ones being replaced with Indy, not Borland's own components.
| Quote: | But they are all blocking....
|
Everything in Indy is blocking. As I said earlier, Indy is not an asynchronous library. It intentionally uses blocking sockets, and blocking operations. If you really want to use asynchronous components, then go back to using TClientSocket and TServerSocket, or alternatively use a third-party asynchronous library, such as ICS (http://www.overbyte.be).
| Quote: | But my whole problem is I cannot know beforehand
*when* to call the method!
|
You missed the point I was making earlier - if you move the reading into another thread, then it won't matter if the reading blocks. The reading thread will be the only thing being blocked, and it will be unblocked when data actually arrives. For example:
type
TReadingThread = class(TThread)
protected
FConn: TIdTCPConnection;
procedure Execute; override;
public
constructor Create(ACon: TIdTCPConnection); reintroduce;
end;
constructor TReadingThread.Create(ACon: TIdTCPConnection);
begin
FConn := AConn;
inherited Create(False);
end;
procedure TReadingThread.Execute;
begin
while not Terminated do
begin
FConn.ReadFromStack(True, 1000, False);
if FConn.InputBuffer.Size > 0 then
begin
// read bytes from FConn up
// to InputBuffer.Size bytes ...
//
// process bytes as needed ...
end;
end;
end;
var
ReadingThread: TReadingThread = nil;
procedure TForm1.ConnectButtonClick(Sender: TObject);
begin
IdTCPClient.Host = '...';
IdTCPClient1.Port := ...;
IdTCPClient1.Connect(5000);
try
ReadingThread := TReadingThread.Create(IdTCPClient1);
except
IdTPClient1.DisconnectSocket;
raise;
end;
end;
procedure TForm1.DisconnectButtonClick(Sender: TObject);
begin
if ReadingThread <> nil then ReadingThread.Terminate;
try
IdTCPClient1.DiconnectSocket;
finally
if ReadingThread <> nil then
begin
ReadingThread.WaitFor;
FreeAndNil(ReadingThread);
end;
end;
end;
| Quote: | If I call a method that never returns then effectively
my application is locked up and cannot even be closed
normally (must use Task Manager, I assume).
|
That is way you need to use a separate thread for the reading. Then the main thread is not blocked.
| Quote: | Probably Indy was designed for situations like a web
server access when the client sends a command that
will get a reply in a short time, reads this and
disconnects.
|
Indy is designed for all kinds of different situations. Especially for clients that have persistent conection. Indy has a lot more client components than it has server components. Indy works just fine for the situation you have described. You just have to understand how to use it effectively.
| Quote: | But that is simply not possible for me.
|
Yes, it is. See above.
| Quote: | OK, so the trick is to create my own event like
this then?
|
No, not like that. More like this instead:
var
S: String;
begin
S := IdTCPclient1.CurrentReadBuffer;
if Length(S) > 0 then
DoSomethingWithRetrievedData(s);
end;
I do not recommend using Indy with timers. It is technically possible, using short timeouts and such, but using threads are more efficient.
| Quote: | Except I am dealing with a hardware port server
that just sends whatever is coming in on the
RS232 port....
|
Yes, but your server application that is reading the RS232 data is wrapping it in TCP for re-transmission. Which means you are free to add whatever you want to around the RS232 data. You don't have to alter the RS232 data itself in any day, just wrap it with an extra frame that TCP receivers can process effectively. For example:
--- server ---
OnRxData:
for I := 0 to ClientCount-1 do
begin
try
Conn.WriteInteger(ByteCount);
Conn.WriteBuffer(Bytes, ByteCount);
except
// error, do something...
end;
end;
--- client ---
procedure TReadingThread.Execute;
var
nCount: Integer;
begin
while not Terminated do
begin
nCount := FConn.ReadInteger;
// read up to nCount number of
// Bytes, then process as needed ...
end;
end;
Gambit |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|