It currently has threadsafe methods of writing packets from outside the thread. I am basically looking for more ideas and suggestions.

What I really need is a good way to terminate the connection. Should I just .free the reader thread and indy client or is there a better way?

PS, I don't like just links to commonly known sites. I like examples and detailed suggestions.

I will be awarding points appropriately. Some points are owed to a contributor in "Part 1", but I will still give to others who contribute. Soon as there is enough useful information in this topic, I will make a "Part 3" and continue on from there.

String variables can contain nulls (#0 characters as well) the only thing you have to take care of is when using string functions that expect a PChar with these strings. Now I used strings so I could just append the ip-buffer to it and then check if a complete message is in it by validating its length with DataLength. There is no wait state involved then. If you do a Read(DataLength, 2) the thread will wait infinitely for those two bytes even if there are lots of outgoing messages waiting on the queue. Further more strings are automatically allocated/deallocated (and reference counted, and copied on writing) and the fixed size record you propose is always 64KB big (but you use only one so that OK).

What we could do is put the receivced string with a complete record into FPacket. That would look like this:

I also added some stability by discarding anything that doesn't start with $FF. This way when illegal messages are received the ReaderThread will automatically resync.

>>I am only going to have the thread handle the packets so I will be modifying it so that it will ignore packets that don't have
>>a "process_" method. I think this would be safe enough to use a single FPacket data structure instead of passing it to
>>the methods...

>>Another problem I am having is that I want to display some messages in TMemo's, guess that is what syncronize is for.

If the packet contains information that need some handling (displaying, updating lists) in the client you can use Synchronize and then read the information fro FPacket in the thread. The action taken by the main thread will have to be swift because it will block any further reading/writing by the thread during the action. (That is why I used the QueueIn). Another thing with synchronize is that it will only be handled when the main thread is idle.

Synchronize works like this:
- it puts a message on the main threads message queue (this message contains the method to be called)
- the thread starts waiting for this message to be handled
- the main thread handles messages and idles after each message
- when the synchronize message come it calls the method in the message and idles again
- then the thread will continue its rounds

I always try to never mix thread things with UI things, and I always stay away from Synchronize. If, for example, you do a ShowMessage in the synchonized method the thread can be blocked for a few minutes and not respond to server PINGs. That is why I implemented a timer and let the UI grab anything for it.

Now one pitfall is when you use fClient.Read(....) because there the thread will wait for IP input infinitly. That is why I used ReadLn with a timeout in the past and this time I found an even better way of reading using the ReadFromStack/Extract where there is no wait.

Anyway, I don't have a need for any other process to read the packets outside the thread so I took out the critical section for the reading part.

Basically what I want to do is that if I don't have a process for it, to call process_other which will display a packet dump of the data to the screen. I have some code to this already (though a little buggy).

Would put Name = SID_Something if there is a const for it, but no process handler. Anyway, no real question involved here, just explaining my next goal so you have a better understanding what I am doing.

Heres a couple of questions...

If I call each process_xxx, do I have to pass parameters for any particular reason? I figured I would just use the fData and fDataLength and stuff instead. Calling these process procedures holds up the rest of the thread anyway right?

Also, I've been messing with something in the past unsuccessfully. Can I use something like a TMemoryStream on memory that has already been allocated without writing my own TCustomStream? I suppose I could make fData a TMemoryStream itlsef and work with it from there, but would it be safe to treat String(fData.memory) as a string?

Actually, if there was a component that allowed using an existing pointer/string, to be allowed to do things like ReadInt, ReadByte, ReadBoolean, etc, it would be extremely useful for this. I've written my own one in the past for a customized TCP client I made about 5 years ago that I could dig up I guess if I can find it. Any thoughts on this? Basically in each process_XXX I'd like to ideally just do something like this...

>>Ok. So what if I want to disconnect from the client side manually instead of waiting for the server to disconnect? Do I just use MyReaderThread.free?

You could. Free while call Terminate (set fTerminate to True) Execute will break from the while loop (in a short time if you use the ReadFromStack) and if you want you can that call fClient.Disconnect before exiting the Execute method (would be clean).

The thing is, this scheme will not work if you wait in any of the ReadXXX functions (except ReadFromStack).

>>If I call each process_xxx, do I have to pass parameters for any particular reason? I figured I would just use the fData and fDataLength and stuff instead. Calling these process procedures holds up the rest of the thread anyway right?

If you use private packet members you do not need to pass anything. And yep, calling the procedures hold up the thread.

>>Also, I've been messing with something in the past unsuccessfully. Can I use something like a TMemoryStream on memory that has already been allocated without writing my own TCustomStream? I suppose I could make fData a TMemoryStream itlsef and work with it from there, but would it be safe to treat String(fData.memory) as a string?

What should work is the following:

Add this empty decalration:

type
TAllocatedMemoryStream = class(TMemoryStream);

With it you can call protected functions so in you code you can create and use an ordinary TMemoryStream and assign it you memory just by using a typecast:

...
TAllocatedMemoryStream(MS).SetPointer(@fData[0], 65536);
...

I am not sure about the [0] maybe leave it away. And you have to make sure you don't call Write, change the Size or anything, just read.

I still think it would be easier to have fData as a string, you could use is as a stream as simple as a call to TStringStream.Create(fData).

>>Actually, if there was a component that allowed using an existing pointer/string, to be allowed to do things like ReadInt, ReadByte, ReadBoolean, etc, it would be extremely useful for this. I've written my own one in the past for a customized TCP client I made about 5 years ago that I could dig up I guess if I can find it. Any thoughts on this? Basically in each process_XXX I'd like to ideally just do something like this...

When I can determine the format myself I always use TReader/TWriter from the classes unit for that purpose. You can hook them to a stream and read all kinds of data types from it.

You can easily make a descendant of TStringStream that supports reading Integers/Bytes from it:

I suppose I could just use it as a string. Just going to require a little reconditioning on my part. I'm still in the mentality of getting the length of a string from checking astring[0] and stuff from back in turbo pascal. Never really developed my string manipulating skills, but now isn't the worst time to force myself into it.

Actually, what I am thinking of is making a couple of helper functions that do something like...

As far as the packet dump thing, I got that pretty much figured out with the help of a couple other fellows. Nothing I coudn't do on my own, just like to see how others do things and very frequently find better ways of doing things. The second guy gave me the idea of converting to a string instead of directly to a memo file which would let me reused the doMessage procedure. Anyway, enough with that. :)

This way the thread will first be terminated and then you resources will be freed.

I have no server so for me it stuck at the Connect with does much longer that 500 milliseconds...

I have IdSocketError added in Tools\Debugger Options\Language Exceptions to ignore it. This measure might be to extreme for you but Indy developers indicate in the sources to at least add EIdSilentException to the ignored ones.

Anyway, I am going to close this topic soon and give ya these points and start a new topic again.

So far things seem to work fine, but like to implement a reconnection timer or somehow informing the main form that one of the clients disconnected.

Also, I need to slow down the outgoing packets some how so that it transmits packets at a delay between them. The server I am connecting to has flood control that IP bans the client if it detects spam. Maybe some way of guaging the chars per second for the last 10 seconds and adjust the delay between packets accordingly or something. Is this something I would use a timer for and have the timer insert a string into the queue on an interval?

Anyway, I'm just babbling. It's 4am right now and I should be in bed. I always lose track of time when I am writing a program. Small flaw in me I guess.

You could for example add an extra state to the state machine csConnectionTimeOut which just sleeps a timeout and goes to csDisconnected again.

>>Also, I need to slow down the outgoing packets some how so that it transmits packets at a delay between them.

Maybe you can use one of Indy IOHandlers which does a speed limit: TIdIOHandlerThrottle. You can limit the speed to a number of bytes/bits per second for a connection. You could create one in your thread. I haven't experimented with them but I think you can just connect is to the TIdTCPClient.IOHandler before opening it.

>>Anyway, I am going to close this topic soon and give ya these points and start a new topic again.

Thanks! I think this one will push me over the 100.000 points overall. Do they still give out T-Shirts?

>>Anyway, I'm just babbling. It's 4am right now and I should be in bed. I always lose track of time when I am writing a program. Small flaw in me I guess.

Featured Post

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

The uses clause is one of those things that just tends to grow and grow.
Most of the time this is in the main form, as it's from this form that all others are called.
If you have a big application (including many forms),
the uses clause in the in…

This article explains how to create forms/units independent of other forms/units object names in a delphi project.
Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…