继续接着第一篇写:使用C#实现DHT磁力搜索的BT种子后端管理程序+数据库设计(开源)[搜片神器]

谢谢园子朋友的支持,已经找到个VPS进行测试,国外的服务器: http://www.sosobta.com 大家可以给提点意见...

开源地址:https://github.com/h31h31/H31DHTMgr

程序下载:H31DHT下载

看大家对昨天此类文章的兴趣没有第一篇高,今天就简单的对支持的朋友进行交流.园子里的朋友希望授大家以渔,所以这部分代码就先不放出来.希望大家更多的加入进来.

也希望谁有能力将C++的代码转换成C#的,添加到我们的搜片神器工具里面.

昨天通过向大家介绍DHT的工作原理,相信大家大概明白怎么回事,不明白的朋友可以继续分享接下来的文章.

本人借鉴的代码是C++版本的:transmission里面的DHT代码,大家可以访问网站下载:http://www.transmissionbt.com/

不过里面的代码环境是LINUX下的,需要自己转换到相应的WIN平台上来.

有兴趣使用C#来完成DHT功能的朋友可以借鉴mono-monotorrent,里面的框架代码比较多,不如C++的transmission里面就三个文件来得明白.

transmission里面只有三个文件就可以实现dht的功能: dht.c dht.h dht-example.c,并且接口很简单,复用性很好。

下面介绍进入DHT网络主要功能步骤

dht.c dht.h代码分成三部分:

1、路由表的插入操作。
1)如果节点已经在路由表中,则更新节点,返回。
2)如果桶没有满,则插入,返回。
3)如果发现失效节点,替换,返回。
4)发现可疑节点,则保存新节点到缓存中并且如果该可疑节点没有ping,发出ping_node操作,返回。
5)现在,桶已经充满了好的节点,如果自己的ID没有落在这个桶中,返回。
6)将桶空间分成两半。跳到步骤1)。

2、KAD远程处理调用。
这部分又分成3种,
1)ping/pong操作。
所有的包的tid都使用pg\0\0
2)find_node操作。
所有的包的tid都使用fn\0\0
3)get_peers/annouce_peer操作。
对同一个HASH的一次递归查询中,tid保持不变。
其中只有3)种实现bittorrent的DHT规范里面提到的递归查询操作,1)和2)仅仅用来维护路由表,并且不保存状态。

3、定时器处理:
为了检测路由表中节点的有效性(根据规范,路由表中应该只保存有效节点),在代码中,在执行krpc操作时如果发现时对路由表中的节点操作,那么则保存操作的开始时间
pinged_time,通过操作的开始时间来判断操作是否超时。

expire_stuff_time
超时时,会执行下面的操作:
1、检查路由表中失效的节点(根据pinged_time来判定),并将该节点删除。
2、检查用来保存annoounce_peer的节点是否超过30分钟(这个不打算深入讨论,故不做解析)。
3、检查递归查询操作超时。

rotate_secrets_time
定时器。
用来每隔大约15分左右就更换token(见DHT规范).

confirm_nodes_time
定时器。
查找长期没有活动的桶,然后通过执行一个find_node的krpc操作来刷新它。

search_time定时器。
有可能出现发出的所有的get_peers操作,都没有应答,那么search_time定时器遇到这种情形时负责重发所有请求。(注意:
get_peers操作最大未决的krpc请求数是3)

用于维持路由表的ping/pong操作:
在试图插入节点时,发现桶已经满,而存在可疑节点时会触发ping_node操作。未响应的节点会有可疑最终变为失效节点,而被替换。

下面介绍我们是如何进入DHT网络

  1. DHT必须把自己电脑当服务器,别人才能够知道自己是谁,所以需要通过UDP绑定端口,参考代码里面支持IPV6,个人觉得可以过滤掉.WIN平台代码如下:

    1. //初始化socket
    2. m_soListen =(int)socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
    3. if (m_soListen == INVALID_SOCKET) {
    4. m_iErrorNo=WSAGetLastError();
    5. _dout(_T("CH31CarMonitorDlg Start Error(%d).\n"),m_iErrorNo);
    6. return -;
    7. }
    8. //初始化服务器地址
    9. SOCKADDR_IN addr;
    10. memset(&addr, , sizeof(addr));
    11. addr.sin_family = AF_INET;
    12. addr.sin_port = htons(port);
    13. addr.sin_addr.s_addr = htonl(INADDR_ANY);
    14. //绑定端口监听
    15. if (bind(m_soListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR) {
    16. m_iErrorNo=WSAGetLastError();
    17. _dout(_T("CH31CarMonitorDlg Start Error(%d).\n"),m_iErrorNo);
    18. return -;
    19. }

    UDP端口绑定

  2. DHT需要生成一个自己的20位ID号,当然可以通过随机一个数值,然后通过SHA1来生成20位的ID号,WIN平台代码如下:
    1. unsigned char p[];
    2. CSHA1 sha1;
    3. sha1.Reset();
    4. sha1.Update((const unsigned char *)m_myID.GetBuffer(), m_myID.GetLength());
    5. sha1.Final();
    6. sha1.GetHash(p);

    SHA1生成ID号

  3. 初始化他人服务器的IP信息,这样我们就可以从他们那里查询我们要的信息,借鉴代码如下:
    1. rc = getaddrinfo("router.utorrent.com","", &hints1, &info);
    2. //rc = getaddrinfo("router.bittorrent.com","6881", &hints1, &info);
    3. //rc = getaddrinfo("dht.transmissionbt.com","6881", &hints1, &info);
    4. if(rc != ) {
    5. fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rc));
    6. exit();
    7. }
    8. infop = info;
    9. while(infop&&m_bDataThread)
    10. {
    11. memcpy(&bootstrap_nodes[num_bootstrap_nodes],infop->ai_addr, infop->ai_addrlen);
    12. infop = infop->ai_next;
    13. num_bootstrap_nodes++;
    14. }
    15. freeaddrinfo(info);

    服务器信息

  4. 现在就可以初始化我们的DHT类了.由于此类使用C写的,大家可以自行封装成C++类使用.
    1. rc = m_dht.dht_init(s, s6, m_myid,NULL);
    2. if(rc < ) {
    3. perror("dht_init");
    4. exit();
    5. }

    初始化DHT类

  5. 对服务器进行PING操作,服务器就会回应PONG操作,这样就表明服务器活动正常.
    1. for(int i = ; i < num_bootstrap_nodes&&m_bDataThread; i++)
    2. {
    3. m_dht.dht_ping_node((struct sockaddr*)&bootstrap_nodes[i],sizeof(bootstrap_nodes[i]));
    4. Sleep(m_dht.random() % );
    5. }

    PING服务器

  6. 下面就可以使用搜索类进行操作,查询我们要的HASH值的BT种子文件代码.借鉴代码如下:
    1. if(searching) {
    2. if(s >= )
    3. dht_search(hash, , AF_INET, callback, NULL);
    4. if(s6 >= )
    5. dht_search(hash, , AF_INET6, callback, NULL);
    6. searching = ;
    7. }

    dht_search

  7. 大家可以借鉴dht-example.c里面接下来的Search函数的操作,不过我们不是这样来的,我们需要直接向服务器发送Findnode和Get_Peer操作.
    1. unsigned char tid[];
    2. m_dht.make_tid(tid, "fn", );
    3. m_dht.send_find_node(&ipRecvPingList[ipListPOS].fromaddr,sizeof(sockaddr),tid,,ipRecvPingList[ipListPOS].ID,,);
    4. Sleep();
    5. memset(tid,,sizeof(tid));
    6. m_dht.make_tid(tid, "gp", );
    7. m_dht.send_get_peers(&ipRecvPingList[ipListPOS].fromaddr,sizeof(sockaddr),tid,,hashList[],,);

    发送FINDNODE和GET_PEER操作

  8. 接下来的事情就是等待别人返回的信息进行分析就可以了,当然DHT类代码已经全部为我们做好的.
    1. FD_ZERO(&readfds);
    2. if(m_soListen >= )
    3. FD_SET(m_soListen, &readfds);
    4. if(s6 >= )
    5. FD_SET(s6, &readfds);
    6. rc = select(m_soListen > s6 ? m_soListen + : s6 + , &readfds, NULL, NULL, &tv);
    7. if(rc <&&m_bDataThread)
    8. {
    9. if(errno != EINTR) {
    10. perror("select");
    11. Sleep();
    12. }
    13. }
    14.  
    15. if(!m_bDataThread)
    16. break;
    17.  
    18. if(rc > &&m_bDataThread)
    19. {
    20. fromlen = sizeof(from1);
    21. memset(buf,,sizeof(buf));
    22. if(m_soListen >= && FD_ISSET(m_soListen, &readfds))
    23. rc = recvfrom(m_soListen, buf, sizeof(buf) - , ,&from1, &fromlen);
    24. else if(s6 >= && FD_ISSET(s6, &readfds))
    25. rc = recvfrom(s6, buf, sizeof(buf) - , ,&from1, &fromlen);
    26. else
    27. abort();
    28. }
    29.  
    30. if(rc > &&m_bDataThread)
    31. {
    32. buf[rc] = '\0';
    33. rc = m_dht.dht_periodic(buf, rc, &from1, fromlen,&tosleep, DHT_callback, this);
    34.  
    35. }
    36. else
    37. {
    38. rc = m_dht.dht_periodic(NULL, , NULL, , &tosleep, DHT_callback, this);
    39. }

    等待返回DHT网络信息

  9. 如何解析信息DHT代码已经有了,如何别人的请求,代码也已经有了,大家可以分析DHT.c就知道是怎么回事.
    1. int CDHT::dht_periodic(const void *buf, size_t buflen,const struct sockaddr *fromAddr, int fromlen,time_t *tosleep,dht_callback *callback, void *closure)
    2. {
    3. gettimeofday(&nowTime, NULL);
    4.  
    5. if(buflen > )
    6. {
    7. int message;
    8. unsigned char tid[], id[], info_hash[], target[];
    9. unsigned char nodes[], nodes6[], token[];
    10. int tid_len = , token_len = ;
    11. int nodes_len = , nodes6_len = ;
    12. unsigned short port;
    13. unsigned char values[], values6[];
    14. int values_len = , values6_len = ;
    15. int want;
    16. unsigned short ttid;
    17.  
    18. struct sockaddr_in* tempip=(struct sockaddr_in *)fromAddr;
    19.  
    20. if(is_martian(fromAddr))
    21. goto dontread;
    22.  
    23. if(node_blacklisted(fromAddr, fromlen)) {
    24. _dout("Received packet from blacklisted node.\n");
    25. goto dontread;
    26. }
    27.  
    28. if(((char*)buf)[buflen] != '\0') {
    29. _dout("Unterminated message.\n");
    30. errno = EINVAL;
    31. return -;
    32. }
    33.  
    34. message = parse_message((unsigned char *)buf, buflen, tid, &tid_len, id, info_hash,target, &port, token, &token_len,nodes, &nodes_len, nodes6, &nodes6_len,values, &values_len, values6, &values6_len,&want);
    35.  
    36. if(token_len>)
    37. {
    38. int a=;
    39. }
    40. if(message < || message == ERROR || id_cmp(id, zeroes) == )
    41. {
    42. _dout("Unparseable message: ");
    43. debug_printable((const unsigned char *)buf, buflen);
    44. _dout("\n");
    45. goto dontread;
    46. }
    47.  
    48. if(id_cmp(id, myid) == ) {
    49. _dout("Received message from self.\n");
    50. goto dontread;
    51. }
    52.  
    53. if(message > REPLY) {
    54. /* Rate limit requests. */
    55. if(!token_bucket()) {
    56. _dout("Dropping request due to rate limiting.\n");
    57. goto dontread;
    58. }
    59. }
    60.  
    61. switch(message)
    62. {
    63. case REPLY:
    64. if(tid_len != )
    65. {
    66. _dout("Broken node truncates transaction ids: ");
    67. debug_printable((const unsigned char *)buf, buflen);
    68. _dout("\n");
    69. /* This is really annoying, as it means that we will
    70. time-out all our searches that go through this node.
    71. Kill it. */
    72. blacklist_node(id, fromAddr, fromlen);
    73. goto dontread;
    74. }
    75. if(tid_match(tid, "pn", NULL))
    76. {
    77. _dout("Pong!From IP:%s:[%d] id:[%s]\n",inet_ntoa(tempip->sin_addr),tempip->sin_port,id);
    78. new_node(id, fromAddr, fromlen, );
    79. (*callback)(closure, DHT_EVENT_PONG_VALUES,id,(void*)fromAddr, fromlen);
    80. //send_find_node(from,fromlen,tid,4,id,0,0);
    81. }
    82. else if(tid_match(tid, "fn", NULL) ||tid_match(tid, "gp", NULL))
    83. {
    84. int gp = ;
    85. struct search *sr = NULL;
    86. if(tid_match(tid, "gp", &ttid))
    87. {
    88. gp = ;
    89. sr = find_search(ttid, fromAddr->sa_family);
    90. }
    91. _dout("Nodes found (%d+%d)%s!From IP:%s:[%d]\n", nodes_len/, nodes6_len/,gp ? " for get_peers" : "",inet_ntoa(tempip->sin_addr),tempip->sin_port);
    92. if(nodes_len % != || nodes6_len % != )
    93. {
    94. _dout("Unexpected length for node info!\n");
    95. blacklist_node(id, fromAddr, fromlen);
    96. }
    97. //else if(gp && sr == NULL)
    98. //{
    99. // _dout("Unknown search!\n");
    100. // new_node(id, fromAddr, fromlen, 1);
    101. // }
    102. else
    103. {
    104. int i;
    105. new_node(id, fromAddr, fromlen, );
    106. for(i = ; i < nodes_len / ; i++)
    107. {
    108. unsigned char *ni = nodes + i * ;
    109. struct sockaddr_in sin;
    110. if(id_cmp(ni, myid) == )
    111. continue;
    112. memset(&sin, , sizeof(sin));
    113. sin.sin_family = AF_INET;
    114. memcpy(&sin.sin_addr, ni + , );
    115. memcpy(&sin.sin_port, ni + , );
    116. new_node(ni, (struct sockaddr*)&sin, sizeof(sin), );
    117. (*callback)(closure, DHT_EVENT_FINDNODE_VALUES, ni,(void*)&sin, sizeof(sin));
    118. if(sr && sr->af == AF_INET)
    119. {
    120. insert_search_node(ni,(struct sockaddr*)&sin,sizeof(sin),sr, , NULL, );
    121. }
    122. //send_get_peers((struct sockaddr*)&sin,sizeof(sockaddr),tid,4,ni,0,0);
    123. }
    124. for(i = ; i < nodes6_len / ; i++)
    125. {
    126. unsigned char *ni = nodes6 + i * ;
    127. struct sockaddr_in6 sinip6;
    128. if(id_cmp(ni, myid) == )
    129. continue;
    130. memset(&sinip6, , sizeof(sinip6));
    131. sinip6.sin6_family = AF_INET6;
    132. memcpy(&sinip6.sin6_addr, ni + , );
    133. memcpy(&sinip6.sin6_port, ni + , );
    134. new_node(ni, (struct sockaddr*)&sinip6, sizeof(sinip6), );
    135. if(sr && sr->af == AF_INET6)
    136. {
    137. insert_search_node(ni,(struct sockaddr*)&sinip6,sizeof(sinip6),sr, , NULL, );
    138. }
    139. }
    140. if(sr)
    141. /* Since we received a reply, the number of requests in flight has decreased. Let's push another request. */
    142. search_send_get_peers(sr, NULL);
    143. }
    144. //if(sr)
    145. {
    146. // insert_search_node(id, fromAddr, fromlen, sr,1, token, token_len);
    147. if(values_len > || values6_len > )
    148. {
    149. _dout("Got values (%d+%d)!\n", values_len / , values6_len / );
    150. if(callback) {
    151. if(values_len > )
    152. (*callback)(closure, DHT_EVENT_VALUES, sr->id,(void*)values, values_len);
    153.  
    154. if(values6_len > )
    155. (*callback)(closure, DHT_EVENT_VALUES6, sr->id,(void*)values6, values6_len);
    156. }
    157. }
    158. }
    159. }
    160. else if(tid_match(tid, "ap", &ttid))
    161. {
    162. struct search *sr;
    163. _dout("Got reply to announce_peer.\n");
    164. sr = find_search(ttid, fromAddr->sa_family);
    165. if(!sr) {
    166. _dout("Unknown search!\n");
    167. new_node(id, fromAddr, fromlen, );
    168. }
    169. else
    170. {
    171. int i;
    172. new_node(id, fromAddr, fromlen, );
    173. for(i = ; i < sr->numnodes; i++)
    174. {
    175. if(id_cmp(sr->nodes[i].id, id) == )
    176. {
    177. sr->nodes[i].request_time = ;
    178. sr->nodes[i].reply_time = nowTime.tv_sec;
    179. sr->nodes[i].acked = ;
    180. sr->nodes[i].pinged = ;
    181. break;
    182. }
    183. }
    184. /* See comment for gp above. */
    185. search_send_get_peers(sr, NULL);
    186. }
    187. }
    188. else
    189. {
    190. _dout("Unexpected reply: ");
    191. debug_printable((const unsigned char *)buf, buflen);
    192. _dout("\n");
    193. }
    194. break;
    195. case PING:
    196. _dout("Ping (%d)!From IP:%s:%d\n", tid_len,inet_ntoa(tempip->sin_addr),tempip->sin_port);
    197. new_node(id, fromAddr, fromlen, );
    198. _dout("Sending pong.\n");
    199. send_pong(fromAddr, fromlen, tid, tid_len);
    200. break;
    201. case FIND_NODE:
    202. _dout("Find node!From IP:%s:%d\n",inet_ntoa(tempip->sin_addr),tempip->sin_port);
    203. new_node(id, fromAddr, fromlen, );
    204. _dout("Sending closest nodes (%d).\n", want);
    205. send_closest_nodes(fromAddr, fromlen,tid, tid_len, target, want,, NULL, NULL, );
    206. break;
    207. case GET_PEERS:
    208. _dout("Get_peers!From IP:%s:%d\n",inet_ntoa(tempip->sin_addr),tempip->sin_port);
    209. new_node(id, fromAddr, fromlen, );
    210. if(id_cmp(info_hash, zeroes) == )
    211. {
    212. _dout("Eek! Got get_peers with no info_hash.\n");
    213. send_error(fromAddr, fromlen, tid, tid_len,, "Get_peers with no info_hash");
    214. break;
    215. }
    216. else
    217. {
    218. struct storage *st = find_storage(info_hash);
    219. unsigned char token[TOKEN_SIZE];
    220. make_token(fromAddr, , token);
    221. if(st && st->numpeers > )
    222. {
    223. _dout("Sending found%s peers.\n",fromAddr->sa_family == AF_INET6 ? " IPv6" : "");
    224. send_closest_nodes(fromAddr, fromlen,tid, tid_len,info_hash, want,fromAddr->sa_family, st,token, TOKEN_SIZE);
    225. }
    226. else
    227. {
    228. _dout("Sending nodes for get_peers.\n");
    229. send_closest_nodes(fromAddr, fromlen,tid, tid_len, info_hash, want,, NULL, token, TOKEN_SIZE);
    230. }
    231. if(callback)
    232. {
    233. (*callback)(closure, DHT_EVENT_GET_PEER_VALUES, info_hash,(void *)fromAddr, fromlen);
    234. }
    235. }
    236.  
    237. break;
    238. case ANNOUNCE_PEER:
    239. _dout("Announce peer!From IP:%s:%d\n",inet_ntoa(tempip->sin_addr),tempip->sin_port);
    240. new_node(id, fromAddr, fromlen, );
    241.  
    242. if(id_cmp(info_hash, zeroes) == )
    243. {
    244. _dout("Announce_peer with no info_hash.\n");
    245. send_error(fromAddr, fromlen, tid, tid_len,, "Announce_peer with no info_hash");
    246. break;
    247. }
    248. if(!token_match(token, token_len, fromAddr)) {
    249. _dout("Incorrect token for announce_peer.\n");
    250. send_error(fromAddr, fromlen, tid, tid_len,, "Announce_peer with wrong token");
    251. break;
    252. }
    253. if(port == ) {
    254. _dout("Announce_peer with forbidden port %d.\n", port);
    255. send_error(fromAddr, fromlen, tid, tid_len,, "Announce_peer with forbidden port number");
    256. break;
    257. }
    258. if(callback)
    259. {
    260. (*callback)(closure, DHT_EVENT_ANNOUNCE_PEER_VALUES, info_hash,(void *)fromAddr, fromlen);
    261. }
    262. storage_store(info_hash, fromAddr, port);
    263. /* Note that if storage_store failed, we lie to the requestor.
    264. This is to prevent them from backtracking, and hence polluting the DHT. */
    265. _dout("Sending peer announced.\n");
    266. send_peer_announced(fromAddr, fromlen, tid, tid_len);
    267. }
    268. }
    269.  
    270. dontread:
    271. if(nowTime.tv_sec >= rotate_secrets_time)
    272. rotate_secrets();
    273.  
    274. if(nowTime.tv_sec >= expire_stuff_time) {
    275. expire_buckets(buckets);
    276. expire_buckets(buckets6);
    277. expire_storage();
    278. expire_searches();
    279. }
    280.  
    281. if(search_time > && nowTime.tv_sec >= search_time) {
    282. struct search *sr;
    283. sr = searches;
    284. while(sr) {
    285. if(!sr->done && sr->step_time + <= nowTime.tv_sec)
    286. {
    287. search_step(sr, callback, closure);
    288. }
    289. sr = sr->next;
    290. }
    291.  
    292. search_time = ;
    293.  
    294. sr = searches;
    295. while(sr) {
    296. if(!sr->done) {
    297. time_t tm = sr->step_time + + random() % ;
    298. if(search_time == || search_time > tm)
    299. search_time = tm;
    300. }
    301. sr = sr->next;
    302. }
    303. }
    304.  
    305. if(nowTime.tv_sec >= confirm_nodes_time) {
    306. int soon = ;
    307.  
    308. soon |= bucket_maintenance(AF_INET);
    309. soon |= bucket_maintenance(AF_INET6);
    310.  
    311. if(!soon)
    312. {
    313. if(mybucket_grow_time >= nowTime.tv_sec - )
    314. soon |= neighbourhood_maintenance(AF_INET);
    315. if(mybucket6_grow_time >= nowTime.tv_sec - )
    316. soon |= neighbourhood_maintenance(AF_INET6);
    317. }
    318.  
    319. /* In order to maintain all buckets' age within 600 seconds, worst
    320. case is roughly 27 seconds, assuming the table is 22 bits deep.
    321. We want to keep a margin for neighborhood maintenance, so keep
    322. this within 25 seconds. */
    323. if(soon)
    324. confirm_nodes_time = nowTime.tv_sec + + random() % ;
    325. else
    326. confirm_nodes_time = nowTime.tv_sec + + random() % ;
    327. }
    328.  
    329. if(confirm_nodes_time > nowTime.tv_sec)
    330. *tosleep = confirm_nodes_time - nowTime.tv_sec;
    331. else
    332. *tosleep = ;
    333.  
    334. if(search_time > ) {
    335. if(search_time <= nowTime.tv_sec)
    336. *tosleep = ;
    337. else if(*tosleep > search_time - nowTime.tv_sec)
    338. *tosleep = search_time - nowTime.tv_sec;
    339. }
    340.  
    341. return ;
    342. }

    dht_periodic

  10. 至于节点如何进行桶操作,调试过一次代码就会明白对应的原理,当然上面也介绍了如何进行桶分裂的原理.
  11. 接下来就是将上面的操作步骤进行循环.

通过上面的流程,了解DHT的工作方法后,如何增加更多的返回信息就需要下一篇的技术性问题的介绍,希望大家一起修改我们的开源程序.

大家有不明白的地方,可以一起讨论.

 

大家的推荐才是下一篇介绍的动力...

[搜片神器]之DHT网络爬虫的代码实现方法的更多相关文章

  1. [搜片神器]之DHT网络爬虫的C++程序初步开源

    回应大家的要求,特地整理了一开始自己整合的代码,这样最简单,最直接的可以分析流程,至于文章里面提供的程序界面更多,需要大家自己开发. 谢谢园子朋友的支持,已经找到个VPS进行测试,国外的服务器: ht ...

  2. [搜片神器]直接从DHT网络下载BT种子的方法

    DHT抓取程序开源地址:https://github.com/h31h31/H31DHTDEMO 数据处理程序开源地址:https://github.com/h31h31/H31DHTMgr DHT系 ...

  3. [C#搜片神器] 之P2P中DHT网络爬虫原理

    继续接着上一篇写:使用C#实现DHT磁力搜索的BT种子后端管理程序+数据库设计(开源)[搜片神器] 昨天由于开源的时候没有注意运行环境,直接没有考虑下载BT种子文件时生成子文件夹,可能导致有的朋友运行 ...

  4. python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息,抓取政府网新闻内容

    python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI,采用Python语言编写 ...

  5. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

  6. [搜片神器]winform程序自己如何更新自己的方法代码

    DHT抓取程序开源地址:https://github.com/h31h31/H31DHTDEMO 数据处理程序开源地址:https://github.com/h31h31/H31DHTMgr 国外测试 ...

  7. java编写的一段简单的网络爬虫demo代码

    功能: 从网站上下载附件,并从页面中提取页面文章内容 关于NIO 在大多数情况下,Java 应用程序并非真的受着 I/O 的束缚.操作系统并非不能快速传送数据,让 Java 有事可做:相反,是 JVM ...

  8. 网络爬虫的C++程序

    [搜片神器]之DHT网络爬虫的C++程序初步开源 回应大家的要求,特地整理了一开始自己整合的代码,这样最简单,最直接的可以分析流程,至于文章里面提供的程序界面更多,需要大家自己开发. 谢谢园子朋友的支 ...

  9. [搜片神器]使用C#实现DHT磁力搜索的BT种子后端管理程序+数据库设计(开源)

    谢谢园子朋友的支持,已经找到个VPS进行测试,国外的服务器:http://www.sosobta.com   大家可以给提点意见... 出售商业网站代码,万元起,非诚勿扰,谢谢. 联系h31h31 a ...

随机推荐

  1. myeclipse 8.6 安装svn插件

    第一种:在线安装 1.打开HELP->MyEclipse Configuration Center,切换到SoftWare标签页. 2.点击Add Site 打开对话框,在对话框Name输入Sv ...

  2. 编译FreePascal和Lazarus

    一,准备目录假设我们准备将整个FreePascal环境安装到D盘的fpc目录下,那么我们需要创建以下几个目录:d:\fpc_svn\bind:\laz_svn二,准备环境1,安装SVN客户端Torto ...

  3. How to configure Spring facet in IntelliJ IDEA

    遇到了这个问题,稀里糊涂的就给搞定了,在stackoverfolw上看到了相同的问题,直接拷贝下来吧 Spring Configuration Check Unmapped Spring config ...

  4. [转]设置控件全局显示样式appearance proxy

    转自:huifeidexin_1的专栏 appearance是apple在iOS5.0上加的一个协议,它让程序员可以很轻松地改变某控件的全局样式(背景) @selector(appearance) 支 ...

  5. 监听Activity进入后台(最小化),并根据时间判断是否超时,此解决办法可用于超时重登陆

    通过重写一个继承自Activity的基类中的onUserLeaveHint()方法,当用户按Home键等操作使程序进入后台时即开始计时,当用户使程序恢复为前台显示时执行onResume()方法,在其中 ...

  6. 【笔记】一些linux实用函数技巧【原创】

    函数返回的是函数的地址 kallsyms_lookup_name()

  7. 四大开源协议:BSD、Apache、GPL、LGPL

    参考文献:http://www.fsf.org/licensing/licenses/ 现今存在的开源协议很多,而经过Open Source Initiative组织通过批准的开源协议目前有58种.我 ...

  8. IE下easyui 缓存问题

    $.ajaxSetup ({   cache: false //关闭AJAX相应的缓存 }); 这一句话就足够了,很管用!

  9. android 安装 出现Android Native Development Tools不能安装

    Software being installed: Android Native Development Tools 20.0.0.v201206242043-391819 (com.android. ...

  10. UVa 1395 Slim Span【最小生成树】

    题意:给出n个节点的图,求最大边减最小边尽量小的值的生成树 首先将边排序,然后枚举边的区间,判定在该区间内是否n个点连通,如果已经连通了,则构成一颗生成树, 则此时的苗条度是这个区间内最小的(和kru ...