/// <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); } } } }
/// <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); }
public async Task Handle(WebSocket webSocket) { if (MaxPeers <= Peer.Count) { await webSocket.CloseAsync(WebSocketCloseStatus.EndpointUnavailable, "Too many peers", CancellationToken.None); return; } using (Peer peer = Peer.Create(webSocket)) { try { ArraySegment <byte> buffer = new ArraySegment <byte>(new byte[4 * 1024]); while (webSocket.State == WebSocketState.Open) { List <byte> receivedBytes = new List <byte>(); WebSocketReceiveResult result = await webSocket.ReceiveAsync(buffer, CancellationToken.None); while (!result.EndOfMessage) { receivedBytes.AddRange(buffer.Take(result.Count)); result = await webSocket.ReceiveAsync(buffer, CancellationToken.None); } receivedBytes.AddRange(buffer.Take(result.Count)); string message = receivedBytes.ToArray().GetString(); await ParseMessage(peer, message); } } finally { if (!string.IsNullOrWhiteSpace(peer.lobby) && _lobbies.TryGetValue(peer.lobby, out Lobby lobby) && await lobby.Leave(peer)) { _ = _lobbies.TryRemove(peer.lobby, out _); Console.WriteLine("Deleted lobby {0}, {1} still open.", peer.lobby, _lobbies.Count); peer.lobby = null; } peer.shouldCloseConnection = false; } } }
/// <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); }
/// <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("待解码的数据为空,这意味着对方客户端目前持有的连接列表和用户列表为空。"); } }
public static bool AddPeer(TcpClient tcpc) { lock (addpeers_operation) { try { return(tcpc.Ip() != "" && !IpAddr.EqualsMine(tcpc.Ip()) && !HasPeer(tcpc.Ip()) && !HasMaximumConnections() && AddPeer(Peer.Create(Core, tcpc))); } catch { Log.NewLine($"Failed to connect to {tcpc.Ip()}."); return(false); } } }
public static bool AddPeer(string ip) { lock (addpeers_operation) { try { return(ip != "" && !IpAddr.EqualsMine(ip) && !HasPeer(ip) && !HasMaximumConnections() && AddPeer(Peer.Create(Core, ip))); } catch { Log.NewLine($"Failed to connect to {ip}."); return(false); } } }
/// <summary> /// 设定程序认为的 BitTorrent 客户端眼中的“自己”是什么。 /// </summary> /// <param name="endPoint">本地的 BitTorrent 客户端使用的端点,包括本地局域网地址(不是本地环回地址)及其监听端口。</param> public void SetMyself(IPEndPoint endPoint, string peerID = null) { _myself = Peer.Create(KEndPoint.FromEndPoint(endPoint), peerID); }