收录日期:2019/10/18 22:23:11 时间:2012-02-16 13:51:08 标签:c#,networking,asynchronous

I've been working with asynchronous sending in C# lately, and just a couple of days ago when I encountered that I needed to send multiple bits of data, i started wondering if the data sent are sent in order.

So my question is, when you send data asynchronously in C#, does it send the data accordingly as it's sent?

Eg. I send an array of data holding 10 bytes, and right after I send it, I send an array holding 5 bytes. Would this mean that the array holding 5 bytes would reach the remote client first (seeing as 5 bytes would most likely take less time to process than 10)?

Here's the code (C#):

    /// <summary>
    /// Sends the welcome screen to the character.
    /// </summary>
    /// <param name="character">The character requesting the welcome screen.</param>
    public void SendWelcomeScreen(Character character)
    {
        SendWindowPane(character, 549);
        SendInterface(character, 1, 549, 2, 378);
        SendInterface(character, 1, 549, 3, 15);
        SendString(character, "Welcome to " + GameEngine.World.Name + ".", 378, 115);
        // TODO: last ip
        SendString(character, "You are connected from: " + character.Session.Connection.IPAddress, 378, 116);
        SendString(character, "0", 378, 39);
        SendString(character, "0", 378, 96);
        SendString(character, "0 unread messages", 378, 37);
        SendString(character, GameEngine.World.Name + " staff will NEVER email you. We use the Message Centre on the website instead.", 378, 38);
        SendString(character, "0 days of member credit", 378, 94);
        SendString(character, "You have 0 days of member credit remaining. Please click here to extend your credit.", 378, 93);
        SendString(character, "You do not have a bank pin. Please visit a bank if you would like one.", 378, 62);
        SendString(character, "You have not yet set any recovery questions.", 378, 56);
        SendString(character, "Message of the Week", 15, 0);
        SendString(character, "Remember to keep your account secure: set a bank PIN, set recover questions and NEVER give away your password.", 15, 4);
    }

    /// <summary>
    /// Sends a window pane to the character's client.
    /// </summary>
    /// <param name="character">The character to send window pane to.</param>
    /// <param name="pane">The pane id.</param>
    public void SendWindowPane(Character character, short pane)
    {
        character.Session.SendPacket(
            new PacketBuilder(239)
            .AppendShort(pane)
            .AppendByteA(0));
    }

    /// <summary>
    /// Sends an interface to the character's client.
    /// </summary>
    /// <param name="character">The character to send interface to.</param>
    /// <param name="showId">The show ids.</param>
    /// <param name="windowId">The window id.</param>
    /// <param name="interfaceId">The interface id.</param>
    /// <param name="childId">The child id.</param>
    public void SendInterface(Character character, byte showId, short windowId, short interfaceId, short childId)
    {
        character.Session.SendPacket(
            new PacketBuilder(93)
            .AppendShort(childId)
            .AppendByteA(showId)
            .AppendShort(windowId)
            .AppendShort(interfaceId));
    }

    /// <summary>
    /// Sends a piece of text to the character's client.
    /// </summary>
    /// <param name="character">The character to send the text to.</param>
    /// <param name="text">The string to be displayed.</param>
    /// <param name="interfaceId">The interface id of which we place this text on.</param>
    /// <param name="childId">The child id of which we place this text on.</param>
    public void SendString(Character character, string text, short interfaceId, short childId)
    {
        int stringSize = text.Length + 5;
        character.Session.SendPacket(
            new PacketBuilder(179)
            .AppendByte((byte)(stringSize / 256))
            .AppendByte((byte)(stringSize % 256))
            .AppendString(text)
            .AppendShort(childId)
            .AppendShort(interfaceId));
    }

The data is guaranteed to be sent in the order posted to the socket. In other words the order in which you call NetworkStream.BeginWrite or Socket.BeginSend. Ultimately what matters is the order in which WSASend is called by the framework for you (or the equivalent socket write done by CLR, I'm not sure if they not use WSAIoctl or even FileWriteEx, since they all work). From MSDN:

The completion routines can be called in any order, not necessarily in the same order the overlapped operations are completed. However, the posted buffers are guaranteed to be sent in the same order they are specified.

If you are using I/O completion ports, be aware that the order of calls made to WSASend is also the order in which the buffers are populated. WSASend should not be called on the same socket simultaneously from different threads, because it can result in an unpredictable buffer order.

So the order is guaranteed both within the scatter/gather buffer order of a single WSASend call and across multiple WSASend calls. The multiple thread invoking WSASend issue is obvious, so I hope is not the case with your code.

However usually things get out of order in the application on the receive side if you post multiple buffers to the socket with WSARecv or Socket.BeginReceive or NetworkStream.BeginRead, it is very easy to find yourself processing them out of order. Make sure this is not the case for you.

That doesn't depend on whether it is async or sync. It depends on channel below: if TCP, order is honored. In UDP is not guaranteed.