java基于t-io框架实现区块链中的p2p网络构建模拟区块信息同步

向彪-技术总监,区块链应用架构师


前言

上次我们用java实现了默克树,这次我们用java基于t-io框架实现区块链中的p2p网络构建,实现通信的功能,当然,实现p2p也可以基于WebSocket!下次我们再来实现一下。

区块链 P2P闲聊


  P2P Peer-to-Peer ,最早起源于 1997 年, otlin ommunication 公司研制了能让用户从别人电脑中下载内容的软件,这便是最早 P2P 当时P2P 网络一词 的定义也与此相关,即网络是一类允许 组用户互相连接并直接从用户硬盘上获取文件的 网络 Hotlin 曾一度成 P2P 网络的代名词,曾有文章 以“ tlin- The Gl01y Days Of P2P 为题介绍当时的盛况随着互联网技术的发展 P2P 演化成了一种分布式网络 在分布式网络 ,网络的各个节点,无论是机构还是个人可以共享们所拥有的 部分软 硬件资源如数据处理能力、信息存储能力、互联网连接能力、打印机等。 P2P 分布式网络这些共享资源能被其他对 节点( peer )直接访问,无须 过中间的服务器在区块链领域,不同的公链系统所使用的 P2P网络技术大不相同。国内迅雷发布的区块链文件系统 TCFS 是基于 IPFS 实现的,比特币系统的 P2P 络是无结构的,而以太坊的 P2P 络是有结构的。在以太坊中, P2P 网络主要采用 Kademlia 算法实现, Kademlia 是分布式散列表( Distributed Hash Table, DHT )技术的一种。借助该技术,以太坊系统实现了在分布式环境下快速准确地路由和定位数据。分布式散列表是 个由广域网内大 节点共同维护的巨大散列表。这张散列表被分割成不连续的块,每个节点被分配给 个属于自己的散列块,并成为这个散列块的管理者。 DHT 的节点是动态的,在节点中,通过加密散列函数,对象的名字或关键词就可以被映射为 128 位或 160 位的散列值。目前, Tapest可、 Chord CANPas 都使用了分布式散列表技术当然,不同结构的P2P 网络,有不同的优点和缺点。比特币系统的网络结构是最容易理解、最容易实现的种形式,而以太坊网络引入了分布式散表、异或距离、二叉前缀树等,结构上更复杂,实现起来也更困难。

一、t-io是什么?

在这里插入图片描述
常见应用场景:

IM(官方提供了im例子,含web端)
实时监控
推送服务(已内置API)
RPC
游戏
物联网(已有很多案例)
其它实时通讯类型的场景,不一一列举
t-io是一个网络框架,从这一点来说是有点像 netty 的,但 t-io 为常见和网络相关的业务(如 IM、消息推送、RPC、监控)提供了近乎于现成的解决方案,即丰富的编程 API,极大减少业务层的编程难度
官方地址(有兴趣的可以研究一下):https://www.tiocloud.com/2/product/tio.html
提示:目前t-io的文档已经收费!但是对于学习能力强的小伙伴看看demo就能明白,能简单的上手。

二、使用步骤

1.引入库

maven依赖:

<!-- https://mvnrepository.com/artifact/org.t-io/tio-core -->
<dependency>
    <groupId>org.t-io</groupId>
    <artifactId>tio-core</artifactId>
    <version>3.7.0.v20201010-RELEASE</version>
</dependency>

2.服务端代码

在这里插入图片描述
图片来自:https://blog.csdn.net/qwerdfgg/article/details/106017467?biz_id=102&spm=1018.2118.3001.4449

代码如下:

package org.xiangbiao.p2p.server;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.Node;
import org.tio.core.Tio;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.intf.Packet;
import org.tio.server.intf.ServerAioHandler;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;

/**
 * MyServerAioHandler
 * @author larry.xiang
 */
public class MyServerAioHandler implements ServerAioHandler {
    
    


    private static final Logger logger = LoggerFactory.getLogger(MyServerAioHandler.class);

    public Packet decode(ByteBuffer byteBuffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
    
    
        if (readableLength < ServerPacket.PACKET_HEADER_LENGTH) {
    
    
            return null;
        }

        int bodyLength = byteBuffer.getInt();

        if (bodyLength < 0) {
    
    
            throw new AioDecodeException("body length[" + bodyLength + "] is invalid. romote: " + channelContext.getServerNode());
        }

        int len = bodyLength + ServerPacket.PACKET_HEADER_LENGTH;
        if (len > readableLength) {
    
    
            return null;
        } else {
    
    
            byte[] bytes = new byte[len];
            int i = 0;
            while(true){
    
    

                if(byteBuffer.remaining() == 0){
    
    
                    break;
                }
                byte b = byteBuffer.get();
                bytes[i++] = b;
            }

            ServerPacket serverPacket = new ServerPacket();
            serverPacket.setBody(bytes);
            return serverPacket;
        }
    }

    public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext) {
    
    
        ServerPacket serverPacket = (ServerPacket) packet;

        byte[] body = serverPacket.getBody();

        int bodyLength = 0;
        if(body != null){
    
    
            bodyLength = body.length;
        }

        ByteBuffer byteBuffer = ByteBuffer.allocate(bodyLength + ServerPacket.PACKET_HEADER_LENGTH);
        byteBuffer.order(groupContext.getByteOrder());
        byteBuffer.putInt(bodyLength);

        if (body != null) {
    
    
            byteBuffer.put(body);
        }
        String bodyStr = null;
        try {
    
    
            bodyStr = new String(body, "utf-8");
        } catch (UnsupportedEncodingException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("bodyStr2:"+bodyStr);
        return byteBuffer;
    }

    public void handler(Packet packet, ChannelContext channelContext) throws Exception {
    
    
        channelContext.setServerNode(new Node("127.0.0.1", ServerPacket.PORT));
        ServerPacket serverPacket = (ServerPacket) packet;
        byte[] body = serverPacket.getBody();
        if(body != null){
    
    
            String bodyStr = new String(body, "utf-8");
            ServerPacket serverPacket1 = new ServerPacket();
            serverPacket1.setBody(("receive from ["+ channelContext.getClientNode() + "]: " + bodyStr).getBytes("utf-8"));
            Tio.send(channelContext, serverPacket1);
        }
    }
}
package org.xiangbiao.p2p.server;


import org.tio.core.ChannelContext;
import org.tio.core.intf.Packet;
import org.tio.server.intf.ServerAioListener;

/**
 * MyServerAioListener
 * @author larry.xiang
 */
public class MyServerAioListener implements ServerAioListener {
    
    
    public void onAfterConnected(ChannelContext channelContext, boolean b, boolean b1) throws Exception {
    
    

    }

    public void onAfterDecoded(ChannelContext channelContext, Packet packet, int i) throws Exception {
    
    

    }

    public void onAfterReceivedBytes(ChannelContext channelContext, int i) throws Exception {
    
    

    }

    public void onAfterSent(ChannelContext channelContext, Packet packet, boolean b) throws Exception {
    
    

    }

    public void onAfterHandled(ChannelContext channelContext, Packet packet, long l) throws Exception {
    
    

    }

    public void onBeforeClose(ChannelContext channelContext, Throwable throwable, String s, boolean b) throws Exception {
    
    

    }
}
package org.xiangbiao.p2p.server;


import org.tio.core.intf.Packet;

/**
 * 服务包实体
 * @author larry.xiang
 */
public class ServerPacket extends Packet {
    
    

    public static final Integer PACKET_HEADER_LENGTH = 4;
    public static final Integer PORT = 8999;

    byte[] body;//数据

    public byte[] getBody() {
    
    
        return body;
    }

    public void setBody(byte[] body) {
    
    
        this.body = body;
    }
}
package org.xiangbiao.p2p.server;


import org.tio.core.Tio;
import org.tio.server.ServerGroupContext;
import org.tio.server.TioServer;

import java.io.IOException;

/**
 * 服务端启动类
 * @author larry.xiang
 */
public class TestServer {
    
    

    public static void main(String[] args) throws IOException {
    
    
        ServerGroupContext serverGroupContext = new ServerGroupContext("tio-blockchain", new MyServerAioHandler(), new MyServerAioListener());
        TioServer server = new TioServer(serverGroupContext);
        server.start("127.0.0.1", 8999);
    }
}

3.客户端代码

在这里插入图片描述
图片来自:https://blog.csdn.net/qwerdfgg/article/details/106017467?biz_id=102&spm=1018.2118.3001.4449
代码如下:

package org.xiangbiao.p2p.client;


import org.tio.core.intf.Packet;

/**
 * 客户端包实体
 * @author larry.xiang
 */
public class ClientPacket extends Packet {
    
    

    public static final Integer PACKET_HEADER_LENGTH = 4;

    private byte[] body;

    public byte[] getBody() {
    
    
        return body;
    }

    public void setBody(byte[] body) {
    
    
        this.body = body;
    }
}
package org.xiangbiao.p2p.client;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.client.intf.ClientAioHandler;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.intf.Packet;

import java.nio.ByteBuffer;

/**
 * MyClientAioHander
 * @author larry.xiang
 */
public class MyClientAioHander implements ClientAioHandler {
    
    

    Logger logger = LoggerFactory.getLogger(MyClientAioHander.class);

    @Override
    public Packet heartbeatPacket() {
    
    
        return new ClientPacket();
    }

    @Override
    public Packet decode(ByteBuffer byteBuffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
    
    

        if(readableLength < ClientPacket.PACKET_HEADER_LENGTH){
    
    
            return null;
        }

        int bodyLength = byteBuffer.getInt();
        if(bodyLength < 0){
    
    
            throw new AioDecodeException("body length is invalid.romote: " + channelContext.getServerNode());
        }

        int usefulLength = ClientPacket.PACKET_HEADER_LENGTH + bodyLength;

        if(usefulLength > readableLength){
    
    
            return null;
        }else {
    
    
            ClientPacket packet = new ClientPacket();
            byte[] body = new byte[bodyLength];
            byteBuffer.get(body);
            packet.setBody(body);

            return packet;
        }
    }

    @Override
    public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext) {
    
    

        ClientPacket clientPacket = (ClientPacket) packet;
        byte[] body = clientPacket.getBody();

        int bodyLength = 0;

        if(body != null){
    
    
            bodyLength = body.length;
        }

        int len = ClientPacket.PACKET_HEADER_LENGTH + bodyLength;

        ByteBuffer byteBuffer = ByteBuffer.allocate(len);
        byteBuffer.order(groupContext.getByteOrder());
        byteBuffer.putInt(bodyLength);

        if(body != null){
    
    
            byteBuffer.put(body);
        }

        return byteBuffer;
    }

    @Override
    public void handler(Packet packet, ChannelContext channelContext) throws Exception {
    
    

        ClientPacket clientPacket = (ClientPacket) packet;
        byte[] body = clientPacket.getBody();
        if(body != null){
    
    
            String bodyStr = new String(body, "utf-8");
            logger.debug("客户端收到消息: " + bodyStr);
        }
    }
}
package org.xiangbiao.p2p.client;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.client.intf.ClientAioListener;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.core.intf.Packet;

/**
 * MyClientAioListener
 * @author larry.xiang
 */
public class MyClientAioListener implements ClientAioListener {
    
    
    Logger logger = LoggerFactory.getLogger(MyClientAioListener.class);
    private static Integer count = 0;

    @Override
    public void onAfterConnected(ChannelContext channelContext, boolean b, boolean b1) throws Exception {
    
    
        logger.info("onAfterConnected!");
    }

    @Override
    public void onAfterDecoded(ChannelContext channelContext, Packet packet, int i) throws Exception {
    
    
        logger.info("onAfterDecoded...");
    }

    @Override
    public void onAfterReceivedBytes(ChannelContext channelContext, int i) throws Exception {
    
    
        logger.info("onAfterReceivedBytes-------------------" + i);
    }

    @Override
    public void onAfterSent(ChannelContext channelContext, Packet packet, boolean b) throws Exception {
    
    
        logger.info("onAfterSent...");

    }

    @Override
    public void onAfterHandled(ChannelContext channelContext, Packet packet, long l) throws Exception {
    
    
        System.out.println("onAfterHandled...");

        ClientPacket clientPacket = (ClientPacket) packet;
        String resData = new String(clientPacket.getBody(), "utf-8");

        logger.info("[" + channelContext.getServerNode() + "]: " + resData);

        count++;

        ((ClientPacket) packet).setBody(("[" + channelContext.getServerNode() + "]: " + count).getBytes());

        Thread.sleep(5000);

        Tio.send(channelContext, packet);
    }

    @Override
    public void onBeforeClose(ChannelContext channelContext, Throwable throwable, String s, boolean b) throws Exception {
    
    

        logger.error(throwable.getMessage());
        logger.info(s);

    }
}
package org.xiangbiao.p2p.client;


import org.tio.client.ClientChannelContext;
import org.tio.client.ClientGroupContext;
import org.tio.client.TioClient;
import org.tio.core.Node;
import org.tio.core.Tio;

/**
 *客户端测试
 * @author larry.xiang
 */
public class TestClient
{
    
    
    public static void main( String[] args ) throws Exception {
    
    

        ClientGroupContext clientGroupContext = new ClientGroupContext(new MyClientAioHander(), new MyClientAioListener());

        TioClient tioClient = new TioClient(clientGroupContext);

        ClientChannelContext clientChannelContext = tioClient.connect(new Node("127.0.0.1", 8999));

        ClientPacket clientPacket = new ClientPacket();

        // 模拟区块信息同步
        String blockStr="{blockHash: 0x16872b8dc1039c4e2ccc114cc1bfecb6fffe011904f2ccc66ac154a1629c331c\\n\" +\"blockNumber: 0x25\\n\" +\n" +
                "                \"gas: 0x5f5e100\\n\" +\n" +
                "                \"from: 0x8dbfde6445d39de6b84b5f5c445be00f8fb755cf\\n\" +\n" +
                "                \"transactionIndex: 0x0\\n\" +\n" +
                "                \"to: 0xef860c28e70e59d1f775a24b38b90bde1b317ad2\\n\" +\n" +
                "                \"nonce: 0xbcd86aae2291d3dac96b02f4834e6b2db218f91e9022d7022c7fc7a679d67f\\n\" +\n" +
                "                \"value: 0x0\\n\" +\n" +
                "                \"hash: 0xdb8234eeabd623cf15fd3193e79b1b7bb169c986b2ea193c6736dfdb0a914790\\n\" +\n" +
                "                \"gasPrice: 0x1\\n\" + \"input:xxxxxxlarry }\"";

        clientPacket.setBody(blockStr.getBytes("utf-8"));

        Tio.send(clientChannelContext, clientPacket);
    }
} 

测试结果如下:
在这里插入图片描述
可以拿到模拟的区块信息


总结

这次我们用java基于t-io框架实现区块链中的p2p网络构建,实现通信的功能,初步模拟了区块信息同步的demo,但是在实现的底层中远远比我们的要复杂,但是其原理不变。

猜你喜欢

转载自blog.csdn.net/ws327443752/article/details/109284604