import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
public class TestSwingTree extends JFrame {
private JPanel p;
public TestSwingTree(String title){
super(title);
}
public void init(){
Container c = this.getContentPane();
DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("child1");
DefaultMutableTreeNode child11 = new DefaultMutableTreeNode("child11");
DefaultMutableTreeNode child12 = new DefaultMutableTreeNode("child12");
DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("child2");
DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("child3");
DefaultMutableTreeNode child31 = new DefaultMutableTreeNode("child31");
root.add(child1);
root.add(child2);
root.add(child3);
child1.add(child11);
child1.add(child12);
child3.add(child31);
JTree tree = new JTree(root);
tree.setPreferredSize(new Dimension(120, 400));
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
p.removeAll();
JLabel l = new JLabel(e.getPath().toString());
l.setBounds(5, 190, 170, 20);
p.add(l);
p.repaint();
}
});
c.add(tree,BorderLayout.WEST);
p = new JPanel();
p.setLayout(null);
p.setPreferredSize(new Dimension(180, 400));
c.add(p,BorderLayout.CENTER);
this.setLocation(400, 300);
this.setSize(300, 400);
this.setResizable(false);
this.setVisible(true);
this.setDefaultCloseOperation(this.DISPOSE_ON_CLOSE);
}
public static void main(String[] args) {
new TestSwingTree("Test Swing Jtree").init();
}
}
節點發現功能主要涉及 Server \ Table \ udp 這幾個數據結構,它們有獨自的事件響應循環,節點發現功能便是它們互相協作完成的。其中,每個以太坊客戶端啟動后都會在本地運行一個 Server ,并將網絡拓撲中相鄰的節點視為 Node ,而 Table 是 Node 的容器, udp 則是負責維持底層的連接。下面重點描述它們中重要的字段和事件循環處理的關鍵部分。
PrivateKey - 本節點的私鑰,用于與其他節點建立時的握手協商
Proto買粉絲ls - 支持的所有上層協議
StaticNodes - 預設的靜態 Peer ,節點啟動時會首先去向它們發起連接,建立鄰居關系
newTransport - 下層傳輸層實現,定義握手過程中的數據加密解密方式,默認的傳輸層實現是用 newRLPX() 創建的 rlpx ,這不是本文的重點
ntab - 典型實現是 Table ,所有 peer 以 Node 的形式存放在 Table
ourHandshake - 與其他節點建立連接時的握手信息,包含本地節點的版本號以及支持的上層協議
addpeer - 連接握手完成后,連接過程通過這個通道通知 Server
Server 的監聽循環,啟動底層監聽socket,當收到連接請求時,Accept后調用 setupConn() 開始連接建立過程
Server的主要事件處理和功能實現循環
Node 唯一表示網絡上的一個節點
IP - IP地址
UDP/TCP - 連接使用的UDP/TCP端口號
ID - 以太坊網絡中唯一標識一個節點,本質上是一個橢圓曲線公鑰(PublicKey),與 Server 的 PrivateKey 對應。一個節點的IP地址不一定是固定的,但ID是唯一的。
sha - 用于節點間的距離計算
Table 主要用來管理與本節點與其他節點的連接的建立\更新\刪除
bucket - 所有 peer 按與本節點的距離遠近放在不同的桶(bucket)中,詳見之后的 節點維護
refreshReq - 更新 Table 請求通道
Table 的主要事件循環,主要負責控制 refresh 和 revalidate 過程。
refresh.C - 定時(30s)啟動Peer刷新過程的定時器
refreshReq - 接收其他線程投遞到 Table 的 刷新Peer連接 的通知,當收到該通知時啟動更新,詳見之后的 更新鄰居關系
revalidate.C - 定時重新檢查以連接節點的有效性的定時器,詳見之后的 探活檢測
udp 負責節點間通信的底層消息控制,是 Table 運行的 Kademlia 協議的底層組件
買粉絲nn - 底層監聽端口的連接
addpending - udp 用來接收 pending 的channel。使用場景為:當我們向其他節點發送數據包后(packet)后可能會期待收到它的回復,pending用來記錄一次這種還沒有到來的回復。舉個例子,當我們發送ping包時,總是期待對方回復pong包。這時就可以將構造一個pending結構,其中包含期待接收的pong包的信息以及對應的callback函數,將這個pengding投遞到udp的這個channel。 udp 在收到匹配的pong后,執行預設的callback。
gotreply - udp 用來接收其他節點回復的通道,配合上面的addpending,收到回復后,遍歷已有的pending鏈表,看是否有匹配的pending。
Table - 和 Server 中的ntab是同一個 Table
udp 的處理循環,負責控制消息的向上遞交和收發控制
udp 的底層接受數據包循環,負責接收其他節點的 packet
以太坊使用 Kademlia 分布式路由存儲協議來進行網絡拓撲維護,了解該協議建議先閱讀 易懂分布式 。更權威的資料可以查看 wiki 。總的來說該協議:
源碼中由 Table 結構保存所有 bucket , bucket 結構如下
節點可以在 entries 和 replacement
2024-06-29 19:49
2024-06-29 19:46
2024-06-29 19:04
2024-06-29 18:48
2024-06-29 18:31
2024-06-29 18:30