コード例 #1
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);
        }
コード例 #2
0
        /// <summary>
        /// 处理 GotPeer 消息。
        /// </summary>
        /// <param name="args">处理时所需要的信息。</param>
        private void HandleGotPeer(HandleMessageArgs args)
        {
            Logger.Log("收到消息: 在其他客户端上找到种子。");
            ushort   bitTorrentClientPort;
            InfoHash infoHash;

            Logger.Log("解码用户及 infohash 数据。");
            MessageFactory.GetPeerMessageContent(args.Message, out infoHash, out bitTorrentClientPort);
            Logger.Log("Infohash: " + infoHash.ToHexString() + Environment.NewLine + "用户端口: " + bitTorrentClientPort.ToString());
            List <Peer> peerList;

            if (!TrackerServer.Seeds.TryGetValue(infoHash, out peerList))
            {
                // 这是本客户端发出的信息,那么本客户端就要接收并处理
                peerList = new List <Peer>(8);
                lock (TrackerServer.Seeds)
                {
                    TrackerServer.Seeds.Add(infoHash, peerList);
                }
            }
            {
                Logger.Log("处理GOTPEER。添加对方地址。");
                KEndPoint ep = args.Message.Header.SourceEndPoint;
                ep.SetPort(bitTorrentClientPort);
                Peer peer = Peer.Create(ep);
                if (!peerList.Contains(peer))
                {
                    lock (peerList)
                    {
                        peerList.Add(peer);
                    }
                }
            }
        }
コード例 #3
0
        /// <summary>
        /// 处理 PeerExitNetwork 消息。
        /// </summary>
        /// <param name="args">处理时所需要的信息。</param>
        private void HandlePeerExitNetwork(HandleMessageArgs args)
        {
            Logger.Log("收到消息: 用户退出。");
            ushort   bitTorrentClientPort;
            InfoHash infoHash;

            Logger.Log("解码用户及 infohash 数据。");
            MessageFactory.GetPeerMessageContent(args.Message, out infoHash, out bitTorrentClientPort);
            Logger.Log("Infohash: " + infoHash.ToHexString() + Environment.NewLine + "用户端口: " + bitTorrentClientPort.ToString());
            List <Peer> peerList;

            if (TrackerServer.Seeds.TryGetValue(infoHash, out peerList))
            {
                Logger.Log("在本机发现该种子。移出列表。");
                KEndPoint ep = args.Message.Header.SourceEndPoint;
                ep.SetPort(bitTorrentClientPort);
                Peer peer = Peer.Create(ep);
                lock (peerList)
                {
                    peerList.Remove(peer);
                    EventHelper.RaiseEvent(ConnectionListChanged, this, EventArgs.Empty);
                }
            }

            Logger.Log("转发消息。");
            // 转发
            BroadcastMessage(args.Message);
        }
コード例 #4
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);
        }
コード例 #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);
        }
コード例 #6
0
 /// <summary>
 /// 设置本客户端的端点为新的端点。用于自我端点确认。
 /// </summary>
 /// <param name="ep">要被设置为的端点。</param>
 /// <exception cref="System.ArgumentNullException">ep 为 null 时发生。</exception>
 private void SetLocalEndPoint(IPEndPoint ep)
 {
     if (ep == null)
     {
         throw new ArgumentNullException("ep");
     }
     _localEndPoint  = ep;
     _localKEndPoint = KEndPoint.FromEndPoint(_localEndPoint);
 }
コード例 #7
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);
        }
コード例 #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);
        }
コード例 #9
0
ファイル: KMessageHeader.cs プロジェクト: radtek/KeiSystem
        /// <summary>
        /// 从一个字节数组中读取 <see cref="Kei.KNetwork.KMessageHeader"/> 所需的信息,并创建相应的 <see cref="Kei.KNetwork.KMessageHeader"/>。
        /// </summary>
        /// <param name="data">包含所需信息的字节数组。注意长度必须等于某个版本的 <see cref="Kei.KNetwork.KMessageHeader"/> 的长度。</param>
        /// <returns>创建的 <see cref="Kei.KNetwork.KMessageHeader"/>。</returns>
        public static KMessageHeader FromByteArray(byte[] data)
        {
            KMessageHeader header;

            // +4
            header.HeaderLength = BitConverter.ToUInt32(data, 0);
            // +2
            header.HeaderVersion = BitConverter.ToUInt16(data, 4);
            // +8
            header.MessageID = BitConverter.ToUInt64(data, 6);
            // +4
            header.Code = (KMessageCode)BitConverter.ToInt32(data, 14);
            // +?
            header.SourceEndPoint = KEndPoint.FromByteArray(data.Skip(18).Take(6).ToArray());

            return(header);
        }
コード例 #10
0
        /// <summary>
        /// 判断一个 List&lt;ConnectionListItem&gt; 内是否含有以指定 <see cref="Kei.KEndPoint"/> 区分的 <see cref="Kei.KNetwork.ConnectionListItem"/>。
        /// </summary>
        /// <param name="list">要判断的 List&lt;ConnectionListItem&gt;。</param>
        /// <param name="endPoint">作为指定键的 <see cref="Kei.KEndPoint"/>。</param>
        /// <returns>一个 <see cref="System.Boolean"/>,指示是否找到了符合条件的 <see cref="Kei.KNetwork.ConnectionListItem"/>。</returns>
        public static bool Contains(this List <ConnectionListItem> list, KEndPoint endPoint)
        {
            if (list == null)
            {
                return(false);
            }
            bool contains = false;

            lock (list)
            {
                foreach (var item in list)
                {
                    if (item.ClientLocation.Equals(endPoint))
                    {
                        contains = true;
                        break;
                    }
                }
            }
            return(contains);
        }
コード例 #11
0
        /// <summary>
        /// 将一个端点(对应一个客户端)添加至连接列表。如果列表中已有此端点,则返回对应的连接列表项。
        /// <para>注意,本方法执行中会为 ConnectionList 加锁。</para>
        /// </summary>
        /// <param name="endPoint">要添加的客户端的端点。</param>
        /// <returns>一个 <see cref="Kei.KNetwork.ConnectionListItem"/>,表示添加或者找到的连接列表项。</returns>
        private ConnectionListItem AddToConnectionList(KEndPoint endPoint)
        {
            if (endPoint.Equals(LocalKEndPoint))
            {
                Logger.Log("KClient::AddToConnectionList(KEndPoint): 不能加入自己");
                return(null);
            }
            Logger.Log("KClient::AddToConnectionList(KEndPoint)" + Environment.NewLine + "待加入的端点: " + endPoint.ToString());
            int index;
            var connItem = ConnectionList.FindConnectionListItem(endPoint, out index);

            if (connItem == null)
            {
                Logger.Log("在列表中未发现,即将加入。");
                connItem = new ConnectionListItem(endPoint);
                lock (ConnectionList)
                {
                    ConnectionList.Add(connItem);
                    EventHelper.RaiseEvent(ConnectionListChanged, this, EventArgs.Empty);
                }
            }
            return(connItem);
        }
コード例 #12
0
        /// <summary>
        /// 处理 ClientExitNetwork 消息。
        /// </summary>
        /// <param name="args">处理时所需要的信息。</param>
        private void HandleClientExitNetwork(HandleMessageArgs args)
        {
            Logger.Log("收到消息: 客户端离开分布网络。");
            KEndPoint remoteEndPoint = args.Message.Header.SourceEndPoint;
            // 只是简单的移除
            int clItemIndex;
            ConnectionListItem item = ConnectionList.FindConnectionListItem(remoteEndPoint, out clItemIndex);

            if (item != null)
            {
                Logger.Log("找到项目并移除。");
                lock (ConnectionList)
                {
                    // 一样,应该不用担心前面的项被移动到后面去导致索引错误的事情吧
                    ConnectionList.RemoveAt(clItemIndex);
                    EventHelper.RaiseEvent(ConnectionListChanged, this, EventArgs.Empty);
                }
            }

            Logger.Log("转发消息。");
            // 转发
            BroadcastMessage(args.Message);
        }
コード例 #13
0
        /// <summary>
        /// 处理 PeerEnterNetwork 消息。
        /// </summary>
        /// <param name="args">处理时所需要的信息。</param>
        private void HandlePeerEnterNetwork(HandleMessageArgs args)
        {
            Logger.Log("收到消息: 用户加入。");
            ushort   bitTorrentClientPort;
            InfoHash infoHash;

            Logger.Log("解码用户及 infohash 数据。");
            MessageFactory.GetPeerMessageContent(args.Message, out infoHash, out bitTorrentClientPort);
            Logger.Log("Infohash: " + infoHash.ToHexString() + Environment.NewLine + "用户端口: " + bitTorrentClientPort.ToString());
            List <Peer> peerList;

            if (TrackerServer.Seeds.TryGetValue(infoHash, out peerList))
            {
                Logger.Log("在本机发现该种子。加入列表。");
                KEndPoint remoteClientEP = args.Message.Header.SourceEndPoint;
                KEndPoint remoteBTEP     = remoteClientEP;
                remoteBTEP.SetPort(bitTorrentClientPort);
                Peer peer = Peer.Create(remoteBTEP);
                if (!peerList.Contains(peer))
                {
                    lock (peerList)
                    {
                        peerList.Add(peer);
                    }
                }

                // 同时报告信息源,我这里有种子
                Logger.Log("报告消息源在这里找到种子。");
                // 此时 ep 的 Port 已经被修改,所以不能直接向 ep 所表示的端点发送,之前写错了……
                // 所以后面索性改了名称,remoteClientEP 和 remoteBTEP
                SendMessage(remoteClientEP, MessageFactory.GotPeer(LocalKEndPoint, infoHash, (ushort)TrackerServer.Myself.EndPoint.GetPortNumber()));
            }

            Logger.Log("转发消息。");
            // 转发
            BroadcastMessage(args.Message);
        }
コード例 #14
0
 /// <summary>
 /// 使用指定的目标端点、项状态和已尝试次数创建一个新的 ConnectionListItem。
 /// </summary>
 /// <param name="clientLocation">指定的目标端点。</param>
 /// <param name="state">指定的项状态。</param>
 /// <param name="timesTried">指定的已尝试次数。</param>
 private ConnectionListItem(KEndPoint clientLocation, ConnectionState state, uint timesTried)
 {
     _clientLocation = clientLocation;
     _state          = state;
     _timesTried     = timesTried;
 }
コード例 #15
0
        /// <summary>
        /// 在指定的 List&lt;ConnectionListItem&gt; 中寻找以指定 <see cref="Kei.KEndPoint"/> 区分的 <see cref="Kei.KNetwork.ConnectionListItem"/>,并返回查找结果。
        /// </summary>
        /// <param name="list">要在其中查找的 List&lt;ConnectionListItem&gt;。</param>
        /// <param name="endPoint">作为指定键的 <see cref="Kei.KEndPoint"/>。</param>
        /// <param name="index">输出一个 <see cref="System.Int32"/>。如果找到了,这个值是找到的项的索引;如果未找到,则该值无效。</param>
        /// <returns>一个 <see cref="Kei.KNetwork.ConnectionListItem"/>,指示找到的项;或者 null,表示没找到。</returns>
        public static ConnectionListItem FindConnectionListItem(this List <ConnectionListItem> list, KEndPoint endPoint, out int index)
        {
            if (list == null)
            {
                index = 0;
                return(null);
            }
            ConnectionListItem cli = null;
            int i = 0;

            index = 0;
            lock (list)
            {
                foreach (var item in list)
                {
                    if (item.ClientLocation.Equals(endPoint))
                    {
                        index = i;
                        cli   = item;
                        break;
                    }
                    i++;
                }
            }
            return(cli);
        }
コード例 #16
0
        /// <summary>
        /// 消息处理的核心分发过程。
        /// </summary>
        /// <param name="args">处理时需要的信息。</param>
        private void HandleMessage(HandleMessageArgs args)
        {
            Logger.Log("KClient::HandleMessage() from " + args.Message.Header.SourceEndPoint.ToString() + " with code " + args.Message.Header.Code.ToString());
            // 确保不是自己发出的消息
            if (!args.Message.Header.SourceEndPoint.Equals(_localKEndPoint))
            {
                KHandledMessage handledMessage;
                int             handledMessageIndex;
                handledMessage = HandledMessages.FindHandledMessage(args.Message, out handledMessageIndex);
                string tmpLog;
                tmpLog  = "消息代码: " + args.Message.Header.Code.ToString();
                tmpLog += Environment.NewLine + "消息唯一编码: " + args.Message.Header.MessageID.ToString();
                tmpLog += Environment.NewLine + "消息来源: " + args.Message.Header.SourceEndPoint.ToString();
                Logger.Log(tmpLog);
                // 若未处理过该消息
                if (handledMessage == null)
                {
                    Logger.Log("未处理过该消息。");
                    switch (args.Message.Header.Code)
                    {
                    case KMessageCode.ReportAlive:
                        if (!args.Message.Header.SourceEndPoint.AddressEquals(IPAddress.Loopback))
                        {
                            HandleReportAlive(args);
                            AddToConnectionList(args.Message.Header.SourceEndPoint);
                        }
                        break;

                    case KMessageCode.ClientEnterNetwork:
                        HandleClientEnterNetwork(args);
                        if (args.RealPort == 0)
                        {
                            // 这条消息是用来连接到接入点的,此时对方还未初始化完毕,所以不用判断源端点的事情
                            AddToConnectionList(KEndPoint.FromEndPoint(args.EndPoint));
                        }
                        else
                        {
                            // 这是接入点连接到接入点的请求
                            // 此时地址是真实的,监听端口由 args.RealPort 反映
                            var kep = KEndPoint.FromEndPoint(args.EndPoint);
                            kep.SetPort(args.RealPort);
                            AddToConnectionList(kep);
                        }
                        break;

                    case KMessageCode.ClientExitNetwork:
                        if (!args.Message.Header.SourceEndPoint.AddressEquals(IPAddress.Loopback))
                        {
                            HandleClientExitNetwork(args);
                            // 既然退出了为什么还要保留这个项
                            //AddToConnectionList(args.Message.Header.SourceEndPoint);
                        }
                        break;

                    case KMessageCode.PeerEnterNetwork:
                        if (!args.Message.Header.SourceEndPoint.AddressEquals(IPAddress.Loopback))
                        {
                            HandlePeerEnterNetwork(args);
                            AddToConnectionList(args.Message.Header.SourceEndPoint);
                        }
                        break;

                    case KMessageCode.PeerExitNetwork:
                        if (!args.Message.Header.SourceEndPoint.AddressEquals(IPAddress.Loopback))
                        {
                            HandlePeerExitNetwork(args);
                            AddToConnectionList(args.Message.Header.SourceEndPoint);
                        }
                        break;

                    case KMessageCode.GotPeer:
                        if (!args.Message.Header.SourceEndPoint.AddressEquals(IPAddress.Loopback))
                        {
                            HandleGotPeer(args);
                            AddToConnectionList(args.Message.Header.SourceEndPoint);
                        }
                        break;

                    default:
                        return;
                    }

                    Logger.Log("将消息加入“已处理”列表。");
                    // 对于收到的信息,都加入“已处理消息”列表
                    lock (HandledMessages)
                    {
                        HandledMessages.Add(new KHandledMessage(args.Message));
                    }
                }
                else
                {
                    Logger.Log("更新消息生命周期。");
                    // 更新生命周期,并将其移动到列表最后(因为是“刚刚加入”的)
                    handledMessage.LifeStart = DateTime.Now;
                    lock (HandledMessages)
                    {
                        // 访问这个过程的只有一个线程,所以不用担心说前面某一项先被移动到后面去了导致索引错误
                        HandledMessages.RemoveAt(handledMessageIndex);
                        HandledMessages.Add(handledMessage);
                    }
                    Logger.Log("清理“已处理”列表。");
                    SweepHandledMessages();
                }
            }
        }
コード例 #17
0
        /// <summary>
        /// 连接到指定的接入端点,并广播“加入分布网络”消息。
        /// <para>注意,本方法执行中会为 ConnectionList 加锁。</para>
        /// </summary>
        /// <param name="pointInsertion">要连接的端点。</param>
        /// <param name="realPort">非零表示这是接入点要连接接入点,该端口是本机正在监听的端口;零表示只是普通用户连接接入点。</param>
        /// <param name="workerThread">返回发送用的 <see cref="System.Threading.Thread"/>。</param>
        /// <returns>一个 <see cref="System.Threading.WaitHandle"/>,通过此对象可以对发送操作进行等待。</returns>
        public WaitHandle EnterNetwork(IPEndPoint pointInsertion, ushort realPort, out Thread workerThread)
        {
            Logger.Log("KClient::EnterNetwork(IPEndPoint)" + Environment.NewLine + "接入点: " + pointInsertion.ToString());
            AutoResetEvent ev     = new AutoResetEvent(false);
            Thread         thread = new Thread(delegate()
            {
                try
                {
                    // 第一次发送信息,要使用以后用来接收信息的端口,这样才能让接入点知道你映射后的接收端口
                    //TcpClient client = new TcpClient();
                    // 那么就不能采取常规的方式,而应该用 TcpListener 的 socket 发出去
                    //TcpClient client = new TcpClient(new IPEndPoint(IPAddress.Loopback, LocalEndPoint.Port));
                    Socket enterSocket;
                    if (realPort == 0)
                    {
                        // 普通用户
                        enterSocket = _listener.Server;
                        if (!enterSocket.IsBound)
                        {
                            enterSocket.Bind(new IPEndPoint(IPAddress.Any, ((IPEndPoint)_listener.LocalEndpoint).Port));
                        }
                    }
                    else
                    {
                        enterSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    }
                    //Socket enterSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    //Socket enterSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                    //enterSocket.Bind(new IPEndPoint(IPAddress.Loopback, LocalEndPoint.Port));
                    Logger.Log("尝试连接端点: " + pointInsertion.ToString());
                    //var iar = client.BeginConnect(pointInsertion.Address, pointInsertion.Port, null, null);
                    var iar = enterSocket.BeginConnect(pointInsertion, null, null);
                    if (!iar.IsCompleted)
                    {
                        iar.AsyncWaitHandle.WaitOne(SendTimeout);
                    }
                    if (!iar.IsCompleted)
                    {
                        Logger.Log("连接超时。");
                        // 未成功连接的话,直接返回
                        //return false;
                        ev.Set();
                        ev.Dispose();
                        return;
                        // 这里好像无法直接返回连接状态
                        // 但是应该可以判断连接列表是否为空来确定是否成功,非空则成功,否则失败
                    }
                    //client.EndConnect(iar);
                    enterSocket.EndConnect(iar);
                    Logger.Log("socket 本地端点: " + enterSocket.LocalEndPoint.ToString() + " 远程端点: " + enterSocket.RemoteEndPoint.ToString());
                    // 为了不干扰连接列表的报告,先发送信息
                    Logger.Log("连接成功,发送自己进入的消息。");

                    int origPortNumber;
                    string origAddress;

                    //var bs = client.GetStream();
                    {
                        // 写入
                        //bs.WriteMessage(MessageFactory.ClientEnterNetwork(LocalKEndPoint));
                        enterSocket.WriteMessage(MessageFactory.ClientEnterNetwork(LocalKEndPoint, realPort));

                        // 接下来接收接入点返回的当前连接信息
                        // 先是读取列表数据大小
                        Logger.Log("读取返回数据: 数据长度");
                        //int dataLength = bs.ReadInt32();
                        int dataLength = enterSocket.ReadInt32();
                        Logger.Log("长度: " + dataLength.ToString());
                        // 然后读列表
                        var dataBuffer      = new byte[1024];
                        var data            = new byte[dataLength];
                        int readLength      = 0;
                        int totalReadLength = 0;
                        // 确保全部读取
                        while (totalReadLength < dataLength)
                        {
                            //readLength = bs.Read(dataBuffer, 0, dataBuffer.Length);
                            readLength = enterSocket.Receive(dataBuffer);
                            if (readLength > 0)
                            {
                                Array.Copy(dataBuffer, 0, data, totalReadLength, readLength);
                                totalReadLength += readLength;
                            }
                        }
                        Logger.Log("数据读取完毕。准备解码。");

                        origPortNumber = LocalEndPoint.Port;
                        origAddress    = LocalEndPoint.Address.ToString();

                        // 解码并将自己的连接列表和用户列表同步为接入点的数据
                        DecodeTargetInformation(data);
                        Logger.Log("他人眼中的我: " + LocalEndPoint.ToString());

                        StringBuilder sb = new StringBuilder(100);
                        sb.AppendLine("刚刚收到的连接列表如下。");
                        lock (ConnectionList)
                        {
                            foreach (var item in ConnectionList)
                            {
                                sb.AppendLine(item.ToString());
                            }
                        }
                        sb.AppendLine("刚刚收到的用户列表如下。");
                        lock (TrackerServer.Seeds)
                        {
                            foreach (var item in TrackerServer.Seeds)
                            {
                                sb.AppendLine("[" + item.Key.ToHexString() + "]");
                                foreach (var peer in item.Value)
                                {
                                    sb.AppendLine(peer.EndPoint.ToString());
                                }
                            }
                        }
                        Logger.Log(sb.ToString());
                    }

                    Logger.Log("添加对方持有的连接信息结束,将对方加入自己的连接列表。");
                    // 然后将对方加入自己的连接列表
                    try
                    {
                        AddToConnectionList(KEndPoint.FromEndPoint(pointInsertion));
                    }
                    catch (Exception)
                    {
                    }

                    // 不需要
                    //if (ConnectionList.Count > 0)
                    //{
                    //    Logger.Log("尝试广播消息,让连接列表中的客户端报告他们存活。");
                    //    // 要其他人报告他们存活
                    //    // 能发送等价于存活,所以并不期望回音,只是测试能连接而已
                    //    BroadcastMessage(MessageFactory.ReportAlive(LocalKEndPoint));
                    //}

                    //client.Close();
                    enterSocket.Disconnect(false);
                    enterSocket.Close();
                    _listener.Stop();

                    // 说到底,要 EnterNetwork(),肯定是普通客户端,那么就请求端口映射吧
                    //if (origPortNumber != LocalEndPoint.Port)
                    {
                        if (PortMapping.CanUsePortMapping)
                        {
                            try
                            {
                                if (origPortNumber == LocalEndPoint.Port)
                                {
                                    Thread.Sleep(TimeSpan.FromSeconds(0.8));
                                }
                                //PortMapping.AddPortMapping((ushort)origPortNumber, (ushort)LocalEndPoint.Port, origAddress);
                                PortMapping.AddPortMapping((ushort)origPortNumber, (ushort)LocalEndPoint.Port);
                            }
                            catch (Exception ex)
                            {
                                System.Diagnostics.Debug.Print("AddPortMapping: " + ex.Message);
                                Logger.Log("AddPortMapping: " + ex.Message);
                            }
                        }
                    }

                    ev.Set();
                    ev.Dispose();
                    //return true;
                }
                catch (Exception ex)
                {
                    Logger.Log(ex.Message + Environment.NewLine + ex.StackTrace);
                }
            });

            thread.IsBackground = true;
            thread.Start();
            workerThread = thread;
            return(ev);
        }
コード例 #18
0
ファイル: KClient.Interops.cs プロジェクト: radtek/KeiSystem
        /// <summary>
        /// 解码连接到接入点时收到的字节数组,并应用这些信息。
        /// </summary>
        /// <param name="data">收到的数据。</param>
        ///// <param name="sendPeerEnter">解码过程中是否应该广播 PeerEnterNetwork 消息。</param>
        /// <exception cref="System.FormatException">解码失败时发生。</exception>
        /// <remarks>
        /// 需要发送 PeerEnterNetwork 消息的情况会发生于:A开启客户端和μT,B开启客户端和μT,A(用户列表非空)再尝试连接B,此时如果B并没有保存全网的用户列表,那么A就要广播 PeerEnterNetwork。
        /// </remarks>
        private void DecodeTargetInformation(byte[] data)
        {
            // 如果对方发过来的是空,那么就肯定不会有数据啦
            if (data.Length > 0)
            {
                BEncodedDictionary dictionary = BEncodedDictionary.Decode(data) as BEncodedDictionary;
                if (dictionary == null)
                {
                    throw new FormatException("无法解码。");
                }
                BEncodedList       connList  = dictionary["connections"] as BEncodedList;
                BEncodedDictionary peersDict = dictionary["peers"] as BEncodedDictionary;

                // 规范 v1.2
                // 先确认自己,同时 if ... 是兼容老版的通信
                if (dictionary.ContainsKey("your endpoint"))
                {
                    BEncodedDictionary yourEndPoint = dictionary["your endpoint"] as BEncodedDictionary;
                    var ip   = new IPAddress((yourEndPoint["ip"] as BEncodedString).TextBytes);
                    var port = BitConverter.ToUInt16((yourEndPoint["port"] as BEncodedString).TextBytes, 0);
                    // 分别设置 KClient、TrackerServer 和 BT 客户端的自己
                    SetLocalEndPoint(new IPEndPoint(ip, port));
                    TrackerServer.SetLocalEndPoint(new IPEndPoint(ip, TrackerServer.LocalEndPoint.Port));
                    TrackerServer.SetMyself(new IPEndPoint(ip, TrackerServer.Myself.EndPoint.GetPortNumber()));

                    this.FreeToGo          = true;
                    TrackerServer.FreeToGo = true;
                }

                // ...
                lock (ConnectionList)
                {
                    foreach (var item in connList)
                    {
                        var       d   = item as BEncodedDictionary;
                        KEndPoint kep = KEndPoint.Empty;
                        kep.SetAddress((d["ip"] as BEncodedString).TextBytes);
                        kep.SetPort((int)BitConverter.ToUInt16((d["port"] as BEncodedString).TextBytes, 0));
                        try
                        {
                            AddToConnectionList(kep);
                        }
                        catch (Exception)
                        {
                        }
                    }
                }

                // 如果已经有用户登记了,那么应该广播
                if (TrackerServer.Seeds.Count > 0)
                {
                    lock (TrackerServer.Seeds)
                    {
                        foreach (var kv in TrackerServer.Seeds)
                        {
                            // 广播消息
                            BroadcastMyselfAddAsPeer(kv.Key);
                        }
                    }
                }
                lock (TrackerServer.Seeds)
                {
                    foreach (var kv in peersDict)
                    {
                        InfoHash    infoHash = InfoHash.FromByteArray(kv.Key.TextBytes);
                        List <Peer> peers    = new List <Peer>((kv.Value as BEncodedList).Count);
                        foreach (var item in (kv.Value as BEncodedList))
                        {
                            var       d   = item as BEncodedDictionary;
                            KEndPoint kep = KEndPoint.Empty;
                            kep.SetAddress((d["ip"] as BEncodedString).TextBytes);
                            kep.SetPort((int)BitConverter.ToUInt16((d["port"] as BEncodedString).TextBytes, 0));
                            Peer peer = Peer.Create(kep);
                            peers.Add(peer);
                        }
                        try
                        {
                            TrackerServer.Seeds.Add(infoHash, peers);
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }
            else
            {
                Logger.Log("待解码的数据为空,这意味着对方客户端目前持有的连接列表和用户列表为空。");
            }
        }
コード例 #19
0
 /// <summary>
 /// 设定程序认为的 BitTorrent 客户端眼中的“自己”是什么。
 /// </summary>
 /// <param name="endPoint">本地的 BitTorrent 客户端使用的端点,包括本地局域网地址(不是本地环回地址)及其监听端口。</param>
 public void SetMyself(IPEndPoint endPoint, string peerID = null)
 {
     _myself = Peer.Create(KEndPoint.FromEndPoint(endPoint), peerID);
 }
コード例 #20
0
 /// <summary>
 /// 使用指定的目标端点创建一个新的 ConnectionListItem。
 /// </summary>
 /// <param name="clientLocation">指定的目标端点。</param>
 public ConnectionListItem(KEndPoint clientLocation)
     : this(clientLocation, ConnectionState.Active, 0)
 {
 }
コード例 #21
0
        /// <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);
        }