已经看完第一章的内容了吗,欢迎回来。

上一章我们介绍了关于怎么去编写自己的区块链,完成哈希和新块的校验。但是它只是在一个终端(结点)上跑。我们怎么样来连接其他结点以及贡献新的块呢,怎么样广播到其他结点告诉他们要更新区块了呢?

本章就是要告诉你这些。

// 区块链的核心部分
// 维护一个在启动时可以连接的对等节点列表。当一个完整的节点第一次启动时,它必须被自举(bootstrapped)到网络。
// 自举过程完成后,节点向其对等节点发送一个包含其自身IP地址的addr消息。其对等的每个节点向它们自己的对等节点转发这个信息,以便进一步扩大连接池。
// 块广播
// 在与对等节点建立连接后,双方互发包含最新块哈希值的getblocks消息。
// 如果某个节点坚信其拥有最新的块信息或者有更长的链,它将发送一个inv消息(invite),其中包含至多500个最新块的哈希值,以此来表明它的链更长。
// 收到的节点使用getdata来请求块的详细信息,而远程的节点通过命令block来发送这些信息。
// 在500个块的信息被处理完之后,节点可以通过getblocks请求更多的块信息。这些块在被接收节点认证之后得到确认。
// 新块的确认也可通过矿工挖到并发布的块来发现。其扩散过程和上述类似。
// 通过之前的连接,新块以inv消息发布出去,而接收节点可以通过getdata请求这些块的详细信息。

我们会做什么

1.建立第一个结点,作为tcp server监听

2.打开一个终端,连接到第一个结点上来,模拟新产生区块

3.结点将新的区块链以广播的形式传播到所有结点

我们不会做什么

与上一篇文章一样,本教程的目的是模拟节点网络,以便您可以直观的认识区块链网络。所以不会像真实的区块链网络一样去实现所有功能,对以上功能会进行简化。

让我们开始编码吧!

除了上一章讲过的区块内容,我们将去掉http的部分,今天讲的重点是TCP构建网络

TCP 和 HTTP 有什么差别?

这里我们不会详细讨论,但是您需要知道的是TCP是一个传输数据的基本协议。HTTP建立在TCP之上,以在web和浏览器上使用此数据传输。当您查看一个网站时,您使用的是HTTP,它是由一个名为TCP的底层数据传输协议支持的。

在本教程中,我们将使用TCP,因为我们不需要在浏览器中查看任何内容。

我们创建一个Node.java的文件,来开始我们的编写。

Imports

包声明我们需要的导入。

import java.net.ServerSocket;
import java.net.Socket;

Review

我们的块,以及块相关操作的内容都没有变,索性我们把这些内容放在一起,新建BlockUtils.java文件,内容如下:

public class BlockUtils {
/**
* 计算区块的hash值
*
* @param block
* 区块
* @return
*/
public static String calculateHash(Block block) {
String record = (block.getIndex()) + block.getTimestamp() + (block.getVac()) + block.getPrevHash();
MessageDigest digest = DigestUtils.getSha256Digest();
byte[] hash = digest.digest(StringUtils.getBytesUtf8(record));
return Hex.encodeHexString(hash);
} /**
* 区块的生成
*
* @param oldBlock
* @param vac
* @return
*/
public static Block generateBlock(Block oldBlock, int vac) {
Block newBlock = new Block();
newBlock.setIndex(oldBlock.getIndex() + 1);
newBlock.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
newBlock.setVac(vac);
newBlock.setPrevHash(oldBlock.getHash());
newBlock.setHash(calculateHash(newBlock));
return newBlock;
} /**
* 校验区块的合法性(有效性)
*
* @param newBlock
* @param oldBlock
* @return
*/
public static boolean isBlockValid(Block newBlock, Block oldBlock) {
if (oldBlock.getIndex() + 1 != newBlock.getIndex()) {
return false;
}
if (!oldBlock.getHash().equals(newBlock.getPrevHash())) {
return false;
}
if (!calculateHash(newBlock).equals(newBlock.getHash())) {
return false;
}
return true;
}
}

好了!我们已经基本得到了所有的区块链相关函数,从第1章删除了所有HTTP相关的内容。我们现在可以进行联网了。

网络

在此之前,让我们声明一个名为bcServer的全局变量(blockchainServer的简称),它是接收传入块的队列。

我们从main函数开始,我们需要把结点作为TCP server启动,监听端口8333。

当网络中有链接进来时,我们接收链接,并开线程处理它。这样如果有很多结点连接过来,每个结点通讯都可以并行处理。

接着,我们模拟广播到全网。
       // 建立TCP监听8333
serverSocket = new ServerSocket(8333); LOGGER.info("*** Node is started,waiting for others ***");
// 监听对等网络中的结点
for(;;) {
final Socket socket = serverSocket.accept();
// 创建一个新的线程 ,和建立连接的结点通讯
new NodeThread(socket, blockChain).start(); // 模拟网络结点广播
new BroadcastThread(socket, blockChain).start();
}

让我们来写NodeThread的实现。当新的结点连上来,我们发出一条信息“请输入一个资产值”,新结点输入一个数值,创建一个新的块,发送回链,我们将这个新的块加入到链上。

代码如下

     BufferedReader br = null;
PrintWriter pw = null;
try {
//提示结点输入
pw = new PrintWriter(socket.getOutputStream());
pw.write("please enter a new number(vac):\n");
pw.flush();
String info = null; // 读取结点发送的信息
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while ((info = br.readLine()) != null) {
try {
int vac = Integer.parseInt(info);
// 根据vac创建区块
Block newBlock = BlockUtils.generateBlock(blockChain.get(blockChain.size() - 1), vac);
if (BlockUtils.isBlockValid(newBlock, blockChain.get(blockChain.size() - 1))) {
blockChain.add(newBlock);
pw.write("Success!\n");
pw.write(gson.toJson(blockChain));
} else {
pw.write("HTTP 500: Invalid Block Error\n");
}
LOGGER.info("add new block with vac:" + vac);
} catch (Exception e) {
LOGGER.error("not a number:", e);
pw.write("not a number! \n");
}
pw.write("Please enter a new number(vac):" + "\n");
// 调用flush()方法将缓冲输出
pw.flush();
} } catch (Exception e) {
LOGGER.error("TCP i/o error Or client closed", e);
} finally {
LOGGER.info("node closed:" + address.getHostAddress() + ",port:" + socket.getPort());
// 关闭资源
try {
if (pw != null) {
pw.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
LOGGER.error("close error:", e);
}
}
 

模拟广播

我们需要把得到的新链告诉所有链接到我们TCPServer的结点,因此,我们将模拟数据如何传输到所有的其他结点。

如何做呢,设置一个时间,定时把blockchain用json化写入连接,通知所有连接结点。

代码如下:

for (;;) {
PrintWriter pw = null;
try {
Thread.sleep(30000);
LOGGER.info("\n------------broadcast-------------\n");
LOGGER.info(gson.toJson(blockChain));
pw = new PrintWriter(socket.getOutputStream());
// 发送到其他结点
pw.write("------------broadcast-------------\n");
pw.write(gson.toJson(blockChain));
pw.flush();
} catch (InterruptedException e) {
LOGGER.error("error:", e);
} catch (IOException e) {
LOGGER.error("error:", e);
}
}

可以到我的github查看完整的代码

https://github.com/Mignet/blockchain

实验步骤:

启动第一个结点.Node

打开一个终端,执行命令nc localhost 8333

打开更多终端,执行命令nc localhost 8333

在任意终端输入数字,看链的打印

每过30秒,看广播到的链。

如果你有真正的网络环境,想象一下,你的任意一台设备作为结点,都可能是第一个结点,它们直接通过tcp互相通讯,广播,是不是也一样类似上面的过程呢。

下一章,我们将谈一谈工作量证明算法(Proof Of Work)。

只用120行Java代码写一个自己的区块链-2网络的更多相关文章

  1. 只用120行Java代码写一个自己的区块链-3挖矿算法

    在本系列前两篇文章中,我们向大家展示了如何通过精炼的Java代码实现一个简单的区块链.包括生成块,验证块数据,广播通信等等,这一篇让我们聚焦在如何实现 PoW算法. 大家都无不惊呼比特币.以太坊及其他 ...

  2. 只用120行Java代码写一个自己的区块链

    区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Java 语言来实现一个简单的区块链,用不到 120 行代码来揭示区 ...

  3. 只用120行Java代码写一个自己的区块链-4实现真正的p2p网络

    在之前的文章中,我们模拟了节点网络通讯,很多朋友反馈说,他们想看真正的节点网络通讯而不是单节点的模拟.本章将满足你们.

  4. 只用200行Go代码写一个自己的区块链!

    Coral Health · 大约23小时之前 · 220 次点击 · 预计阅读时间 7 分钟 · 不到1分钟之前 开始浏览 区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大 ...

  5. 只用200行Go代码写一个自己的区块链!(转)

    区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Go 语言来实现一个简单的区块链,用不到 200 行代码来揭示区块链 ...

  6. 用java代码写一个简单的网上购物车程序

    需求:1.写一个商品类,有商品编号.商品名称.商品分类.商品单价属性.2.写一个商品条目信息类,有商品和数量两个属性,有商品总价格方法. 3.写一个购物车类,有添加商品方法.查看订单信息,删除商品,修 ...

  7. 如何用java代码写一个堆栈

    public class Stack { int[] data; int maxSize; int top; public Stack(int maxSize) { this.maxSize = ma ...

  8. 用 Python 构建一个极小的区块链

    虽然有些人认为区块链是一个早晚会出现问题的解决方案,但是毫无疑问,这个创新技术是一个计算机技术上的奇迹.那么,究竟什么是区块链呢? 区块链 以比特币(Bitcoin)或其它加密货币按时间顺序公开地记录 ...

  9. 如何用70行Java代码实现深度神经网络算法

    http://www.tuicool.com/articles/MfYjQfV 如何用70行Java代码实现深度神经网络算法 时间 2016-02-18 10:46:17  ITeye 原文  htt ...

随机推荐

  1. 【WebService】——CXF整合Spring

    相关博客: [WebService]--入门实例 [WebService]--SOAP.WSDL和UDDI 前言: 之前的几篇博客基本上都是使用jdk来实现WebService的调用,没有使用任何框架 ...

  2. phpquery中文手册

    [简介] phpQuery是一个基于PHP的服务端开源项目,它可以让PHP开发人员轻松处理DOM文档内容.更有意思的是,它采用了jQuery的思想,使得可以像使用jQuery一样处理页面内容,获取想要 ...

  3. 当xml结构很深时候 可以通过父节点删除子元素

    当xml结构很深时候 可以通过父节点删除子元素

  4. 2018宁夏邀请赛K Vertex Covers

    题目链接:https://nanti.jisuanke.com/t/28411 题意: 给出n(n<=36)个点的一个图.求点覆盖集数. 题解: 将n个点折半为L和R两部分.对于R内部的边,枚举 ...

  5. 【POJ 1201 Intervals】

    Time Limit: 2000MSMeamory Limit: 65536K Total Submissions: 27949Accepted: 10764 Description You are ...

  6. OpenJudge百炼-2747-数字方格-C语言-枚举

    描述:如上图,有3个方格,每个方格里面都有一个整数a1,a2,a3.已知0 <= a1, a2, a3 <= n,而且a1 + a2是2的倍数,a2 + a3是3的倍数, a1 + a2 ...

  7. [hdu 2102]bfs+注意INF

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2102 感觉这个题非常水,结果一直WA,最后发现居然是0x3f3f3f3f不够大导致的……把INF改成I ...

  8. 牛客多校对抗第6场 A Singing Contest

    [20分]标题:A.Singing Contest | 时间限制:1秒 | 内存限制:256MJigglypuff is holding a singing contest. There are 2n ...

  9. Step-By-Step: Setting up Active Directory in Windows Server 2016

    There are interesting new features now made available in Windows Server 2016 such as time based grou ...

  10. PHP 扒一扒这些题目都考了哪些知识点

    1.模除 题目: <?php echo -10%3; *结果* -1 分析:其实这道题的知识点是在考模除和正负号的关系,那么我们看一段进阶的代码 <?php echo "10%3 ...