//Begins an Asynchronous read from a specific client's network stream. This function handles //reading in header information that defines the size of the object, and then invokes a callback //which will handle reading and deserializing the actual object. public void BeginRead(onReadEventHandler callback, int clientNumber) { if (m_listener != null) { try { //Set a callback to notify the user of a succsesful read, once we've finished with //our part. onReadUserCallback = callback; //ServerReadInfo will hold the id of the client who we're reading from, and an empty //array to read into. ServerReadInfo header = new ServerReadInfo(); header.Id = clientNumber; header.Data = new Byte[4]; //Set reading to true. The caller has to use an if to check if we're reading before //calling this function. Note: Maybe the if can be done in this class? I wasn't sure //it as appropriate to take away that control. m_clients[clientNumber].Reading = true; //Read the data into our header object and send the instance of that object to the //onRead header local callback. m_clients[clientNumber].Stream.BeginRead(header.Data, 0, 4, onReadHeaderLocalCallback, header); } catch (Exception e) { throw e; } } }
//This function is triggered by an event when a header is successfully read. private void onReadHeader(IAsyncResult ar) { try { //Cast the object which we send from "BeginRead" (which is stored in the //IAsyncResult as "AsyncState", which I think is a stupid name because //the object is not a "state" at all)...whatever ServerReadInfo header = (ServerReadInfo)ar.AsyncState; int objectSize = BitConverter.ToInt32(header.Data, 0); //Got the objectSize stored in a new int, and the id is obviously the same, //so we set the byte array in "header" to a new Byte array with the size of //the object so we can basically reuse this object. header.Data = new Byte[objectSize]; //End the current read m_clients[header.Id].Stream.EndRead(ar); //Begin a new Async read now that we know the size of the object. Store it //again in the data field of "header" and send the "header" object to the //final callback. m_clients[header.Id].Stream.BeginRead(header.Data, 0, objectSize, onReadLocalCallback, header); } catch (Exception e) { throw e; } }
//Begins an Asynchronous write to a specific client's network stream. This function handles //writing of header information that defines the size of the object, and then invokes a callback //which will handle serializing and writing the actual object. public void BeginWrite(onWriteEventHandler callback, int clientNumber, object obj) { if (m_listener != null) { try { //I resused the ServerReadInfo class which contains the client's id and a byte array //containing data to be written or read to or from the server. Here we will de- //serialize the object that we want to write. ServerReadInfo writeInfo = new ServerReadInfo(); writeInfo.Id = clientNumber; writeInfo.Data = Serializer.Serializer.SerializeObject(obj); //Store the size of our object in a Byte array. Byte[] array = BitConverter.GetBytes(writeInfo.Data.Length); onWriteUserCallback = callback; //Set writing to true. The caller has to use an if to check if we're writing before //calling this function. m_clients[clientNumber].Writing = true; //Write out the size of the object. Send the actual object to the onWrite header local callback m_clients[clientNumber].Stream.BeginWrite(array, 0, array.Length, onWriteHeaderLocalCallback, writeInfo); } catch (Exception e) { throw e; } } }
private void onWriteHeader(IAsyncResult ar) { try { //We've successfully written the size of the object. Now simply write the object //which was passed to us in the AsyncState. ServerReadInfo writeInfo = (ServerReadInfo)ar.AsyncState; m_clients[writeInfo.Id].Stream.EndWrite(ar); m_clients[writeInfo.Id].Stream.BeginWrite(writeInfo.Data, 0, writeInfo.Data.Length, onWriteLocalCallback, writeInfo.Id); } catch (Exception e) { throw e; } }
private void onRead(IAsyncResult ar) { //WTF: Okay, so this try and catch was falling into the catch and throwing an index //out of bounds exception WHICH IT'S CLEARLY NOT because the function works fine //without the try and catch, and it didn't even after I added the "if" statement //below (labeled by a comment saying "//TEST"), which clearly prevents ANY sort of //out of bounds id. //try //{ //Cast ar.StupidName as our ReadInfo so we can get the idea and object data OnReadEventArgs eA = new OnReadEventArgs(); ServerReadInfo readObject = (ServerReadInfo)ar.AsyncState; int id = readObject.Id; //TEST if (id > m_clients.Count - 1 || id < 0) { return; } //Deserialize the byte array to create an object. We're gonna store the object //in our OnReadEventArgs along with the id to send back to the original caller. eA.Obj = Serializer.Serializer.DeserializeByteArray(readObject.Data); eA.ClientID = id; //Done reading. The position of this assignment is actually very important in //the async world, apparently. m_clients[id].Reading = false; m_clients[id].Stream.EndRead(ar); //Invoke the User's callback onReadUserCallback.Invoke(this, eA); //} /*catch (Exception e) * { * throw e; * }*/ }