/// <summary> /// Computes the "info_hash" of the provided torrent (BDictionary). /// </summary> /// <param name="torrent">The BDicionary containing the torrent file.</param> /// <returns>An InfoHash object with the SHA1 hash.</returns> public static InfoHash ComputeInfoHash(BDictionary torrent) { IBValue info = null; //looks for the "info" dictionary foreach (KeyValuePair <string, IBValue> item in torrent.Items) { if (item.Key == "info" && item.Value is BDictionary) { info = item.Value; break; } } //if found, then computes the SHA1 hash and returns it if (info != null) { //the info_hash is the sha1 hash of the bencoded "info" dictionary, so gets it string bencoded = info.ToBEncodedString(); List <byte> bytes = new List <byte>(bencoded.Length); //adds its bytes to a list to be used in the ComputeHash function foreach (char c in info.ToBEncodedString()) { bytes.Add((byte)c); } SHA1 sha1 = SHA1.Create(); return(InfoHash.FromByteArray(sha1.ComputeHash(bytes.ToArray()))); } //if the "info" dictionary is not found, then returns an empty InfoHash to avoid exceptions... return(InfoHash.FromByteArray(new byte[] { })); }
/// <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); }
/// <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("待解码的数据为空,这意味着对方客户端目前持有的连接列表和用户列表为空。"); } }