views:

1260

answers:

2

I am trying to conceptually work through a model for a client-server socket application I am writing in c# (both client and server). My server will need to handle many clients at once, and preferably multiple requests from a client at once. I have worked out a container for my communication where I will send a fixed length header at the beginning of each message which will contain (among other things) the length of the message. I have some experience with socket programming in c#, so I am comfortable using Asynchronous sockets.

The main thing I am having trouble with, conceptually, is I need both the client and server to be able to receive messages at any time. The client will establish a connection, and remain "logged in" (like an IM client), and it will need to both receive data at arbitrary times and make requests at arbitrary times. I had also wanted, as part of my protocol, to receive a response to each request made (be it from the server to client, or client to server).

I would like to be able to use a single socket if possible. I believe I could make this work using two sockets, one for making server->client requests and one for client->server requests, but I don't want the extra complications of dealing with two ports/connections. However, using a single socket, I am not sure how to manage sending requests and getting responses when they could be interleaved.

I cannot find any examples of a similar server or client in my searching. Thanks to anyone who offers any ides.

+2  A: 

If you're using TCP, then you will necessarily have to have one socket per client on the server side, as well as the socket to listen for connections. For UDP you can do this all with one socket. Regardless, do the following for either your one UDP socket, or for each TCP client socket:

Have a BeginReceive going at all times and BeginWrite when you the necessary event occurs (ie the user presses a button or a message came in that you need to do something about).

EDIT: In response to your comment, the way I'd handle it would be to include a request ID in your header. Whenever sending a request (from either end) include a unique value for that message. Use a Dictionary<RequestId, RequestState> (with appropriate substitutions for the types) and see if an incoming message relates to a pre-existing request. Furthermore, you could specify that all IDs with the high bit set are requests originating at the client and IDs with the high bit cleared are from the server to avoid collisions:

Server                      Client
Request 0x00 -------------> Starts processing

Starts processing <-------  (unrelated) Request 0x80

Request 0x00 complete <---- Sends response to 0x00

Sends response to 0x80 ---> Request 0x80 complete

This is the system that AOL's OSCAR protocol uses for (possibly slow) stateful requests.

EDIT2: Hm, ok... you really want to block on it? Something you could do is have a separate thread handle the Send/Receive calls. This thread would communicate with the main thread via a thread-safe "send message" queue and a similar "message received" psuedo-queue. The reason I call the latter a psuedo-queue is that you would want the ability to take messages out of order from the queue. The main thread would put a message on the send queue, then block on the receive queue. Each time the socket thread updates the receive queue, the main thread would wake up and check if the message it wants is there yet. If it is, it would take it (out of order) and finish the desired operation. If the message is not there yet, it'd just block on the queue again. When the operation is finally completed it'd just resume normal operation and get messages in order from the receive queue and process them or do whatever else it does.

Does that make any sense? I'm sorry if it's kinda confusing...

Neil Williams
Yes. But assume the server requests something from the client that takes a while. Meanwhile the client asks the server for something. In my server's BeginReceive, how do I know this incoming data is the request from the client, or the response to the server's request? This is what I am struggling with. I have the basics of the async socketing down. Thanks.
Jarrod
Sorry, all the talk of sockets made me think your problem was at that level; how's this update?
Neil Williams
Sorry, I had a hard time framing my question... but this is exactly the kind of thing I have been trying to hash out. I had thought of using request ID's and the dictionary, but wondered how could I implement a method to make a request and block until the result came (and returned it) without using something like a ResetEvent in my request state object. That would bad to create those for each request no? Thanks.
Jarrod
Yeah that makes sense. Thank you, you have been a big help!
Jarrod
+1  A: 

Sockets are bidirectional. You only need one socket to send data back and forth between two endpoints. But on the client you will probably need to have one thread constantly reading from the socket and notifying some component or queuing the data it receives so it gets processed elsewhere, while another thread is sending data through the same socket. This way you have asynchronous communication.

But on the server side you need to open a ServerSocket which will bind to a TCP port and accept connections on that port; every connection you accept is a new Socket. So you have a socket per client, and you will probably need one thread per client.

You need to establish a protocol for the messages you will be passing through the socket. You can either brew your own protocol or look one up, depending on what you want to do. Usually you will first send two or four bytes with the length for the rest of the message, so the other side knows to read that many bytes from the stream; that's a message. The beginning of the message is usually the message type; in a very simple scenario, you can say that a "1" is a client request, a "2" is a server response, a "3" is a client echo, "4" is server echo response, "5" is server echo, "6" is client echo response, etc. That way you receive a message, parse it and then you know it's a request from a client or a response to a message sent from the server, for example.

Chochos