Java 構建p2p網絡
這篇文章是一篇關於pbft算法實現的一篇補充文章,但是在這里不會涉及pbft的算法方面,所以可以當做一篇單獨的文章食用。如果想查看關於區塊鏈或者PBFT算法的文章,可以參考一下我的文章。
文章實現范圍
這篇文章是構建一個p2p網絡,需要擁有獨立的ip地址(當然如果是在局域網內使用就無所謂了),不會涉及到內網穿透的一些實現方法,也就是說如果你是把自己電腦構建了一個p2p結點,則其他人的電腦是無法鏈接你電腦的結點的。
預備知識
p2p網絡中,一個結點既是客戶端也是服務端,可以實現任意時刻的雙向的通信。因此在每個結點中,會維護一張客戶端的表和一張服務端的表。我們可以從代碼的方面來理解一下這個東西,如下圖所示:

兩個被方框圈住的表則是需要維護的表,如果在代碼中的話,我們就可以使用List去保存這張表
客戶端的表我們可以用來發送消息,而服務端的表我們可以用來控制群發。
這里我們可以解釋和理解一下何為p2p。舉例:
結點1想與結點2進行通信時:A1跑出來說,我有到結點2的通道,然后向結點2發出request
,結點2的服務端接收到結點1(A1)發出來的消息的時,進行response。
這個時候,可能就會有人問:為什么不是結點1的服務端向結點2發出request呢,然后結點2再向服務端返回response?yes,這個是可以的,並且能夠成功。那是不是意味着客戶端的表實際上可以不要?當然不是!!!
首先我們從哲學的角度理解這個東西,request理所當然應該是client發出來的,response也應當是response發出來的。當然,這個完全是扯蛋。在兩個結點中,一個當做server一個當做client,這樣確實不會出現問題,當時如果是3個節點呢?這樣做能不能實現兩兩單獨
發送呢?很抱歉,不能。(注意,在這里是兩個結點直接進行request和response,而不借助其他結點)。
下面這種模式是一個結點只為Server,另外的幾個也只為Client。在這種模式中,C1和其他的結點無法獨自進行兩兩通信,必須借助Server來進行通信。
OK,說了這么多,那么現在讓我們來進行構建P2P網絡
Java構建項目
在這里,我將使用maven構建項目,下面是需要使用的工具類:
- t-io:進行Socket通信。當然,t-io不是必須的,使用websocket或者netty都是可以的
項目地址:github
代碼其實沒什么好講的,主要是不熟悉tio的使用,這里,emm,吐槽一下tio,媽耶,文檔也太貴了吧(╮(╯▽╰)╭,學生優惠沒有了,哭唧唧)。

代碼解釋
代碼其實很簡單,就是開上幾個server,然后client連接Server就行了。
主要代碼如下所示(實際上使用什么框架都行,自己喜歡就行,把思路弄好就可以了):
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String ip = "127.0.0.1";
// 服務端開始
System.out.println("請輸入服務端初始化端口:");
int port = in.nextInt();
// 處理消息handler
ServerAioHandler handler = new P2PServerAioHandler();
// 監聽
ServerAioListener listener = new ServerListener();
// 配置
ServerTioConfig config = new ServerTioConfig("服務端", handler, listener);
// 設置timeout
config.setHeartbeatTimeout(Const.TIMEOUT *2);
TioServer tioServer = new TioServer(config);
try {
// 啟動
tioServer.start(ip, port);
} catch (IOException e) {
System.out.println("啟動錯誤:" + e.getMessage());
}
// client開始
ClientChannelContext[] contexts = new ClientChannelContext[3];
for (int i = 0; i < 3; i++) {
// client的handler
ClientAioHandler clientAioHandler = new P2pClientAioHandler();
// client 的配置
ClientTioConfig clientTioConfig = new ClientTioConfig(clientAioHandler, new P2PClientLinstener(),new ReconnConf(Const.TIMEOUT));
clientTioConfig.setHeartbeatTimeout(Const.TIMEOUT);
ClientChannelContext context;
try {
TioClient client = new TioClient(clientTioConfig);
System.out.println("輸入端口:");
int serverPort = in.nextInt();
context = client.connect(new Node(ip, serverPort), Const.TIMEOUT);
contexts[i] = context;
} catch (Exception e) {
System.out.println("客戶端啟動錯誤:" + e.getMessage());
}
}
while (true) {
System.out.println("請輸入發送的服務端的index");
int index = in.nextInt();
System.out.println("請輸入發送的內容");
String body = in.next();
try {
MsgPacket msgPacket = new MsgPacket();
msgPacket.setBody("測試數據".getBytes(MsgPacket.CHARSET));
Tio.send(contexts[index], msgPacket);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
思路:
思路還是蠻簡單的,先進行server創建,並對他進行配置(配置它的消息處理方式和一些超時屬性等等),在tio中,server會在后台(也就是P2PServerAioHandler這個類)接受並處理消息。然后是配置client,同樣配置一些Handler和timeout等等。在這里我使用了ClientChannelContext[] contexts = new ClientChannelContext[3]
來保存client。ClientChannelContext可以理解為client到server的一根管道,通過他我們可以來控制消息的發送。
項目地址:GitHub