IPSearcher.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using System.Threading.Tasks;
  5. using TEAMModelOS.SDK.IP2Region;
  6. using TEAMModelOS.SDK.Models;
  7. namespace TEAMModelOS.SDK
  8. {
  9. public class IPSearcher : IDisposable
  10. {
  11. const int BTREE_ALGORITHM = 1;
  12. const int BINARY_ALGORITHM = 2;
  13. const int MEMORY_ALGORITYM = 3;
  14. private IPConfig _dbConfig = null;
  15. /// <summary>
  16. /// db file access handler
  17. /// </summary>
  18. private Stream _raf = null;
  19. /// <summary>
  20. /// header blocks buffer
  21. /// </summary>
  22. private long[] _headerSip = null;
  23. private int[] _headerPtr = null;
  24. private int _headerLength;
  25. /// <summary>
  26. /// super blocks info
  27. /// </summary>
  28. private long _firstIndexPtr = 0;
  29. private long _lastIndexPtr = 0;
  30. private int _totalIndexBlocks = 0;
  31. /// <summary>
  32. /// for memory mode
  33. /// the original db binary string
  34. /// </summary>
  35. private byte[] _dbBinStr = null;
  36. /// <summary>
  37. /// Get by index ptr.
  38. /// </summary>
  39. private DataBlock GetByIndexPtr(long ptr)
  40. {
  41. _raf.Seek(ptr, SeekOrigin.Begin);
  42. byte[] buffer = new byte[12];
  43. _raf.Read(buffer, 0, buffer.Length);
  44. long extra = Utils.GetIntLong(buffer, 8);
  45. int dataLen = (int)((extra >> 24) & 0xFF);
  46. int dataPtr = (int)((extra & 0x00FFFFFF));
  47. _raf.Seek(dataPtr, SeekOrigin.Begin);
  48. byte[] data = new byte[dataLen];
  49. _raf.Read(data, 0, data.Length);
  50. int city_id = (int)Utils.GetIntLong(data, 0);
  51. string region = Encoding.UTF8.GetString(data, 4, data.Length - 4);
  52. return new DataBlock(city_id, region, dataPtr);
  53. }
  54. public IPSearcher(IPConfig dbConfig, string dbFile)
  55. {
  56. if (_dbConfig == null)
  57. {
  58. _dbConfig = dbConfig;
  59. }
  60. try {
  61. _raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read, FileShare.Read);
  62. } catch { }
  63. }
  64. public IPSearcher(string dbFile) : this(null, dbFile) { }
  65. public IPSearcher(IPConfig dbConfig, Stream dbFileStream)
  66. {
  67. if (_dbConfig == null)
  68. {
  69. _dbConfig = dbConfig;
  70. }
  71. _raf = dbFileStream;
  72. }
  73. public IPSearcher(Stream dbFileStream) : this(null, dbFileStream) { }
  74. #region Sync Methods
  75. /// <summary>
  76. /// Get the region with a int ip address with memory binary search algorithm.
  77. /// </summary>
  78. private DataBlock MemorySearch(long ip)
  79. {
  80. int blen = IndexBlock.LENGTH;
  81. if (_dbBinStr == null)
  82. {
  83. if (_raf == null) { return null; }
  84. try { _dbBinStr = new byte[(int)_raf.Length]; } catch (Exception) { return null; }
  85. _raf.Seek(0L, SeekOrigin.Begin);
  86. _raf.Read(_dbBinStr, 0, _dbBinStr.Length);
  87. //initialize the global vars
  88. _firstIndexPtr = Utils.GetIntLong(_dbBinStr, 0);
  89. _lastIndexPtr = Utils.GetIntLong(_dbBinStr, 4);
  90. _totalIndexBlocks = (int)((_lastIndexPtr - _firstIndexPtr) / blen) + 1;
  91. }
  92. //search the index blocks to define the data
  93. int l = 0, h = _totalIndexBlocks;
  94. long sip = 0;
  95. while (l <= h)
  96. {
  97. int m = (l + h) >> 1;
  98. int p = (int)(_firstIndexPtr + m * blen);
  99. sip = Utils.GetIntLong(_dbBinStr, p);
  100. if (ip < sip)
  101. {
  102. h = m - 1;
  103. }
  104. else
  105. {
  106. sip = Utils.GetIntLong(_dbBinStr, p + 4);
  107. if (ip > sip)
  108. {
  109. l = m + 1;
  110. }
  111. else
  112. {
  113. sip = Utils.GetIntLong(_dbBinStr, p + 8);
  114. break;
  115. }
  116. }
  117. }
  118. //not matched
  119. if (sip == 0) return null;
  120. //get the data
  121. int dataLen = (int)((sip >> 24) & 0xFF);
  122. int dataPtr = (int)((sip & 0x00FFFFFF));
  123. int city_id = (int)Utils.GetIntLong(_dbBinStr, dataPtr);
  124. string region = Encoding.UTF8.GetString(_dbBinStr, dataPtr + 4, dataLen - 4);//new String(dbBinStr, dataPtr + 4, dataLen - 4, Encoding.UTF8);
  125. return new DataBlock(city_id, region, dataPtr);
  126. }
  127. /// <summary>
  128. /// Get the region throught the ip address with memory binary search algorithm.
  129. /// </summary>
  130. public DataBlock MemorySearch(string ip)
  131. {
  132. return MemorySearch(Utils.Ip2long(ip));
  133. }
  134. /// <summary>
  135. /// Get the region with a int ip address with b-tree algorithm.
  136. /// </summary>
  137. private DataBlock BtreeSearch(long ip)
  138. {
  139. //check and load the header
  140. if (_headerSip == null)
  141. {
  142. _raf.Seek(8L, SeekOrigin.Begin); //pass the super block
  143. byte[] b = new byte[4096];
  144. _raf.Read(b, 0, b.Length);
  145. //fill the header
  146. int len = b.Length >> 3, idx = 0; //b.lenght / 8
  147. _headerSip = new long[len];
  148. _headerPtr = new int[len];
  149. long startIp, dataPtrTemp;
  150. for (int i = 0; i < b.Length; i += 8)
  151. {
  152. startIp = Utils.GetIntLong(b, i);
  153. dataPtrTemp = Utils.GetIntLong(b, i + 4);
  154. if (dataPtrTemp == 0) break;
  155. _headerSip[idx] = startIp;
  156. _headerPtr[idx] = (int)dataPtrTemp;
  157. idx++;
  158. }
  159. _headerLength = idx;
  160. }
  161. //1. define the index block with the binary search
  162. if (ip == _headerSip[0])
  163. {
  164. return GetByIndexPtr(_headerPtr[0]);
  165. }
  166. else if (ip == _headerPtr[_headerLength - 1])
  167. {
  168. return GetByIndexPtr(_headerPtr[_headerLength - 1]);
  169. }
  170. int l = 0, h = _headerLength, sptr = 0, eptr = 0;
  171. int m = 0;
  172. while (l <= h)
  173. {
  174. m = (l + h) >> 1;
  175. //perfectly matched, just return it
  176. if (ip == _headerSip[m])
  177. {
  178. if (m > 0)
  179. {
  180. sptr = _headerPtr[m - 1];
  181. eptr = _headerPtr[m];
  182. }
  183. else
  184. {
  185. sptr = _headerPtr[m];
  186. eptr = _headerPtr[m + 1];
  187. }
  188. }
  189. //less then the middle value
  190. else if (ip < _headerSip[m])
  191. {
  192. if (m == 0)
  193. {
  194. sptr = _headerPtr[m];
  195. eptr = _headerPtr[m + 1];
  196. break;
  197. }
  198. else if (ip > _headerSip[m - 1])
  199. {
  200. sptr = _headerPtr[m - 1];
  201. eptr = _headerPtr[m];
  202. break;
  203. }
  204. h = m - 1;
  205. }
  206. else
  207. {
  208. if (m == _headerLength - 1)
  209. {
  210. sptr = _headerPtr[m - 1];
  211. eptr = _headerPtr[m];
  212. break;
  213. }
  214. else if (ip <= _headerSip[m + 1])
  215. {
  216. sptr = _headerPtr[m];
  217. eptr = _headerPtr[m + 1];
  218. break;
  219. }
  220. l = m + 1;
  221. }
  222. }
  223. //match nothing just stop it
  224. if (sptr == 0) return null;
  225. //2. search the index blocks to define the data
  226. int blockLen = eptr - sptr, blen = IndexBlock.LENGTH;
  227. byte[] iBuffer = new byte[blockLen + blen]; //include the right border block
  228. _raf.Seek(sptr, SeekOrigin.Begin);
  229. _raf.Read(iBuffer, 0, iBuffer.Length);
  230. l = 0; h = blockLen / blen;
  231. long sip = 0;
  232. int p = 0;
  233. while (l <= h)
  234. {
  235. m = (l + h) >> 1;
  236. p = m * blen;
  237. sip = Utils.GetIntLong(iBuffer, p);
  238. if (ip < sip)
  239. {
  240. h = m - 1;
  241. }
  242. else
  243. {
  244. sip = Utils.GetIntLong(iBuffer, p + 4);
  245. if (ip > sip)
  246. {
  247. l = m + 1;
  248. }
  249. else
  250. {
  251. sip = Utils.GetIntLong(iBuffer, p + 8);
  252. break;
  253. }
  254. }
  255. }
  256. //not matched
  257. if (sip == 0) return null;
  258. //3. get the data
  259. int dataLen = (int)((sip >> 24) & 0xFF);
  260. int dataPtr = (int)((sip & 0x00FFFFFF));
  261. _raf.Seek(dataPtr, SeekOrigin.Begin);
  262. byte[] data = new byte[dataLen];
  263. _raf.Read(data, 0, data.Length);
  264. int city_id = (int)Utils.GetIntLong(data, 0);
  265. String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);// new String(data, 4, data.Length - 4, "UTF-8");
  266. return new DataBlock(city_id, region, dataPtr);
  267. }
  268. /// <summary>
  269. /// Get the region throught the ip address with b-tree search algorithm.
  270. /// </summary>
  271. public DataBlock BtreeSearch(string ip)
  272. {
  273. return BtreeSearch(Utils.Ip2long(ip));
  274. }
  275. /// <summary>
  276. /// Get the region with a int ip address with binary search algorithm.
  277. /// </summary>
  278. private DataBlock BinarySearch(long ip)
  279. {
  280. int blen = IndexBlock.LENGTH;
  281. if (_totalIndexBlocks == 0)
  282. {
  283. _raf.Seek(0L, SeekOrigin.Begin);
  284. byte[] superBytes = new byte[8];
  285. _raf.Read(superBytes, 0, superBytes.Length);
  286. //initialize the global vars
  287. _firstIndexPtr = Utils.GetIntLong(superBytes, 0);
  288. _lastIndexPtr = Utils.GetIntLong(superBytes, 4);
  289. _totalIndexBlocks = (int)((_lastIndexPtr - _firstIndexPtr) / blen) + 1;
  290. }
  291. //search the index blocks to define the data
  292. int l = 0, h = _totalIndexBlocks;
  293. byte[] buffer = new byte[blen];
  294. long sip = 0;
  295. while (l <= h)
  296. {
  297. int m = (l + h) >> 1;
  298. _raf.Seek(_firstIndexPtr + m * blen, SeekOrigin.Begin); //set the file pointer
  299. _raf.Read(buffer, 0, buffer.Length);
  300. sip = Utils.GetIntLong(buffer, 0);
  301. if (ip < sip)
  302. {
  303. h = m - 1;
  304. }
  305. else
  306. {
  307. sip = Utils.GetIntLong(buffer, 4);
  308. if (ip > sip)
  309. {
  310. l = m + 1;
  311. }
  312. else
  313. {
  314. sip = Utils.GetIntLong(buffer, 8);
  315. break;
  316. }
  317. }
  318. }
  319. //not matched
  320. if (sip == 0) return null;
  321. //get the data
  322. int dataLen = (int)((sip >> 24) & 0xFF);
  323. int dataPtr = (int)((sip & 0x00FFFFFF));
  324. _raf.Seek(dataPtr, SeekOrigin.Begin);
  325. byte[] data = new byte[dataLen];
  326. _raf.Read(data, 0, data.Length);
  327. int city_id = (int)Utils.GetIntLong(data, 0);
  328. String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);//new String(data, 4, data.Length - 4, "UTF-8");
  329. return new DataBlock(city_id, region, dataPtr);
  330. }
  331. /// <summary>
  332. /// Get the region throught the ip address with binary search algorithm.
  333. /// </summary>
  334. public DataBlock BinarySearch(String ip)
  335. {
  336. return BinarySearch(Utils.Ip2long(ip));
  337. }
  338. #endregion
  339. #region Async Methods
  340. /// <summary>
  341. /// Get the region throught the ip address with memory binary search algorithm.
  342. /// </summary>
  343. public Task<DataBlock> MemorySearchAsync(string ip)
  344. {
  345. return Task.FromResult(MemorySearch(ip));
  346. }
  347. public async Task<string> SearchIpAsync( string ip)
  348. {
  349. if (ip.Contains("::"))
  350. {
  351. ip = "127.0.0.1";
  352. }
  353. try
  354. {
  355. DataBlock block = await MemorySearchAsync(ip);
  356. if (block != null)
  357. {
  358. string region = block.Region.Replace("0|0|0|0|", "").Replace("0|0|0|", "").Replace("|0|0|0|0", "").Replace("|0|0|0|", "").Replace("|0|0|0", "").Replace("|0|0|", "").Replace("|0|0", "").Replace("|0|", "·").Replace("|0", "").Replace("|", "·");
  359. //if (!string.IsNullOrWhiteSpace(region))
  360. // {
  361. // region = region.Replace("中国·", "").Replace("中国", "").Replace("台湾省", "台湾");
  362. // }
  363. return region;
  364. }
  365. else { return null; }
  366. }
  367. catch (IPInValidException)
  368. {
  369. return "IP Illigel.";
  370. }
  371. catch (Exception) {
  372. return null;
  373. }
  374. }
  375. /// <summary>
  376. /// Get the region throught the ip address with b-tree search algorithm.
  377. /// </summary>
  378. public Task<DataBlock> BtreeSearchAsync(string ip)
  379. {
  380. return Task.FromResult(BtreeSearch(ip));
  381. }
  382. /// <summary>
  383. /// Get the region throught the ip address with binary search algorithm.
  384. /// </summary>
  385. public Task<DataBlock> BinarySearchAsync(string ip)
  386. {
  387. return Task.FromResult(BinarySearch(ip));
  388. }
  389. #endregion
  390. /// <summary>
  391. /// Close the db.
  392. /// </summary>
  393. public void Close()
  394. {
  395. _headerSip = null;
  396. _headerPtr = null;
  397. _dbBinStr = null;
  398. _raf.Close();
  399. }
  400. public void Dispose()
  401. {
  402. Close();
  403. }
  404. }
  405. }