在上一篇文章 : 编程小白模拟简易区块链系统(七)中,我们基本上实现了单个节点的所有功能,接下来我们介绍下去中心化的P2P网络,并基于Spring boot为大家演示下demo。
请大家继续往下看👇
相关概念
P2P
peer-to-peer,点对点网络,没有一个中心化的服务器,每个处于网络中的节点既扮演了server的角色,同时又是client,各个节点地位相等,再向他人提供服务的同时,自己也可以向别人请求服务。在我们这个系统中,假设共有ABC三台机器充当P2P网络的节点,那么对于节点ABC来说每一个都可以进行挖矿,或者交易操作。共有以下几个场景需要用到P2P网络的相关内容:
- 假设A要发起交易,那么A需要把交易广播给其他节点,让BC知道,不广播的后果就是,其他人在进行记账操作时无法得知这一笔发生在网络中的交易。
- 假设ABC同时在挖矿,A的性能比较好,先于其他节点挖矿,那么A需要把新产生的区块广播告知给其他节点,其他节点收到广播后决定是否来更新自己的区块链。因为每个节点都有一份账本,大家需要维护账本的统一,以此来监督其他的交易,防止一个UTXO在不同节点被多次消费。
- 连接A节点的用户生成了一个新的钱包,同样需要广播公钥告知其他节点,否则连接其他节点的用户如果要验证这个新产生钱包的交易,没有公钥无法验证。
- 假设ABC在区块链系统中已经存在一段时间,此是D作为新节点想要加入到这个系统,那么D需要同步消息为:当前的区块链,当前的交易池,当前的所有钱包的公钥,三者缺一不可。
Spring boot 中集成 WebSocket
首先需要引入Spring boot websocket 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
新建配置类WebSocketConfig.java
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
新建服务端P2pServer.java
@ServerEndpoint("/test")
@Component
public class P2pServer {
/**
* 存放所有已经建立的连接
*/
private static List<Session> clients = new ArrayList<>();
/**
* 客户端建立连接后
* @param session 客户端
*/
@OnOpen
public void onOpen(Session session) {
System.out.println("有新的客户端连接,id为:"+ session.getId());
clients.add(session);
}
/**
* 客户端断开连接
* @param session 客户端
*/
@OnClose
public void onClose(Session session) {
System.out.println("有用户断开了, id为:"+session.getId());
clients.remove(session);
}
/**
* 发生错误
* @param throwable
*/
@OnError
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
/**
* 收到客户端发来消息
* @param msg 消息内容
*/
@OnMessage
public void onMessage(String msg) {
System.out.println("服务端收到客户端发来的消息:"+msg);
this.broadcast(msg);
}
/**
* 广播消息
* @param msg 消息内容
*/
private void broadcast(String msg) {
for (Session session : clients) {
send(session,msg);
}
}
/**
* 向某个客户端发送消息
* @param session 客户端
* @param msg 消息内容
*/
public void send(Session session, String msg) {
session.getAsyncRemote().sendText(msg);
}
}
新建客户端P2pClient.java
@Component
@ClientEndpoint
public class P2pClient {
/**
* 客户端
*/
private Session session;
/**
* 连接服务端
* @param url 服务端的IP和端口号
*/
public void connectServer(String url) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
URI uri = URI.create(url);
this.session = container.connectToServer(P2pClient.class, uri);
} catch (DeploymentException | IOException e) {
e.printStackTrace();
}
}
/**
* 打开连接
* @param session
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
}
/**
* 服务端的消息
* @param msg 消息内容
*/
@OnMessage
public void onMessage(String msg) {
}
/**
* 发生错误
* @param throwable
*/
@OnError
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
/**
* 断开与服务端的连接
*/
@OnClose
public void onClosing() throws IOException {
session.close();
}
/**
* 向服务端发送消息
* @param msg 消息内容
*/
public void send(String msg) {
session.getAsyncRemote().sendText(msg);
}
}
为了调试的简单,我们不使用application.properties
文件来配置,而是使用同一个项目,简单修改了启动类Hello.java
,在本地新建了两个节点,用来设置到不同的端口,模拟两个节点通讯。
- 节点1(Hello)的详细配置
![](https://i-blog.csdnimg.cn/blog_migrate/4aa56ead7633c8d63a5f4f63b21a3f79.png)
//节点1(Hello)的启动类
//当作P2P网络中的第一个节点
@SpringBootApplication(scanBasePackages = {"com"})
@RestController
public class Hello {
public static void main(String[] args) {
System.out.println("Hello world");
SpringApplication.run(Hello.class,args);
}
}
- 节点2(Test)的详细配置,设置为本地8091端口,连接Hello,并向Hello发送消息
![](https://i-blog.csdnimg.cn/blog_migrate/64ded025c6a707e81769eadb56a9c091.png)
//节点2(Test)的启动类
//当作P2P网络中的第二个节点
//连接Hello,并向Hello发送消息
@SpringBootApplication(scanBasePackages = {"com"})
@RestController
public class Hello {
public static void main(String[] args) {
System.out.println("Hello world");
String url = "ws://localhost:"+args[0]+"/test";
P2pClient p2pClient = new P2pClient();
p2pClient.connectServer(url);
p2pClient.send("The client msg");
SpringApplication.run(Hello.class,args);
}
}
启动节点Hello,修改Hello.java
,启动节点Test,可以看到Hello节点成功接收到了来自Test的消息。
![](https://i-blog.csdnimg.cn/blog_migrate/efed6f74f47eec4b5fbedd703de62f97.png)
构建成功,接下来让我们把这个简单的P2Pdemo应用到我们的区块链系统中吧!
欢迎去看下一篇文章:编程小白模拟简易区块链系统(九)
这里关于websocket就不介绍太多了,感兴趣的朋友可以看下,参考资料:
SpringBoot2.0集成WebSocket,实现后台向前端推送信息