コード例 #1
0
ファイル: Network.cs プロジェクト: tamutamu/chaos
        /// <summary>
        /// 指定された接続ハンドルに対応した接続からデータを受信します。
        /// </summary>
        /// <param name="handle">接続ハンドル</param>
        /// <param name="data">受信データが書き込まれるバッファ</param>
        /// <returns>
        ///  0 :正常完了
        /// -1 :ハンドルが無効
        /// -2 :受信データの種別取得に失敗
        /// -3 :受信データのサイズ取得に失敗
        /// -4 :受信データのサイズが最大パケット長を超えている
        /// -5 :実際に受信されたデータのサイズがおかしい
        /// -6 :相手から切断された
        /// -7 :ネットワークIDが違うため切断した
        /// -99:通信エラー
        /// </returns>
        /// <exception cref="ArgumentException">指定されたハンドルは使用されていません</exception>
        public int Read(long handle, out byte[] data)
        {
            data = null;
            TcpClient client = GetTcpClient(handle);

            if (client == null)
            {
                return(-1);
            }
            ClientInfo info = _clientInfoMap[handle];

            try
            {
                NetworkStream stream = client.GetStream();
                if (!stream.DataAvailable)
                {
                    //  受信データが無いときに活性チェック(=自身の生存通知)を行なう
                    if (KeepAliveSendInterval > 0 && ++info.ActivityCheckCounter >= KeepAliveSendInterval)
                    {
                        info.ActivityCheckCounter = 0;
                        try
                        {
                            InnerWrite(client, PacketType.KeepAliveNotify, null, false);
                            InnerFlush(client);
                        }
                        catch
                        {
                            //  通信障害と思われる
                            InnerDisconnect(handle, false);
                            return(-6);
                        }
                    }
                    return(0);
                }

                byte[] buf = new byte[4];

                //  データ種別の取得
                if (stream.Read(buf, 0, 4) != 4)
                {
                    Disconnect(handle);
                    return(-2);
                }
                int type = MemoryCopy.GetInt(buf, 0);

                //  データサイズの取得
                if (stream.Read(buf, 0, 4) != 4)
                {
                    Disconnect(handle);
                    return(-3);
                }
                int size = MemoryCopy.GetInt(buf, 0);

                //  最大パケット長を超えていないかチェック
                if (_maxPacketSize > 0 && size > _maxPacketSize)
                {
                    Disconnect(handle);
                    return(-4);
                }

                //  データの取得
                int    originalSize = 0;
                byte[] buffer       = null;
                if (size > 0)
                {
                    //  展開後データサイズの取得
                    if (stream.Read(buf, 0, 4) != 4)
                    {
                        Disconnect(handle);
                        return(-3);
                    }
                    originalSize = MemoryCopy.GetInt(buf, 0);

                    //  圧縮データの取得
                    buffer = new byte[size];
                    int remain = size;
                    int readed = 0;
                    while (remain != 0)
                    {
                        int len = stream.Read(buffer, readed, remain);
                        readed += len;
                        remain -= len;
                        if (len == 0)
                        {
                            break;
                        }
                    }
                    if (readed != size)
                    {
                        //  中途半端にしか読めていない
                        Disconnect(handle); //  通信エラーの可能性もあるので通知を送らない方がよいのかどうかは難しいところ。
                        return(-5);
                    }
                }

                //  データ種別に応じたディスパッチ
                switch (type)
                {
                case (int)PacketType.KeepAliveNotify:
                    //  生存通知
                    return(0);

                case (int)PacketType.DisconnectNotify:
                    //  切断通知
                    InnerDisconnect(handle, false); //  相手はもういないのだから通知する必要はない
                    return(-6);

                case (int)PacketType.NormalMessage:
                    //  通常データ
                    if (NetworkID != null)
                    {
                        //  認証済みでないならデータを捨てて切断
                        if (!_clientInfoMap[handle].IsReceiveNetworkID)
                        {
                            //  ネットワークIDが違うので切断する
                            Disconnect(handle);
                            return(-7);
                        }
                    }
                    if (size > 0 && originalSize != size)
                    {
                        //  展開が必要
                        byte[] tmp = new byte[originalSize];
                        Deflate(buffer, tmp);
                        data = tmp;
                    }
                    else
                    {
                        //  圧縮されていない
                        data = buffer;
                    }
                    return(0);

                case (int)PacketType.NetworkIDNotify:
                    if (NetworkID != null)
                    {
                        long senderNetworkID = MemoryCopy.GetLong(buffer, 0);
                        if (NetworkID == senderNetworkID)
                        {
                            //  認証済みとしてマーク
                            _clientInfoMap[handle].IsReceiveNetworkID = true;
                        }
                        else
                        {
                            //  ネットワークIDが違うので切断する
                            Disconnect(handle);
                            return(-7);
                        }
                    }
                    return(0);
                }
            }
            catch
            {
                InnerDisconnect(handle, false);
                return(-99);
            }
            return(0);
        }