public int Write(long handle, byte[] data) { // 相手のdata queueに積む if (!dic.ContainsKey(handle)) { return(1); } // deep copyしておかないと送信元で書き換えた場合うまく動かない。 byte[] data2 = new byte[data.Length]; MemoryCopy.MemCopy(data2, 0, data, 0, data.Length); dic[handle].Partner.dic[dic[handle].PartnerHandle].Data.AddLast(data2); return(0); }
/// <summary> /// 指定された接続にパケット種別を付加してデータを送信します。 /// preferCompress == trueの時、データを圧縮してサイズが /// 元のサイズより減少する場合は圧縮データを送信します。 /// </summary> /// <param name="client">送信するTcpClientオブジェクト</param> /// <param name="type">送信データの種別</param> /// <param name="data">送信データ</param> /// <param name="preferCompress">データを圧縮する場合はtrue</param> private static void InnerWrite(TcpClient client, PacketType type, byte[] data, bool preferCompress) { NetworkStream stream = client.GetStream(); // パケット種別を書き込む byte[] datatype = MemoryCopy.ToByteArrayFromInt((int)type); stream.Write(datatype, 0, datatype.Length); if (data != null) { // 送信データの圧縮 byte[] compressedData = null; if (preferCompress) { using (MemoryStream ms = new MemoryStream()) using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress)) { ds.Write(data, 0, data.Length); ds.Close(); // FlushでなくCloseでないといけないらしい。。 compressedData = ms.ToArray(); } } byte[] datasize = new byte[8]; if (compressedData != null && compressedData.Length < data.Length) { // 圧縮データのサイズと展開後のサイズを書き込む MemoryCopy.SetInt(datasize, 0, compressedData.Length); MemoryCopy.SetInt(datasize, 4, data.Length); stream.Write(datasize, 0, datasize.Length); // 圧縮データを書き込む stream.Write(compressedData, 0, compressedData.Length); } else { // 圧縮してもサイズが減少しなかったので元のデータを書き込む MemoryCopy.SetInt(datasize, 0, data.Length); MemoryCopy.SetInt(datasize, 4, data.Length); stream.Write(datasize, 0, datasize.Length); stream.Write(data, 0, data.Length); } } else { // 送信データのサイズを書き込む(値は0) byte[] datasize = MemoryCopy.ToByteArrayFromInt(0); stream.Write(datasize, 0, datasize.Length); } }
/// <summary> /// NetworkIDメッセージを送信します。 /// 送信に失敗した場合は例外が発生します。 /// </summary> /// <param name="handle"></param> /// <param name="client"></param> private void SendNetworkIDMessage(long handle, TcpClient client) { InnerWrite(client, PacketType.NetworkIDNotify, MemoryCopy.ToByteArrayFromLong(_networkID.Value), false); InnerFlush(client); }
/// <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); }