Re: server scenario - variables in the right spot?
- From: "Peter Duniho" <NpOeStPeAdM@xxxxxxxxxxxxxxxx>
- Date: Fri, 08 Jun 2007 11:45:08 -0700
On Fri, 08 Jun 2007 08:42:40 -0700, David <nospam@xxxxxxxxxx> wrote:
[...]
- ReceiveCallback calling beginReceive again: I do this but not exactly how
you illustrate, I first check if there is more data to receive and only do
it then. But I now understand what you are saying about always being ready
to receive (calling beginReceive immediately inside the ReceiveCallback).
This seems like an important modification I need to consider. But it has a
domino effect on my whole structure, of course.
Assuming you are using the same receive callback for each BeginReceive(), then I don't see what the difference is. If you are using different receive callbacks depending on the state of your connection, then yes...I can see that you would have a hard time posting a new receive before you've processed the current one. But then, I'd suggest that's not a good design anyway.
If i'm understanding this
correctly (a big 'if' hehe), then I would need to change my 'protocol' by
embedding 'control' information in order to keep things in order.
This I definitely don't feel is true. How you receive the data should not affect what is in the protocol at all. If you are currently using different receive callbacks depending on where you are in the protocol "conversation", then yes...that would have to change. But I think it should anyway. Other than that, nothing else would need to change.
If you are using different receive callbacks depending on the state of the conversation, then you are essentially maintaining that state in code. IMHO, it is better to be data-driven, and to use your data structures to maintain the state. If you do that, then you can use a single receive callback to process all inbound data. What that callback does at any given moment would depend on the state of your data structures, and it would never be a problem to have a receive posted and ready to process incoming data.
For
example: currently, i'm just sending back and forth the 'commands',
'parameters' needed by those commands, and the 'results' of running those
commands. The 'control' of this happening is within my programs sequential
execution, whereas what I envision what you are saying is more or less a
perpetual sending/receiving motion where within the data received would lie
what it is, what to do with it, where you are in a multi-step process etc...
Having a sequential process that you expect your application protocol to follow doesn't rule out using a single receive callback, and always posting a new receive as soon as you start processing the current one. The sequence of commands should be tracked in your data structures, and by doing so you can use the same code to process any command.
to try to illustrate (what I'm doing now) (just the connection-specific
stuff, client-server communication, assume this is CommandA which has a few
steps, assume everywhere I say 'wait' i mean using an autoResetEvent, all
sends and receives use the async begin*)
- client sends commandA and waits for confirmation from server of its
receipt
The client can call BeginReceive() as soon as its connected (BeginConnect() completes). There is no need to delay that until after sending "commandA". The posted receive won't be completed until after the command has been sent and replied to, of course, but there's no harm in being ready to receive beforehand.
- server receives command and after verifying it can work with it, send back
confirmation of it's receipt and waits for next part
The server can call BeginReceive() as soon as it's connected (BeginAccept() completes). This is perhaps more obvious since in your application protocol it appears that the client initiates the communications. But even if the server started things, it could still call BeginReceive() as soon as it's connected.
As the very first thing in the receive callback, it would call another BeginReceive(). Assuming it's already received all the data the client sent (including the data currently being processed), that receive wouldn't complete until after the server gets a chance to reply ("confirmation of its receipt"), but it's not harmful to have the receive posted and waiting.
- client receives confirmation of command's receipt from server so *now
knows* it can send, lets just say, part2 of commandA, it sends and waits for
confirmation
In the client data structure, it should of course keep track of where in the conversation it is. If the next thing to do is wait for confirmation of the receipt of "commandA" and then send "part2" of "commandA", then the data structure should reflect that somehow. Then when it completes another receive, it knows what to do with that.
- server receives part2 of commandA and sends back confirmation of receipt
and begins executing commandA
- client receives server's confirmation of receipt of part2 of commandA and
waits for results
- server finishes executing and send results back to client
- client receives results
As above, assuming the state of the conversation is maintained in each data structure (client and server), then the client and server simple take the appropriate action according to the state of the conversation. Having an extra receive (or several) posted isn't a problem.
You can have as many "parts" or "commands" or whatever as you like. It doesn't matter...you can still keep track of the sequence in data and then the code requires just a single receive callback that does particular things based on the state of the conversation.
Note that nothing about the above requires a change to the application protocol itself. Just the implementation of the code that handles the protocol.
assume there are some commands that may have more than 2 parts (trying to
keep example short). The 'control' in this case is my code on both sides
sequentially going through those steps. Now, with this perpetual
send/receive machine (that sounds cool) I'm envisioning the 'control' having
to be within the data sent so that it would be something like:
- server receives data, breaks it down per the protocol determining that it
is part2 of commandA already in progress, and proceeds appropriately. Like a
big switch statement based on the 'command' part of the protocol, then
within that case, potentially another switch statement or other control
structure for the particular 'part' of the command or where in the commands
total process the data belongs. This way there is no waithandles and control
is removed from the thread where it was and placed in the protocol, allowing
this 'perpetual machine' to run.
does this sound like I'm getting it?
Almost. The main problem in the above is that you are assuming that you need to change the application protocol so that it includes the "state" information within it, when in fact the state information can be kept locally in data structures.
If you want a protocol where various commands can be sent at random times, then of course you would need some sort of data within the protocol to tell you what command you're dealing with at any given time. But if the protocol is strictly a sequential conversation, that state can be easily maintained within the client and server data structures, rather than being sent explicitly over the network.
Now, all that said, a couple of other wrinkles you may not be aware of:
* Any given receive may result in any number of bytes between 1 and the total number of bytes sent but not yet received. This means that in your receive callback (whether you have many or just one), you may or may not receive enough data to complete a "command". You need to include logic to keep track of what command you're working on, and how much of it you've received so far, so that as you receive new data you can append it to the command you're working on currently, and correctly detect when you've actually received enough data to process (that is, the "command" has been completed).
* Perhaps more confusingly, while it is not a problem to have multiple receives posted via BeginReceive(), you do need to keep in mind that the callbacks can be executed in any order for any receive that has completed, due to threading issues. The buffers will be filled in the order in which you post them; that much is guaranteed. However, it is possible to have a BeginReceive() that you posted second, execute its callback first. That is, you would wind up running the code in the callback that actually processes the data for that buffer before the code in the same callback that processes the data for the first buffer.
In the second issue, the usual implementation for dealing with it would be to maintain a list of the buffers you've posted for receiving, flagging them as completed when you get the callback, and treating the list as a queue for the purpose of processing the data, locking it before processing anything, and only processing data from the beginning up to but not including the first one that has _not_ been marked as completed. Doing so will result in a buffer being marked as completed in one callback thread, but possibly being actually processed in a different one. For example, suppose you have two buffers posted for a receive, and the callback for the second buffer is actually called first:
The callback code:
lock the queue
mark buffer as received
while the first buffer in the queue exists and is marked received
{
process the buffer
}
unlock the queue
What each thread does:
Buffer 1 Buffer 2
-------- --------
lock the queue
mark the buffer as received
first buffer (Buffer 1) in the queue isn't marked yet, so loop doesn't execute
unlock the queue
lock the queue
mark the buffer as received
first buffer (Buffer 1) in the queue is now marked, so process it
second buffer (Buffer 2) in the queue is also marked, so process it as well
no more buffers, so exit loop
unlock the queue
The first issue above is mandatory. You absolutely have to deal with it. The second is not, and if you find it too confusing, that's a good reason to _not_ post a new receive as the very first thing in the receive callback, and rather to post it after you've finished processing the current buffer.
Note that you still don't need event handles; the only difference is where in the processing of the current receive event do you wind up posting a new buffer for receiving. Doing it first is more efficient, but more complicated. For simplicity's sake you may prefer to do it last, after you're done processing the current data. Note that doing so doesn't change _whether_ you post a new receive (ie you will always post a new receive regardless), nor does it change _how_ you post that new receive (ie you will always post the same size buffer, or perhaps even the same exact buffer in this case, you posted before). It only changes _where_ you post the new receive.
well, either way, I appreciate your help. Sorry my posts are so long but I
don't know any other way to get out my thoughts/questions. I realize what I
really should do is go get a good book specifically on async network
programming for .net.
I don't know if there is one. You may find Winsock books useful, but even there I'm not aware of any that are considered _great_. "Network Programming for Microsoft Windows, Second Edition" by Ohlund and Jones, as well as "Windows Sockets Network Programming (Addison-Wesley Advanced Windows Series)" by Quinn and Shute may be useful to you, but they aren't specific to .NET and in fact the topics that would be most applicable to ..NET would be those regarding i/o completion ports, and frankly because ..NET simplifies the use of i/o completion ports so much (in a good way), I'm not convinced that learning all the intracacies of doing them in Winsock is necessary.
Pete
.
- Follow-Ups:
- References:
- server scenario - variables in the right spot?
- From: David
- Re: server scenario - variables in the right spot?
- From: Peter Duniho
- Re: server scenario - variables in the right spot?
- From: David
- Re: server scenario - variables in the right spot?
- From: Peter Duniho
- Re: server scenario - variables in the right spot?
- From: David
- server scenario - variables in the right spot?
- Prev by Date: Re: How do I pass beyond cave 7 of digger .net ?
- Next by Date: Re: Launch devenv.exe and open a web site?
- Previous by thread: Re: server scenario - variables in the right spot?
- Next by thread: Re: server scenario - variables in the right spot?
- Index(es):
Relevant Pages
|