`<?php // 文件路徑 define('ABSPATH', dirname(FILE)); // 主進程數, 通常爲CPU的1至4倍 define('WORKER_NUM', 2); // 容許最大鏈接數, 不可大於系統ulimit -n的值 define('MAX_REQUEST', 1000); // 線程數 define('MAX_PROCESS', 10); // 自動查找間隔, 單位爲毫秒 define('AUTO_FIND_TIME', 10000); // 發送find_node間隔, 單位秒 define('NEXT_FIND_NODE_TIME', 0.5);php
// 載入類文件 require_once ABSPATH . '/inc/Node.class.php'; require_once ABSPATH . '/inc/Bencode.class.php'; require_once ABSPATH .'/inc/Base.class.php'; require_once ABSPATH .'/inc/Socket.class.php';node
// 保存swoole_server對象 $serv = NULL; // 設置自身node id $nid = Base::get_node_id(); //echo $nid; // 初始化路由器 $table = array(); // 最後請求時間 $last_find = time(); // 保存線程列表 $threads = array(); //$threads = []; // 長期在線node $bootstrap_nodes = array( // array('dht.transmissionbt.com', 6881), // array('router.bittorrent.com', 6881), // array('router.utorrent.com', 6881) array('160.120.47.92',6829) );bootstrap
write(date('Y-m-d H:i:s', time()) . " - 服務啓動...\n");服務器
//$serv = new swoole_server('0.0.0.0', 6882, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); $socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ); function myswool_server(){ global $nid, $table; global $socket; if ( $socket === false ) { echo "socket_create() failed:reason:" . socket_strerror( socket_last_error() ) . "\n"; } $ok = socket_bind( $socket, '0.0.0.0', 6882 ); if ( $ok === false ) { echo "socket_bind() failed:reason:" . socket_strerror( socket_last_error( $socket ) ); } // auto_find_node(); while ( true ) { auto_find_node(); $from = ""; $port = 0; $len = socket_recvfrom( $socket, $buf,1024, 0, $from, $port ); // echo ' socket_recvfrom len:'.$len.' \n '; $len = recv( $buf,$from, $port); get_peers(array($from, $port), $nid); // echo ' recv len:'.$len.' \r\n '; usleep( 1000 ); // auto_find_node(); } } function recv( $data,$from, $port){ // 檢查數據長度 if(strlen($data) == 0) return false;swoole
// 對數據進行解碼 $msg = Base::decode($data); // 獲取對端連接信息, udp連接須要加上$from_id參數
// $fdinfo = $serv->connection_info($fd, $from_id); $fdinfo['remote_ip']=$from; $fdinfo['remote_port']=$port; // print_r($msg); // echo $from.' ['.$port.'] '.$msg['y']." \n "; // 對接收到的數據進行類型判斷 if($msg['y'] == 'r'){ // 若是是回覆, 且包含nodes信息 if(array_key_exists('nodes', $msg['r'])) // 對nodes進行操做 response_action($msg, array($fdinfo['remote_ip'], $fdinfo['remote_port'])); if(array_key_exists('token', $msg['r'])) on_get_peers($msg, $fdinfo); // print_r($msg); }elseif($msg['y'] == 'q'){ echo 'request_action:'.$from.' ['.$port.'] '.$msg['y'].' '; // 若是是請求, 則執行請求判斷 request_action($msg, array($fdinfo['remote_ip'], $fdinfo['remote_port'])); }else{ return false; } } myswool_server(); exit; $serv = new Socket(); $serv->set(array( 'worker_num' => WORKER_NUM, 'daemonize' => true, 'max_request' => MAX_REQUEST, 'dispatch_mode' => 2, 'log_file' => ABSPATH . 'error.log' )); $serv->on('WorkerStart', function($serv, $worker_id){ // 添加一個定時器, 使服務器定時尋找節點 $serv->addtimer(AUTO_FIND_TIME); auto_find_node(); }); $serv->on('Receive', function($serv, $fd, $from_id, $data){ // 檢查數據長度 if(strlen($data) == 0) return false;網絡
// 對數據進行解碼 $msg = Base::decode($data); // 獲取對端連接信息, udp連接須要加上$from_id參數 $fdinfo = $serv->connection_info($fd, $from_id); // 對接收到的數據進行類型判斷 if($msg['y'] == 'r'){ // 若是是回覆, 且包含nodes信息 if(array_key_exists('nodes', $msg['r'])) // 對nodes進行操做 response_action($msg, array($fdinfo['remote_ip'], $fdinfo['remote_port'])); }elseif($msg['y'] == 'q'){ // 若是是請求, 則執行請求判斷 request_action($msg, array($fdinfo['remote_ip'], $fdinfo['remote_port'])); }else{ return false; }
}); $serv->on('Timer', function($interval){ for($i=0; $i<MAX_PROCESS; $i++){ $process = new swoole_process(function(){ auto_find_node(); }); $pid = $process->start(); $threads[$pid] = $process; swoole_process::wait(); } }); $serv->start();併發
/**app
/**socket
/**ui
發送find_node請求
@param array $address 對端連接信息
@param string $id node id
@return void */ function find_node($address, $id = null){ global $nid, $table;
// 若未指定id則使用自身node id if(is_null($id)) $mid = $nid; else // 不然僞造一個相鄰id $mid = Base::get_neighbor($id, $nid);
// 定義發送數據 $msg = array( 't' => Base::entropy(2), 'y' => 'q', 'q' => 'find_node', 'a' => array( 'id' => $nid, 'target' => $mid ) ); // print_r($msg); // 發送請求數據到對端 send_response($msg, $address); }
function get_peers($address, $id = null){ global $nid, $table;
// 定義發送數據 $msg = array( 't' => Base::entropy(2), 'y' => 'q', 'q' => 'get_peers', 'a' => array( 'id' => $nid, 'info_hash' => "mnopqrstuvwxyz123456" ) );
// print_r($msg); // 發送請求數據到對端 send_response($msg, $address); } /**
/**
處理接收到的find_node回覆
@param array $msg 接收到的數據
@param array $address 對端連接信息
@return void */ function response_action($msg, $address){ // 先檢查接收到的信息是否正確 if(!isset($msg['r']['nodes']) || !isset($msg['r']['nodes'][1])) return false; // print_r( $msg);
// 對nodes數據進行解碼 $nodes = Base::decode_nodes($msg['r']['nodes']);
// print_r( $nodes); // 對nodes循環處理 foreach($nodes as $node){ // 將node加入到路由表中 append($node); } }
/**
處理ping請求
@param array $msg 接收到的ping請求數據
@param array $address 對端連接信息
@return void */ function on_ping($msg, $address){ global $nid; echo "on_ping\n"; // 獲取對端node id $id = $msg['a']['id']; // 生成回覆數據 $msg = array( 't' => $msg['t'], 'y' => 'r', 'r' => array( 'id' => $nid ) );
// 將node加入路由表 append(new Node($id, $address[0], $address[1])); // 發送回覆數據 send_response($msg, $address); }
/**
處理find_node請求
@param array $msg 接收到的find_node請求數據
@param array $address 對端連接信息
@return void */ function on_find_node($msg, $address){ global $nid;
// 獲取node列表 $nodes = get_nodes(16); // 獲取對端node id $id = $msg['a']['id']; // 生成回覆數據 $msg = array( 't' => $msg['t'], 'y' => 'r', 'r' => array( 'id' => $nid, 'nodes' => Base::encode_nodes($nodes) ) );
// 將node加入路由表 append(new Node($id, $address[0], $address[1])); // 發送回覆數據 send_response($msg, $address); }
/**
處理get_peers請求
@param array $msg 接收到的get_peers請求數據
@param array $address 對端連接信息
@return void */ function on_get_peers($msg, $address){ global $nid; echo "on_get_peers\n";
// 獲取info_hash信息 $infohash = $msg['a']['info_hash']; // 獲取node id $id = $msg['a']['id'];
// 生成回覆數據 $msg = array( 't' => $msg['t'], 'y' => 'r', 'r' => array( 'id' => $nid, 'nodes' => Base::encode_nodes(get_nodes()), 'token' => substr($infohash, 0, 2) ) );
// 將node加入路由表 append(new Node($id, $address[0], $address[1])); // 向對端發送回覆數據 send_response($msg, $address); }
/**
處理announce_peer請求
@param array $msg 接收到的announce_peer請求數據
@param array $address 對端連接信息
@return void */ function on_announce_peer($msg, $address){ global $nid; echo "on_announce_peer\n";
// 獲取infohash $infohash = $msg['a']['info_hash']; // 獲取token $token = $msg['a']['token']; // 獲取node id $id = $msg['a']['id'];
// 驗證token是否正確 if(substr($infohash, 0, 2) == $token){ /$txt = array( 'action' => 'announce_peer', 'msg' => array( 'ip' => $address[0], 'port1' => $address[1], 'port2' => $msg['a']['port'], 'infohash' => $infohash ) ); var_dump($txt);/ write(date('Y-m-d H:i:s', time()) . " 獲取到info_hash: " . strtoupper(bin2hex($infohash)) . "\n"); }
// 生成回覆數據 $msg = array( 't' => $msg['t'], 'y' => 'r', 'r' => array( 'id' => $nid ) ); /* $msg = array( 't' => $msg['t'], 'y' => "q", 'q' => "find_node", 'a' => array('id' => $nid, 'target' => Base::get_node_id()) );//同樣能夠 */ // 發送請求回覆 send_response($msg, $address); }
/**
)
/**
添加node到路由表
@param Node $node node模型
@return boolean 是否添加成功 */ function append($node){ global $nid, $table;
// 檢查node id是否正確 if(!isset($node->nid[19])) return false;
// 檢查是否爲自身node id if($node->nid == $nid) return false;
// 檢查node是否已存在 if(in_array($node, $table)) return false;
// 若是路由表中的項達到200時, 刪除第一項 if(count($table) >= 200) array_shift($table); else{ echo count($table).']'.$node->ip.':'.$node->port."\n"; // get_peers(array($node->ip, $node->port), $node->nid); // find_node(array($node->ip, $node->port), $node->nid); } // print_r($node);
return array_push($table, $node); }
function get_nodes($len = 8){ global $table;
if(count($table) <= $len) return $table; $nodes = array(); for($i=0; $i<$len; $i++){ $nodes[] = $table[mt_rand(0, count($table) - 1)]; } return $nodes;
}
/**