Wednesday 15 September 2010

.net - SocketAsyncEventArgs fragment order -



.net - SocketAsyncEventArgs fragment order -

i having issue receive socket packet ordering using socketasynceventargs. crux of problem that when client sends packet server, server receive packet in non-standard sized fragments , processed in random order. means packet cannot decoded app screws entire conversation.

so example, client send total packet using socket.networkstream.write() method:

[-------packet-------]

the server, using socketasynceventargs, receive async callbacks on 2 separate packets lastly chunk of packet processed first:

first packet: et-------] sec packet: [-----pack--

this not happen on packets , haven't been able accurately reproduce based on packet size or timing. implement send/ack comms protocols client won't send packet until server acks lastly packet received can't i'm overloading server.

the frustrating part socket.available on server always 0 according docs means nil available read

if using non-blocking socket, available way determine whether info queued reading, before calling receive. available info total amount of info queued in network buffer reading. if no info queued in network buffer, available returns 0.

with available @ zero, socketeventargs.count doesn't seem provide of value , offset has receive buffer , not based on actual info stream i'm not sure how can set these fragments in order.

my guess issue async callback first part of packet gets preempted sec callback processes , goes first piece. problem can't synclock whole callback (wish .net has synchronized functions java). , if did, seems negate benefits of async callback in first place.

what doing wrong makes these come in wrong order or can create them processed correctly?

i not quite sure doing of statements. write using socketasynceventargs trying deal weird api stuff .count or .available. if socket type tcp doing wrong, because packets in right order. may fragmented 1 byte sized chunks, order right. that's pretty much tcp about.

as you've provided no code , based on statement, guess it's best provide ssce started.

the sample in c# should apply vb.net. check comments in code see received info @ properly. implementation write received info console , send client. echo servers create great samples!

class="lang-cs prettyprint-override">using system; using system.io; using system.net; using system.net.sockets; using system.text; using system.threading.tasks; namespace saeasample { public class programme { static void main() { var server = new server(new ipendpoint(ipaddress.any, 12345)); // ugly sample clients parallel.for(0, 4, => { using (var client = new tcpclient("localhost", 12345)) using (var stream = client.getstream()) using (var author = new binarywriter(stream)) using (var reader = new binaryreader(stream)) { var text = "hello async-server!"; var message = encoding.utf8.getbytes(text); console.writeline("s: {0}: {1}", i, text); writer.write(message); var roundtrip = reader.readbytes(message.length); console.writeline("r: {0}: {1}", i, encoding.utf8.getstring(roundtrip)); } }); console.readline(); } } public class server { private const int readbuffersize = 8192; private const int sendbuffersize = readbuffersize; // have fixed number of clients instead of // pooling sake of beingness illustration private const int maxclients = 4; private const int maxqueue = 10; private readonly byte[] buffer = new byte[maxclients * (readbuffersize + sendbuffersize)]; private readonly socket socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); public server(ipendpoint localendpoint) { socket.bind(localendpoint); socket.listen(maxqueue); (int = 0; < maxclients; i++) { var client = new usertoken(i); client.recvargs.completed += completed; client.sendargs.completed += completed; console.writeline("accepting on client slot {0}", client.slot); if (!socket.acceptasync(client.recvargs)) { completed(this, client.recvargs); } } } private void completed(object sender, socketasynceventargs e) { var client = (usertoken)e.usertoken; // socket operation had success if (e.socketerror == socketerror.success) { // new client connected if (e.lastoperation == socketasyncoperation.accept) { onaccept(client); } // either send or received worked else if (e.bytestransferred > 0) { if (e.lastoperation == socketasyncoperation.receive) { onreceived(client); } else if (e.lastoperation == socketasyncoperation.send) { onsend(client); } // should never happen, handle gracefully else { onother(client); } } // don't handle else else { onother(client); } } // socket error occured else { onother(client); } } private void onaccept(usertoken client) { console.writeline("client slot {0} connected client {1}", client.slot, client.recvargs.acceptsocket.remoteendpoint); // 1 time accepted, start receiving client.recvargs.setbuffer(buffer, client.slot * (readbuffersize + sendbuffersize), readbuffersize); if (!client.recvargs.acceptsocket.receiveasync(client.recvargs)) { completed(this, client.recvargs); } } private void onreceived(usertoken client) { // echo whatever got var builder = new stringbuilder(); // here of import part (int = 0; < client.recvargs.bytestransferred; i++) { // offset buffer , echo in hex builder.append(client.recvargs.buffer[client.slot * (readbuffersize + sendbuffersize) + i].tostring("x2")); } console.writeline("received {0} bytes client slot {1}: {2}", client.recvargs.bytestransferred, client.slot, builder.tostring()); // send info ... echo server after client.sendargs.setbuffer(client.recvargs.buffer, client.slot * (readbuffersize + sendbuffersize) + readbuffersize, client.recvargs.bytestransferred); buffer.blockcopy(client.recvargs.buffer, client.recvargs.offset, client.sendargs.buffer, client.sendargs.offset, client.recvargs.bytestransferred); if (!client.recvargs.acceptsocket.sendasync(client.sendargs)) { completed(this, client.sendargs); } } private void onsend(usertoken client) { console.writeline("sent {0} bytes client slot {1}", client.sendargs.bytestransferred, client.slot); // start receiving 1 time again if (!client.recvargs.acceptsocket.receiveasync(client.recvargs)) { completed(this, client.recvargs); } } private void onother(usertoken client) { console.writeline("disconnecting client slot {0}", client.slot); // close connection , take 1 time again client.recvargs.setbuffer(null, 0, 0); if (client.recvargs.acceptsocket != null) { client.recvargs.acceptsocket.dispose(); client.recvargs.acceptsocket = null; } console.writeline("accepting on client slot {0}", client.slot); if (!socket.acceptasync(client.recvargs)) { completed(this, client.recvargs); } } } public class usertoken { public readonly int slot; public readonly socketasynceventargs recvargs = new socketasynceventargs(); public readonly socketasynceventargs sendargs = new socketasynceventargs(); public usertoken(int slot) { slot = slot; recvargs.usertoken = this; sendargs.usertoken = this; } } }

also note because code asynchronous console outputs may or may not in order. can decrease read , write buffer size constants way downwards 8192 1. packets sent byte byte in both directions, still in order.

for more in depth explainations msdn starting point.

.net sockets networking network-programming socketasynceventargs

No comments:

Post a Comment