Exemple #1
0
        /// <summary>
        /// 解读一条用户连接消息。
        /// </summary>
        /// <param name="message">待解读的消息。</param>
        /// <param name="infoHash">解读出的 InfoHash。</param>
        /// <param name="bitTorrentClientPort">解读出的用户监听端口。</param>
        /// <exception cref="System.ArgumentException">解读的不是用户连接消息时发生。</exception>
        public static void GetPeerMessageContent(KMessage message, out InfoHash infoHash, out ushort bitTorrentClientPort)
        {
            //if (message.Content.Data.Length != 22)
            //{
            //    throw new ArgumentException("要解读的不是一条用户连接消息。");
            //}
            //infoHash = InfoHash.FromByteArray(message.Content.Data.Take(20).ToArray());
            //bitTorrentClientPort = BitConverter.ToUInt16(message.Content.Data, 20);
            BEncodedDictionary dictionary;

            try
            {
                dictionary           = BEncodedValue.Decode <BEncodedDictionary>(message.Content.Data);
                infoHash             = InfoHash.FromByteArray((dictionary["infohash"] as BEncodedString).TextBytes);
                bitTorrentClientPort = (ushort)((dictionary["bt client port"] as BEncodedNumber).Number);
            }
            catch (Exception)
            {
                throw new ArgumentException("附带数据不是用户连接消息数据。");
            }
            // 这里遇到了一个奇怪的问题,就是编解码的时候
#warning 系统的编解码和网络的编解码字节序可能不同!
            // BitConverter 的编解码和网络的编解码顺序可能不同
            // 但是奇怪的是,即使这样,KMessage 还能正常解码,地址大多数是正常的,但是端口大多数是不正常的
            // 即使是端口,也只是低2字节被颠倒了,高2字节都为零所以无法验证
            // 所以目前的临时方法是直接将低2字节颠倒回来,而且只保留低2字节
            // 再次测试后发现似乎是 μTorrent 自己的诈和……(以前)第一次收到的是错误的(后来在调试的时候,第一次好像也对了),接下来收到的都是正确的……
            //var b1 = (byte)((bitTorrentClientPort & 0x0000ff00) >> 8);
            //var b2 = (byte)(bitTorrentClientPort & 0x000000ff);
            //bitTorrentClientPort = (int)(((int)b2 << 8) + b1);
        }
Exemple #2
0
        //public static bool TryRead(this Stream stream, byte[] buffer, int offset, int count, out int bytesRead)
        //{
        //    bool successful = true;
        //    int read = 0;
        //    try
        //    {
        //        read = stream.Read(buffer, offset, count);
        //    }
        //    catch (Exception)
        //    {
        //        successful = false;
        //    }
        //    bytesRead = read;
        //    return successful;
        //}

        //public static bool TryWrite(this Stream stream, byte[] buffer, int offset, int count)
        //{
        //    bool successful = true;
        //    try
        //    {
        //        stream.Write(buffer, offset, count);
        //    }
        //    catch (Exception)
        //    {
        //        successful = false;
        //    }
        //    return successful;
        //}

        /// <summary>
        /// 从指定的 <see cref="System.IO.Stream"/> 中读取一条消息。
        /// </summary>
        /// <param name="stream">要读取的 <see cref="System.IO.Stream"/>。</param>
        /// <param name="message">读取出的消息。如果返回值不是 <see cref="Kei.KNetwork.MessageIOErrorCode.NoError"/>,则该值无效。</param>
        /// <returns>一个 <see cref="Kei.KNetwork.MessageIOCode"/> 值,表示读取的结果。</returns>
        public static MessageIOErrorCode ReadMessage(this Stream stream, out KMessage message)
        {
            byte[]   buffer;
            KMessage km = new KMessage();

            // Check magic
            try
            {
                buffer = new byte[8];
                stream.Read(buffer, 0, 8);
                km.MagicNumber = BitConverter.ToUInt64(buffer, 0);
                if (km.MagicNumber != KMessage.Magic)
                {
                    message = km;
                    return(MessageIOErrorCode.WrongMagicNumber);
                }
            }
            catch (Exception)
            {
                message = km;
                return(MessageIOErrorCode.ReadMagicNumberFailed);
            }

            // Read header
            try
            {
                var headerSize = Marshal.SizeOf(typeof(KMessageHeader));
                buffer = new byte[headerSize];
                stream.Read(buffer, 0, headerSize);
                km.Header = KMessageHeader.FromByteArray(buffer);
            }
            catch (Exception)
            {
                message = km;
                return(MessageIOErrorCode.ReadHeaderFailed);
            }

            // Read content
            try
            {
                KMessageContent content = new KMessageContent();
                buffer = new byte[4];
                stream.Read(buffer, 0, 4);
                content.DataLength = BitConverter.ToUInt32(buffer, 0);
                content.Data       = new byte[content.DataLength];
                if (content.DataLength > 0)
                {
                    stream.Read(content.Data, 0, (int)content.DataLength);
                }
                km.Content = content;
            }
            catch (Exception)
            {
                message = km;
                return(MessageIOErrorCode.ReadContentFailed);
            }

            message = km;
            return(MessageIOErrorCode.NoError);
        }
Exemple #3
0
 /// <summary>
 /// 使用指定的参数创建一个新的 <see cref="Kei.KNetwork.HandleMessageArgs"/> 实例。
 /// </summary>
 /// <param name="stream">网络传输使用的 <see cref="System.IO.Stream"/>。</param>
 /// <param name="message">收到的消息。</param>
 /// <param name="endPoint">通信中的源端点。</param>
 internal HandleMessageArgs(Stream stream, KMessage message, IPEndPoint endPoint, ushort realPort)
 {
     _stream   = stream;
     _message  = message;
     _endPoint = endPoint;
     RealPort  = realPort;
 }
Exemple #4
0
        /// <summary>
        /// 尝试将一个端点(对应一个客户端)加入连接列表,并向该端点发送一条消息。
        /// </summary>
        /// <param name="endPoint">目标端点。</param>
        /// <param name="message">要发送的消息。</param>
        /// <returns>一个 <see cref="System.Threading.WaitHandle"/>,通过此对象可以对发送操作进行等待。</returns>
        private WaitHandle SendMessage(KEndPoint endPoint, KMessage message)
        {
            string tmpLog;

            tmpLog  = "KClient::SendMessage(KEndPoint, KMesage)";
            tmpLog += Environment.NewLine + "消息代码: " + message.Header.Code.ToString();
            tmpLog += Environment.NewLine + "消息唯一编码: " + message.Header.MessageID.ToString();
            tmpLog += Environment.NewLine + "消息来源: " + message.Header.SourceEndPoint.ToString();
            Logger.Log(tmpLog);
            AutoResetEvent ev     = new AutoResetEvent(false);
            Thread         thread = new Thread(delegate()
            {
                try
                {
                    TcpClient tcpClient = new TcpClient();
                    var remoteEndPoint  = endPoint.GetEndPoint();
                    Logger.Log("KClient::SendMessage(KEndPoint, KMessage) 工作线程。");
                    var connListItem = AddToConnectionList(endPoint);
                    if (connListItem == null)
                    {
                        return;
                    }
                    Logger.Log("尝试连接端点: " + remoteEndPoint.ToString());
                    var iar = tcpClient.BeginConnect(remoteEndPoint.Address, remoteEndPoint.Port, null, null);
                    if (!iar.IsCompleted)
                    {
                        iar.AsyncWaitHandle.WaitOne(SendTimeout);
                    }
                    if (iar.IsCompleted)
                    {
                        Logger.Log("连接成功。");
                        // 未超时
                        tcpClient.EndConnect(iar);
                        connListItem.ResetTimesTried();
                        var bs = tcpClient.GetStream();
                        {
                            Logger.Log("发送消息。");
                            bs.WriteMessage(message);
                        }
                        tcpClient.Close();
                    }
                    else
                    {
                        Logger.Log("连接失败。");
                        // 设置/移除出列表
                        connListItem.IncreaseTimesTriedAndCheck();
                    }
                    ev.Set();
                    ev.Dispose();
                }
                catch (Exception ex)
                {
                    Logger.Log(ex.Message + Environment.NewLine + ex.StackTrace);
                }
            });

            thread.IsBackground = true;
            thread.Start();
            return(ev);
        }
Exemple #5
0
        /// <summary>
        /// 生成一个 ClientExitNetwork 消息。
        /// </summary>
        /// <param name="localEP">消息源的端点。</param>
        /// <returns>创建的 ClientExitNetwork 消息。</returns>
        public static KMessage ClientExitNetwork(KEndPoint localEP)
        {
            var message = KMessage.CreateEmptyMessage();

            message.Header.Code           = KMessageCode.ClientExitNetwork;
            message.Header.SourceEndPoint = localEP;
            message.Header.MessageID      = GetMessageHash();
            return(message);
        }
Exemple #6
0
        /// <summary>
        /// 生成一个 ReportAlive 消息。
        /// </summary>
        /// <param name="localEP">消息源的端点。</param>
        /// <returns>创建的 ReportAlive 消息。</returns>
        public static KMessage ReportAlive(KEndPoint localEP)
        {
            var message = KMessage.CreateEmptyMessage();

            message.Header.Code           = KMessageCode.ReportAlive;
            message.Header.SourceEndPoint = localEP;
            message.Header.MessageID      = GetMessageHash();
            return(message);
        }
Exemple #7
0
        /// <summary>
        /// 向指定的 <see cref="System.Net.Sockets.Socket"/> 中写入一条消息。
        /// </summary>
        /// <param name="socket">要进行写入的 <see cref="System.Net.Sockets.Socket"/>。</param>
        /// <param name="message">要写入的消息。</param>
        /// <returns>一个 <see cref="Kei.KNetwork.MessageIOErrorCode"/> 值,表示写入的结果。</returns>
        public static MessageIOErrorCode WriteMessage(this Socket socket, KMessage message)
        {
            var data = message.ToByteArray();

            try
            {
                socket.Send(data);
                return(MessageIOErrorCode.NoError);
            }
            catch (Exception)
            {
                return(MessageIOErrorCode.WriteFailed);
            }
        }
Exemple #8
0
        /// <summary>
        /// 生成一个 ClientEnterNetwork 消息。
        /// </summary>
        /// <param name="localEP">消息源的端点。</param>
        /// <param name="realPort">非零表示这是接入点要连接接入点,该端口是本机正在监听的端口;零表示只是普通用户连接接入点。</param>
        /// <returns>创建的 ClientEnterNetwork 消息。</returns>
        public static KMessage ClientEnterNetwork(KEndPoint localEP, ushort realPort)
        {
            var message = KMessage.CreateEmptyMessage();

            message.Header.Code           = KMessageCode.ClientEnterNetwork;
            message.Header.SourceEndPoint = localEP;
            message.Header.MessageID      = GetMessageHash();

            BEncodedDictionary data = new BEncodedDictionary();

            data.Add("message handled", 0);
            data.Add("real port", realPort);
            message.Content = KMessageContent.FromByteArray(data.Encode());
            return(message);
        }
Exemple #9
0
        /// <summary>
        /// 生成一个 GotPeer 消息。
        /// </summary>
        /// <param name="localEP">消息源的端点。</param>
        /// <param name="infoHash">用户所持有的 InfoHash。</param>
        /// <param name="bitTorrentClientPort">用户所监听的端口(即 BitTorrent 客户端的监听端口)。</param>
        /// <returns>创建的 GotPeer 消息。</returns>
        public static KMessage GotPeer(KEndPoint localEP, InfoHash infoHash, ushort bitTorrentClientPort)
        {
            var message = KMessage.CreateEmptyMessage();

            message.Header.Code           = KMessageCode.GotPeer;
            message.Header.SourceEndPoint = localEP;
            message.Header.MessageID      = GetMessageHash();

            BEncodedDictionary data = new BEncodedDictionary();

            data.Add("infohash", infoHash.ToByteArray());
            data.Add("bt client port", bitTorrentClientPort);
            message.Content = KMessageContent.FromByteArray(data.Encode());
            return(message);
        }
Exemple #10
0
        /// <summary>
        /// 向指定的 <see cref="System.IO.Stream"/> 中写入一条消息。
        /// </summary>
        /// <param name="stream">要进行写入的 <see cref="System.IO.Stream"/>。</param>
        /// <param name="message">要写入的消息。</param>
        /// <returns>一个 <see cref="Kei.KNetwork.MessageIOErrorCode"/> 值,表示写入的结果。</returns>
        public static MessageIOErrorCode WriteMessage(this Stream stream, KMessage message)
        {
            var data = message.ToByteArray();

            try
            {
                stream.Write(data, 0, data.Length);
                stream.Flush();
                return(MessageIOErrorCode.NoError);
            }
            catch (Exception)
            {
                return(MessageIOErrorCode.WriteFailed);
            }
        }
Exemple #11
0
        /// <summary>
        /// 判断一个 List&lt;KHandledMessage&gt; 内是否含有以指定 <see cref="Kei.KNetwork.KMessages"/> 区分的 <see cref="Kei.KNetwork.KHandledMessage"/>。
        /// </summary>
        /// <param name="list">要判断的 List&lt;KHandledMessage&gt;。</param>
        /// <param name="message">作为指定键的 <see cref="Kei.KNetwork.KMessage"/>。</param>
        /// <returns>一个 <see cref="System.Boolean"/>,指示是否找到了符合条件的 <see cref="Kei.KNetwork.KHandledMessage"/>。</returns>
        public static bool Contains(this List <KHandledMessage> list, KMessage message)
        {
            if (list == null)
            {
                return(false);
            }
            bool contains = false;

            lock (list)
            {
                foreach (var item in list)
                {
                    if (item.Message.Equals(message))
                    {
                        contains = true;
                        break;
                    }
                }
            }
            return(contains);
        }
Exemple #12
0
 /// <summary>
 /// 从一个 <see cref="Kei.KNetwork.KMessage"/> 创建一个新的实例,并指定为此时为处理时刻,使用默认生存时间。
 /// </summary>
 /// <param name="message">处理的基础 <see cref="Kei.KNetwork.KMessage"/></param>
 public KHandledMessage(KMessage message)
     : this(message, DateTime.Now)
 {
 }
Exemple #13
0
        /// <summary>
        /// 向连接列表中的所有端点发送消息(即广播)。
        /// <para>注意,本方法执行中会为 ConnectionList 加锁。</para>
        /// </summary>
        /// <param name="message">要广播的消息。</param>
        /// <returns>一个 <see cref="System.Threading.WaitHandle"/>,通过此对象可以对发送操作进行等待。</returns>
        private WaitHandle BroadcastMessage(KMessage message)
        {
            string tmpLog;

            tmpLog  = "KClient::BroadcastMessage(KMesage)";
            tmpLog += Environment.NewLine + "消息代码: " + message.Header.Code.ToString();
            tmpLog += Environment.NewLine + "消息唯一编码: " + message.Header.MessageID.ToString();
            tmpLog += Environment.NewLine + "消息来源: " + message.Header.SourceEndPoint.ToString();
            Logger.Log(tmpLog);
            AutoResetEvent ev     = new AutoResetEvent(false);
            Thread         thread = new Thread(delegate()
            {
                try
                {
                    lock (ConnectionList)
                    {
                        foreach (var item in ConnectionList)
                        {
                            if (item.State != ConnectionState.RemovePending)
                            {
                                TcpClient tcpClient = new TcpClient();
                                var remoteEndPoint  = item.ClientLocation.GetEndPoint();
                                Logger.Log("尝试连接端点: " + remoteEndPoint.ToString());
                                var iar = tcpClient.BeginConnect(remoteEndPoint.Address, remoteEndPoint.Port, null, null);
                                if (!iar.IsCompleted)
                                {
                                    iar.AsyncWaitHandle.WaitOne(SendTimeout);
                                }
                                if (iar.IsCompleted)
                                {
                                    Logger.Log("连接成功。");
                                    // 未超时
                                    tcpClient.EndConnect(iar);
                                    item.ResetTimesTried();
                                    var bs = tcpClient.GetStream();
                                    {
                                        Logger.Log("广播消息。");
                                        // 本次调用是因为收到了 TrackerComm 事件的消息
                                        // 发生事件前 TrackerServer 的 Myself 就已经设置完成了,所以这里不用担心
                                        bs.WriteMessage(message);
                                    }
                                    tcpClient.Close();
                                }
                                else
                                {
                                    Logger.Log("连接超时。");
                                    // 设置/移除出列表
                                    item.IncreaseTimesTriedAndCheck();
                                }
                            }
                        }
                    }
                    Logger.Log("清扫连接列表并重设等待事件。");
                    SweepConnectionList();
                    ev.Set();
                    ev.Dispose();
                }
                catch (Exception ex)
                {
                    Logger.Log(ex.Message + Environment.NewLine + ex.StackTrace);
                }
            });

            thread.IsBackground = true;
            thread.Start();
            return(ev);
        }
Exemple #14
0
 /// <summary>
 /// 从一个 <see cref="Kei.KNetwork.KMessage"/> 创建一个新的实例,并设置处理时刻与生存时间。
 /// </summary>
 /// <param name="message">处理的基础 <see cref="Kei.KNetwork.KMessage"/>。</param>
 /// <param name="lifeStart">消息的处理时刻。</param>
 /// <param name="lifeLength">消息的生存时间。</param>
 public KHandledMessage(KMessage message, DateTime lifeStart, TimeSpan lifeLength)
 {
     _message    = message;
     _lifeStart  = lifeStart;
     _lifeLength = lifeLength;
 }
Exemple #15
0
 /// <summary>
 /// 从一个 <see cref="Kei.KNetwork.KMessage"/> 创建一个新的实例,设置处理时刻,并使用默认生存时间。
 /// </summary>
 /// <param name="message">处理的基础 <see cref="Kei.KNetwork.KMessage"/>。</param>
 /// <param name="lifeStart">消息的处理时刻。</param>
 public KHandledMessage(KMessage message, DateTime lifeStart)
     : this(message, lifeStart, DefaultLifeLength)
 {
 }
Exemple #16
0
        /// <summary>
        /// 在指定的 List&lt;KHandledMessage&gt; 中寻找以指定 <see cref="Kei.KNetwork.KMessage"/> 区分的 <see cref="Kei.KNetwork.KHandledMessage"/>,并返回查找结果。
        /// </summary>
        /// <param name="list">要在其中查找的 List&lt;KHandledMessage&gt;。</param>
        /// <param name="message">作为指定键的 <see cref="Kei.KNetwork.KMessage"/>。</param>
        /// <param name="index">输出一个 <see cref="System.Int32"/>。如果找到了,这个值是找到的项的索引;如果未找到,则该值无效。</param>
        /// <returns>一个 <see cref="Kei.KNetwork.KHandledMessage"/>,指示找到的项;或者 null,表示没找到。</returns>
        public static KHandledMessage FindHandledMessage(this List <KHandledMessage> list, KMessage message, out int index)
        {
            if (list == null)
            {
                index = 0;
                return(null);
            }
            KHandledMessage khm = null;
            int             i   = 0;

            index = 0;
            lock (list)
            {
                foreach (var item in list)
                {
                    if (item.Message.Equals(message))
                    {
                        index = i;
                        khm   = item;
                        break;
                    }
                    i++;
                }
            }
            return(khm);
        }
        /// <summary>
        /// 处理 ClientEnterNetwork 消息。
        /// </summary>
        /// <param name="args">处理时需要的信息。</param>
        private void HandleClientEnterNetwork(HandleMessageArgs args)
        {
            Logger.Log("收到消息: 客户端加入分布网络。");
            KEndPoint remoteEndPoint   = args.Message.Header.SourceEndPoint;
            int       isMessageHandled = 0;
            ushort    realPort         = 0;

            if (args.Message.Content.Data.Length > 0)
            {
                BEncodedDictionary dict;
                dict             = BEncodedValue.Decode <BEncodedDictionary>(args.Message.Content.Data);
                isMessageHandled = (int)((dict["message handled"] as BEncodedNumber).Number);
                realPort         = (ushort)((dict["real port"] as BEncodedNumber).Number);
            }
            string enterArgsRecord = "ClientEnterNetwork Args:";

            enterArgsRecord += Environment.NewLine + "isMessageHandled: " + isMessageHandled.ToString();
            enterArgsRecord += Environment.NewLine + "realPort: " + realPort.ToString();
            Logger.Log(enterArgsRecord);
            args.RealPort = realPort;
            if (!ConnectionList.Contains(remoteEndPoint))
            {
                if (isMessageHandled == 0)
                {
                    Logger.Log("本客户端是第一个收到这条进入消息的客户端。" + Environment.NewLine + "本机的连接列表如下。");
                    StringBuilder sb = new StringBuilder(100);
                    lock (ConnectionList)
                    {
                        foreach (var item in ConnectionList)
                        {
                            sb.AppendLine(item.ToString());
                        }
                    }
                    Logger.Log(sb.ToString());

                    Logger.Log("将当前连接信息编码并发送。我眼中的对方: " + args.EndPoint.ToString());
                    // 将自己的连接列表和用户列表编码,准备发送到连接来的客户端
                    var data = EncodeTargetInformation(args.EndPoint);
                    // 然后要修正 remoteEndPoint,因为加入消息必定是 127.0.0.1:ep 形式,所以要修正为实际的 ep
                    remoteEndPoint = KEndPoint.FromEndPoint(args.EndPoint);
                    try
                    {
                        // 先返回接下来的字节大小
                        args.Stream.WriteInt32(data.Length);
                        // 然后一次性发送
                        args.Stream.Write(data, 0, data.Length);
                        args.Stream.Flush();
                    }
                    catch (Exception ex)
                    {
                        // 首次通信就失败了……
                        Logger.Log(ex.Message);
                        Logger.Log(ex.StackTrace);
                    }
                }
            }

            KMessage message = args.Message;

            if (isMessageHandled == 0)
            {
                // 注意:虽然 KMessage 是一个值类型,但是其中含有引用类型(数组),这里修改了这个引用类型
                Logger.Log("设置该消息为处理过。");
                message.Content.Data = BitConverter.GetBytes((int)1);
            }
            else
            {
                // 本客户端不是第一个处理的,那就报告存活吧
                SendMessage(remoteEndPoint, MessageFactory.ReportAlive(LocalKEndPoint));
            }
            Logger.Log("转发消息。");
            // 转发
            BroadcastMessage(message);
        }