一、唠叨两句

首先,我们都知道区块链是去中心化的,其中节点都是对等节点,每个节点都几乎有完整的区块链特性,CLI就是NEO的一个命令行对等节点,当然也有GUI这个项目,图形化的NEO节点。节点之间需要通信,互通有无,我们今天主要看看这部分。

二、从入口开始

CLI是一个Console程序,那我们就从它的Main开始把。

static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
new MainService().Run(args);
}

Main

这里除了注册一个未捕获异常的处理事件之外,就是这个MainService的Run方法了。MainService继承自ConsoleServiceBase,我们看下ConsoleServiceBase的代码吧,看一眼你就明白这个Service是在搞什么东东了

using System;
using System.Reflection;
using System.Security;
using System.Text; namespace Neo.Services
{
public abstract class ConsoleServiceBase
{
protected virtual string Prompt => "service"; public abstract string ServiceName { get; } protected bool ShowPrompt { get; set; } = true; protected virtual bool OnCommand(string[] args)
{
switch (args[].ToLower())
{
case "clear":
Console.Clear();
return true;
case "exit":
return false;
case "version":
Console.WriteLine(Assembly.GetEntryAssembly().GetName().Version);
return true;
default:
Console.WriteLine("error");
return true;
}
} protected internal abstract void OnStart(string[] args); protected internal abstract void OnStop(); public void Run(string[] args)
{
OnStart(args);
RunConsole();
OnStop();
} private void RunConsole()
{
bool running = true;
#if NET461
Console.Title = ServiceName;
#endif
Console.OutputEncoding = Encoding.Unicode; Console.ForegroundColor = ConsoleColor.DarkGreen;
Version ver = Assembly.GetEntryAssembly().GetName().Version;
Console.WriteLine($"{ServiceName} Version: {ver}");
Console.WriteLine(); while (running)
{
if (ShowPrompt)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write($"{Prompt}> ");
} Console.ForegroundColor = ConsoleColor.Yellow;
string line = Console.ReadLine().Trim();
Console.ForegroundColor = ConsoleColor.White; string[] args = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (args.Length == )
continue;
try
{
running = OnCommand(args);
}
catch (Exception ex)
{
#if DEBUG
Console.WriteLine($"error: {ex.Message}");
#else
Console.WriteLine("error");
#endif
}
} Console.ResetColor();
}
}
}

ConsoleServiceBase

MainService肯定重写了方法OnStart,OnStop,里面干啥了后面再讲,除此之外,主要方法RunConsole里就是等输入命令,然后根据命令进行相应操作,然后循环继续。可以创建个钱包啊,创建个地址啊,blabla的。OnCommand里实现了一些界面上的操作,那MainService类里一定就是侦听CLI可以支持的各种指令咯,具体每个指令做了什么操作顺着这个线索看代码即可了解,后面我们会选几个关键的指令溜一下代码,现在我们就只关注这个OnStart,OnStop都干啥了。

protected internal override void OnStart(string[] args)
{
Blockchain.RegisterBlockchain(new LevelDBBlockchain(Settings.Default.Paths.Chain));
if (!args.Contains("--nopeers") && File.Exists(PeerStatePath))
using (FileStream fs = new FileStream(PeerStatePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
LocalNode.LoadState(fs);
}
LocalNode = new LocalNode();
Task.Run(() =>
{
const string acc_path = "chain.acc";
const string acc_zip_path = acc_path + ".zip";
if (File.Exists(acc_path))
{
using (FileStream fs = new FileStream(acc_path, FileMode.Open, FileAccess.Read, FileShare.None))
{
ImportBlocks(fs);
}
File.Delete(acc_path);
}
else if (File.Exists(acc_zip_path))
{
using (FileStream fs = new FileStream(acc_zip_path, FileMode.Open, FileAccess.Read, FileShare.None))
using (ZipArchive zip = new ZipArchive(fs, ZipArchiveMode.Read))
using (Stream zs = zip.GetEntry(acc_path).Open())
{
ImportBlocks(zs);
}
File.Delete(acc_zip_path);
}
LocalNode.Start(Settings.Default.P2P.Port, Settings.Default.P2P.WsPort);
bool recordNotifications = false;
for (int i = ; i < args.Length; i++)
{
switch (args[i])
{
case "/rpc":
case "--rpc":
case "-r":
if (rpc == null)
{
rpc = new RpcServerWithWallet(LocalNode);
rpc.Start(Settings.Default.RPC.Port, Settings.Default.RPC.SslCert, Settings.Default.RPC.SslCertPassword);
}
break;
case "--record-notifications":
recordNotifications = true;
break;
}
}
if (recordNotifications)
Blockchain.Notify += Blockchain_Notify;
});
}

MainService.OnStart

我们看到OnStart方法里做了如下的事情:

1. 注册LevelDBBlockChain作为本地的链的存储

2. 正常启动需要与其他节点通讯的情况,会先找一个叫做peers.dat的文件。这个文件里存储的是其他对等节点的地址,读取的到地址存储在LocalNode类的静态属性UnconnectedPeers里,拿到这些节点地址可以建立与其的通信。

3. 新建了一个LocalNode实例,这个实例比较关键了,作为本地节点的通讯模块,与之对应的是RemoteNode,每个已连接上的对等节点都会有一个对应的RemoteNode实例,保存在LocalNode实例的connectedPeers列表里。

4. 查看是否有chain.acc或者chain.acc.zip文件,这个文件是链的存储文件,如果不想同步节点,可以使用这个文件作为离线文件启动,免去同步数据的时间。

5. 调用LocalNode.Start方法,该方法里做了啥?

a. 启动两个线程connectThread和poolThread,这两个线程都干啥呢?

I. connectThread: 如果前面讲述的unconnectedPeers列表里还有未连接的节点,就创建对应的任务去连接。如果没有未连接的节点了,就从connectedPeers列表里拿出来已连接的RemoteNode节点,发送一个getaddr消息。如果两个列表都是空,就拿出系统默认的feed节点去连接。

在连接节点的时候,会创建一个RemoteNode实例,添加到connectedPeers列表里,并调用RemoteNode的StartProtocal,这个方法里表达出的是两个对等节点建立连接的过程:

给对方发送Version消息

接收到Version消息,就发送VerAck消息

接收VerAck消息,根据Version消息中的信息判断是不是对方持有更新的数据,如果是就发送getheaders和getblocks消息获取最新的数据

II. poolThread: 首先先说下两个数据结构temp_pool和mem_pool,都是存储交易的,temp_pool存储的是最近接收到的交易,mem_pool是在内存中存储所有未验证交易。当节点接收到了交易消息,会把消息放在temp_pool里,然后在这个poolThread的一个loop中把mem_pool和temp_pool合并,对每笔交易进行验证,验证通过的交易都会发送一个inv消息,把消息发送到已连接上的RemoteNode中去。

b. 开启TcpListener侦听其他对等节点的消息,开启WebSocket侦听WebSocket消息。

6. 如果需要开启rpc,则开启RPC服务。

protected internal override void OnStop()
{
if (consensus != null) consensus.Dispose();
if (rpc != null) rpc.Dispose();
LocalNode.Dispose();
using (FileStream fs = new FileStream(PeerStatePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
LocalNode.SaveState(fs);
}
Blockchain.Default.Dispose();
}

MainService.OnStop

OnStop方法就简单多了

1. 析构共识服务和RPC服务

2. 析构LocalNode,调用LocalNode.Dispose方法,停止TcpListener侦听,与所有的connectedPeers断开连接,并且把所有的connectedPeers放到unconnectedPeers队列里。

3. 将unconnectedPeers写入peers.dat文件

4. 析构链结构,LevelDBBlockchain关闭底层的LevelDB存储。

三、小结

好了,到这里简单介绍了一下NEO CLI,重点讲了启动和关闭时都搞了些什么,基本上也就是NEO网络层干的事情,剩下的就是处理互相通信的消息,处理CLI上输入的指令。到这里你应该已经建立了一个多对等节点建立网络的大概过程。

NEO从入门到开窗(4) - NEO CLI的更多相关文章

  1. NEO从入门到开窗(3) - NEO编译器

    一.啰嗦两句 第一节的时候咱说了C#编译完了之后,就该NEO的编译器搞事情了.我们完全可以按这个节奏搞,手动用NEO的编译器neon编译dll文件生成指令码文件.avm.但是NEO团队给我们写智能合约 ...

  2. NEO从入门到开窗(2) - 智能合约的面相

    一.啰嗦两句 昨天讲了智能合约的一生,那丫长啥样啊?今儿我就跟各位唠叨唠叨. 二.一个简单的智能合约 下面这段就是NEO实例源码里的一个,干撒用的?聪明的你一眼儿就看出来了吧,就是一个所谓域名合约的增 ...

  3. NEO从入门到开窗(1) - 一个智能合约的诞生

    一.啰嗦两句 最近一直都在研究区块链,BitCoin,Etherenum, Hyper Ledger Fabric还有今天的主角小蚂蚁,当然出名以后改了一个艺名叫NEO.区块链大部分都是用Golang ...

  4. [转]NEO与以太坊:为什么NEO可能是2018年最强的加密货币

    本文转自:https://baijiahao.baidu.com/s?id=1591291802129464257&wfr=spider&for=pc NEO,它可以与以太坊竞争吗?N ...

  5. webpack入门(五)webpack CLI

    webpack的CLI安装和命令 Installation $ npm install webpack -g The webpack command is now available globally ...

  6. NEO

    平台: Windows 类型: 虚拟机镜像 软件包: .net core neo application server basic software blockchain neo open sourc ...

  7. C#区块链零基础入门,学习路线图 转

    C#区块链零基础入门,学习路线图 一.1分钟短视频<区块链100问>了解区块链基本概念 http://tech.sina.com.cn/zt_d/blockchain_100/ 二.C#区 ...

  8. SpringBoot入门:Spring Data JPA 和 JPA(理论)

    参考链接: Spring Data JPA - Reference Documentation Spring Data JPA--参考文档 中文版 纯洁的微笑:http://www.ityouknow ...

  9. 使用 Azure CLI 创建 Windows 虚拟机

    Azure CLI 用于从命令行或脚本创建和管理 Azure 资源. 本指南详细介绍如何使用 Azure CLI 部署运行 Windows Server 2016 的虚拟机. 部署完成后,我们连接到服 ...

随机推荐

  1. Linux显示所有可更新的软件清单命令

    Linux显示所有可更新的软件清单命令 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ yum check-update 程序"yum"尚未 ...

  2. 复制粘贴之插件(clipboard.min.js)不需要安装flash

    <!DOCTYPE html> <html> <head> <title>ZeroClipboard Test</title> <me ...

  3. js中的回调函数的理解

    一,常见的但是不是特别注意的回调方法. 1.1,ajax $.ajax({ url:"test.json", type: "GET", data: {usern ...

  4. 关于js中 toFixed()的一个小坑

    作为一名前端,大家都应该知道,toFixed()的作用,toFixed()经常用于前台与后台数据格式的转换,套用下w3c上面的定义: 定义和用法toFixed(n) 方法可把 Number 四舍五入为 ...

  5. 【转载】Spark运行架构

    1. Spark运行架构 1.1 术语定义 lApplication:Spark Application的概念和Hadoop MapReduce中的类似,指的是用户编写的Spark应用程序,包含了一个 ...

  6. $CDQ$分治总结

    A.\(CDQ\) 分治 特别基础的教程略. \(CDQ\)分治的优缺点: ( 1 )优点:代码量少,常数极小,可以降低处理维数. ( 2 )缺点:必须离线处理. \(CDQ\)分治与其他分治最本质的 ...

  7. cocos creator实现棋牌游戏滑动选牌的功能

    最近在玩cocos creator,打算学着做一款类似双扣游戏的棋牌,名字叫文成三星,比双扣还要多一扣,因为需要三幅牌,在我们老家比较流行这种玩法. 目前实现了绝大部分的逻辑效果如下: 有一点不好的体 ...

  8. C# Redis实战(六)

    六.查询数据 在C# Redis实战(五)中介绍了如何删除Redis中数据,本篇将继续介绍Redis中查询的写法. 1.使用Linq匹配关键字查询 using (var redisClient = R ...

  9. Java中常见数据结构Map之HashMap

    之前很早就在博客中写过HashMap的一些东西: 彻底搞懂HashMap,HashTableConcurrentHashMap关联: http://www.cnblogs.com/wang-meng/ ...

  10. kill 掉所有正在运行的hadoop jobs

    # get list of job's process IDs JOB_LIST=$(hadoop job -list 2> /dev/null | grep job_ | awk '{prin ...