NEO从入门到开窗(4) - NEO CLI
一、唠叨两句
首先,我们都知道区块链是去中心化的,其中节点都是对等节点,每个节点都几乎有完整的区块链特性,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的更多相关文章
- NEO从入门到开窗(3) - NEO编译器
一.啰嗦两句 第一节的时候咱说了C#编译完了之后,就该NEO的编译器搞事情了.我们完全可以按这个节奏搞,手动用NEO的编译器neon编译dll文件生成指令码文件.avm.但是NEO团队给我们写智能合约 ...
- NEO从入门到开窗(2) - 智能合约的面相
一.啰嗦两句 昨天讲了智能合约的一生,那丫长啥样啊?今儿我就跟各位唠叨唠叨. 二.一个简单的智能合约 下面这段就是NEO实例源码里的一个,干撒用的?聪明的你一眼儿就看出来了吧,就是一个所谓域名合约的增 ...
- NEO从入门到开窗(1) - 一个智能合约的诞生
一.啰嗦两句 最近一直都在研究区块链,BitCoin,Etherenum, Hyper Ledger Fabric还有今天的主角小蚂蚁,当然出名以后改了一个艺名叫NEO.区块链大部分都是用Golang ...
- [转]NEO与以太坊:为什么NEO可能是2018年最强的加密货币
本文转自:https://baijiahao.baidu.com/s?id=1591291802129464257&wfr=spider&for=pc NEO,它可以与以太坊竞争吗?N ...
- webpack入门(五)webpack CLI
webpack的CLI安装和命令 Installation $ npm install webpack -g The webpack command is now available globally ...
- NEO
平台: Windows 类型: 虚拟机镜像 软件包: .net core neo application server basic software blockchain neo open sourc ...
- C#区块链零基础入门,学习路线图 转
C#区块链零基础入门,学习路线图 一.1分钟短视频<区块链100问>了解区块链基本概念 http://tech.sina.com.cn/zt_d/blockchain_100/ 二.C#区 ...
- SpringBoot入门:Spring Data JPA 和 JPA(理论)
参考链接: Spring Data JPA - Reference Documentation Spring Data JPA--参考文档 中文版 纯洁的微笑:http://www.ityouknow ...
- 使用 Azure CLI 创建 Windows 虚拟机
Azure CLI 用于从命令行或脚本创建和管理 Azure 资源. 本指南详细介绍如何使用 Azure CLI 部署运行 Windows Server 2016 的虚拟机. 部署完成后,我们连接到服 ...
随机推荐
- Linux显示版本信息并退出
Linux显示版本信息并退出 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ cat --version cat (GNU coreutils) 8.21 Co ...
- iOS - GATT Profile 简介
1.引言 现在低功耗蓝牙(BLE)连接都是建立在 GATT (Generic Attribute Profile) 协议之上.GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短 ...
- babel-runtime和babel-polyfill两者区别优缺点
先说两种方式的原理: babel-polyfill 使用场景 Babel 默认只转换新的 JavaScript 语法,而不转换新的 API.例如,Iterator.Generator.Set.Maps ...
- 【BZOJ4003】【JLOI2015】城池攻占(左偏树)
题面 题目描述 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池.这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,其中 fi ...
- 【THUWC 2017】在美妙的数学王国中畅游
数学王国里有n座城市,每座城市有三个参数\(f\),\(a\),\(b\),一个智商为\(x\)的人经过一座城市的获益\(f(x)\)是 若\(f=1\),则\(f(x)=\sin(ax+b)\): ...
- 在做APP前端开发时应注意的一些问题
在做APP前端开发时应注意的一些问题 在整个app开发流程中,app前端开发是一个必不可少的环节,也是一个在app开发过程中重量级的角色.说到这,那么在app应用的前端开发中,又要注意什么问题呢?一. ...
- Python快速入门之与C语言异同
代码较长,建议使用电脑阅读本文. 10分钟入门Python 本文中使用的是Python3如果你曾经学过C语言,阅读此文,相信你能迅速发现这两种语言的异同,达到快速入门的目的.下面将开始介绍它们的异同. ...
- 使用uiautomation自动化重命名pdf书签,使全大写字母变成首字母大写
今天下载了一个英文pdf书籍,但书签全是大写英文字母,看上去有点别扭,于是想办法用自动化重命名pdf书签, 使书签全部变成首字母大写. pdf原始书签如下图: 重命名后的pdf书签 自动化动态效果图, ...
- day01的那些事
代码之道 路漫漫其修远兮,吾将上下而求索
- 笔记:Spring Cloud Feign Hystrix 配置
在 Spring Cloud Feign 中,除了引入了用户客户端负载均衡的 Spring Cloud Ribbon 之外,还引入了服务保护与容错的工具 Hystrix,默认情况下,Spring Cl ...