internal static void ReadCallback(IAsyncResult ar)
        {
            BaseClientStateObject sStateObject = (BaseClientStateObject)ar.AsyncState;

            int nRead = 0;

            try
            {
                nRead = sStateObject.ClientSocket.EndSend(ar);
                if (nRead == 0)
                {
                    sStateObject.Disconnect();
                    return;
                }
                switch (sStateObject.rMode)
                {
                    case ReadMode.Header:
                        sStateObject.headerIndex += nRead;

                        if (sStateObject.headerIndex != sStateObject.headerBuffer.Length)
                        {
                            sStateObject.ClientSocket.BeginReceive(
                                    sStateObject.headerBuffer,
                                    sStateObject.headerIndex,
                                    sStateObject.headerBuffer.Length - sStateObject.headerIndex,
                                    SocketFlags.None,
                                    new AsyncCallback(ReadCallback), sStateObject
                                );

                            return;
                        }

                        sStateObject.pHeader = MarshalExtensions.ByteArrayToStructure<PacketHeader>(sStateObject.headerBuffer);
                        sStateObject.headerBuffer = new byte[Marshal.SizeOf(typeof(PacketHeader))];
                        sStateObject.headerIndex = 0;

                        if (sStateObject.pHeader.DataLength > 0)
                        {
                            if (sStateObject.pHeader.DataLength > MaxAllowedDataSize)
                            {
                                sStateObject.Disconnect();
                                return;
                            }
                            sStateObject.rMode = ReadMode.Data;
                            sStateObject.dataIndex = 0;
                            sStateObject.dataBuffer = new byte[sStateObject.pHeader.DataLength];
                            sStateObject.ClientSocket.BeginReceive(
                                    sStateObject.dataBuffer,
                                    sStateObject.dataIndex,
                                    sStateObject.dataBuffer.Length - sStateObject.dataIndex,
                                    SocketFlags.None,
                                    new AsyncCallback(ReadCallback), sStateObject
                                );
                            return;
                        }

                        break;

                    case ReadMode.Data:
                        sStateObject.dataIndex += nRead;

                        if (sStateObject.dataIndex != sStateObject.dataBuffer.Length)
                        {
                            sStateObject.ClientSocket.BeginReceive(
                                    sStateObject.dataBuffer,
                                    sStateObject.dataIndex,
                                    sStateObject.dataBuffer.Length - sStateObject.dataIndex,
                                    SocketFlags.None,
                                    new AsyncCallback(ReadCallback), sStateObject
                                );

                            return;
                        }

                        BytePacket bPacket = new BytePacket();
                        bPacket.FromExisting(sStateObject.pHeader, sStateObject.dataBuffer);

                        if (sStateObject.OnPacketReceived[bPacket.Header.PacketType] != null)
                        {
                            Thread tOnPacketReceived = new Thread(
                                () => {
                                    try
                                    {
                                        sStateObject.OnPacketReceived[bPacket.Header.PacketType](sStateObject, bPacket);
                                    }
                                    catch { sStateObject.Disconnect(); }
                                });

                            tOnPacketReceived.Start();
                        }

                        sStateObject.rMode = ReadMode.Header;
                        sStateObject.ClientSocket.BeginReceive(
                            sStateObject.headerBuffer,
                            sStateObject.headerIndex,
                            sStateObject.headerBuffer.Length - sStateObject.headerIndex,
                            SocketFlags.None,
                            new AsyncCallback(ReadCallback), sStateObject
                        );

                        break;
                }
            }
            catch
            {
                sStateObject.Disconnect();
                return;
            }
        }