| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- using Masuit.Tools;
- using System.Net;
- using System.Text;
- namespace Masuit.MyBlogs.Core.Common;
- /// <summary>
- /// QQWryIpSearch 请作为单例使用 数据库缓存在内存
- /// </summary>
- public sealed class QQWrySearcher : IDisposable
- {
- private readonly SemaphoreSlim _initLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
- private object _versionLock = new object();
- private static readonly Encoding Gb2312Encoding;
- /// <summary>
- /// 数据库 缓存
- /// </summary>
- private byte[] _qqwryDbBytes;
- /// <summary>
- /// Ip索引 缓存
- /// </summary>
- private long[] _ipIndexCache;
- /// <summary>
- /// 起始定位
- /// </summary>
- private long _startPosition;
- /// <summary>
- /// 是否初始化
- /// </summary>
- private bool? _init;
- private readonly string _dbPath;
- private int? _ipCount;
- private string _version;
- /// <summary>
- /// 记录总数
- /// </summary>
- public int IpCount
- {
- get
- {
- _ipCount ??= _ipIndexCache.Length;
- return _ipCount.Value;
- }
- }
- /// <summary>
- /// 版本信息
- /// </summary>
- public string Version
- {
- get
- {
- if (!string.IsNullOrWhiteSpace(_version))
- {
- return _version;
- }
- lock (_versionLock)
- {
- if (!string.IsNullOrWhiteSpace(_version))
- {
- return _version;
- }
- _version = GetIpLocation(IPAddress.Parse("255.255.255.255")).Network;
- return _version;
- }
- }
- }
- static QQWrySearcher()
- {
- Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
- Gb2312Encoding = Encoding.GetEncoding("gb2312");
- }
- public QQWrySearcher(string dbPath)
- {
- _dbPath = dbPath;
- Init();
- }
- /// <summary>
- /// 初始化
- /// </summary>
- /// <returns></returns>
- public bool Init(bool getNewDb = false)
- {
- if (_init != null && !getNewDb)
- {
- return _init.Value;
- }
- _initLock.Wait();
- try
- {
- if (_init != null && !getNewDb)
- {
- return _init.Value;
- }
- _qqwryDbBytes = File.ReadAllBytes(_dbPath);
- _ipIndexCache = BlockToArray(ReadIpBlock(_qqwryDbBytes, out _startPosition));
- _ipCount = null;
- _version = null;
- _init = true;
- }
- finally
- {
- _initLock.Release();
- }
- if (_qqwryDbBytes == null)
- {
- throw new InvalidOperationException("无法打开IP数据库" + _dbPath + "!");
- }
- return true;
- }
- /// <summary>
- /// 获取指定IP所在地理位置
- /// </summary>
- /// <param name="ip">要查询的IP地址</param>
- /// <returns></returns>
- public (string City, string Network) GetIpLocation(IPAddress ip)
- {
- if (ip.IsPrivateIP())
- {
- return ("内网", "内网");
- }
- var ipnum = IpToLong(ip);
- return ReadLocation(ipnum, _startPosition, _ipIndexCache, _qqwryDbBytes);
- }
- /// <inheritdoc />
- /// <summary>
- /// 释放
- /// </summary>
- public void Dispose()
- {
- _initLock?.Dispose();
- _versionLock = null;
- _qqwryDbBytes = null;
- _qqwryDbBytes = null;
- _ipIndexCache = null;
- _init = null;
- }
- ///<summary>
- /// 将字符串形式的IP转换位long
- ///</summary>
- ///<param name="ip"></param>
- ///<returns></returns>
- private static long IpToLong(IPAddress ip)
- {
- var bytes = ip.GetAddressBytes();
- var ipBytes = new byte[8];
- for (var i = 0; i < 4; i++)
- {
- ipBytes[i] = bytes[3 - i];
- }
- return BitConverter.ToInt64(ipBytes);
- }
- ///<summary>
- /// 将索引区字节块中的起始IP转换成Long数组
- ///</summary>
- ///<param name="ipBlock"></param>
- private static long[] BlockToArray(byte[] ipBlock)
- {
- var ipArray = new long[ipBlock.Length / 7];
- var ipIndex = 0;
- var temp = new byte[8];
- for (var i = 0; i < ipBlock.Length; i += 7)
- {
- Array.Copy(ipBlock, i, temp, 0, 4);
- ipArray[ipIndex] = BitConverter.ToInt64(temp, 0);
- ipIndex++;
- }
- return ipArray;
- }
- /// <summary>
- /// 从IP数组中搜索指定IP并返回其索引
- /// </summary>
- /// <param name="ip"></param>
- /// <param name="ipArray">IP数组</param>
- /// <param name="start">指定搜索的起始位置</param>
- /// <param name="end">指定搜索的结束位置</param>
- /// <returns></returns>
- private static int SearchIp(long ip, long[] ipArray, int start, int end)
- {
- while (true)
- {
- //计算中间索引
- var middle = (start + end) / 2;
- if (middle == start)
- {
- return middle;
- }
- if (ip < ipArray[middle])
- {
- end = middle;
- }
- else
- {
- start = middle;
- }
- }
- }
- ///<summary>
- /// 读取IP文件中索引区块
- ///</summary>
- ///<returns></returns>
- private static byte[] ReadIpBlock(byte[] bytes, out long startPosition)
- {
- long offset = 0;
- startPosition = ReadLongX(bytes, offset, 4);
- offset += 4;
- var endPosition = ReadLongX(bytes, offset, 4);
- offset = startPosition;
- var count = (endPosition - startPosition) / 7 + 1;//总记录数
- var ipBlock = new byte[count * 7];
- for (var i = 0; i < ipBlock.Length; i++)
- {
- ipBlock[i] = bytes[offset + i];
- }
- return ipBlock;
- }
- /// <summary>
- /// 从IP文件中读取指定字节并转换位long
- /// </summary>
- /// <param name="bytes"></param>
- /// <param name="offset"></param>
- /// <param name="bytesCount">需要转换的字节数,主意不要超过8字节</param>
- /// <returns></returns>
- private static long ReadLongX(byte[] bytes, long offset, int bytesCount)
- {
- var cBytes = new byte[8];
- for (var i = 0; i < bytesCount; i++)
- {
- cBytes[i] = bytes[offset + i];
- }
- return BitConverter.ToInt64(cBytes, 0);
- }
- /// <summary>
- /// 从IP文件中读取字符串
- /// </summary>
- /// <param name="bytes"></param>
- /// <param name="flag">转向标志</param>
- /// <param name="offset"></param>
- /// <returns></returns>
- private static string ReadString(byte[] bytes, int flag, ref long offset)
- {
- if (flag == 1 || flag == 2)//转向标志
- {
- offset = ReadLongX(bytes, offset, 3);
- }
- else
- {
- offset -= 1;
- }
- var list = new List<byte>();
- var b = bytes[offset];
- offset += 1;
- while (b > 0)
- {
- list.Add(b);
- b = bytes[offset];
- offset += 1;
- }
- return Gb2312Encoding.GetString(list.ToArray());
- }
- private static (string City, string Network) ReadLocation(long ip, long startPosition, long[] ipIndex, byte[] qqwryDbBytes)
- {
- long offset = SearchIp(ip, ipIndex, 0, ipIndex.Length) * 7 + 4;
- //偏移
- var arrayOffset = startPosition + offset;
- //跳过结束IP
- arrayOffset = ReadLongX(qqwryDbBytes, arrayOffset, 3) + 4;
- //读取标志
- var flag = qqwryDbBytes[arrayOffset];
- arrayOffset += 1;
- //表示国家和地区被转向
- if (flag == 1)
- {
- arrayOffset = ReadLongX(qqwryDbBytes, arrayOffset, 3);
- //再读标志
- flag = qqwryDbBytes[arrayOffset];
- arrayOffset += 1;
- }
- var countryOffset = arrayOffset;
- var city = ReadString(qqwryDbBytes, flag, ref arrayOffset);
- if (flag == 2)
- {
- arrayOffset = countryOffset + 3;
- }
- flag = qqwryDbBytes[arrayOffset];
- arrayOffset += 1;
- var network = ReadString(qqwryDbBytes, flag, ref arrayOffset);
- return (city, network);
- }
- }
|