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 的虚拟机. 部署完成后,我们连接到服 ...
随机推荐
- DirectX--Filter属性页的调用
IEnumFilters* pEnum; HRESULT hr ; if (pigb) { hr = pigb-> EnumFilters(&pEnum); if (FAILED(hr) ...
- hibernate学习(三) hibernate中的对象状态
hibernate对象的状态分为三种: 游离状态,持久化状态,瞬时状态 下面一行代码区分: Configuration cfg=new Configuration().configure(); ...
- [QNAP crontab 定時執行程式
注意要自動執行的 sh 檔不要放在 /root 裡, 不然韌體更新後檔案會不見, 要放在個人帳號的資料夾,例如 /share/homes/帳號/ QNAP 的 crontab 放在 /etc/conf ...
- JavaScript设计模式(5)-组合模式
组合模式 1. 适合使用组合模式的条件: 存在一批组织成某种层次体系的对象,如树形结构(具体的结构在开发期间可能无法得知) 希望对这批对象或其中的一部分对象实施一个相同的操作 2. 注意点: 组合对象 ...
- js中百分比运算,大型数据会算错
改法:被除数乘100在做除法运算,就能改掉算错
- [POJ2115]C Looooops 拓展欧几里得
原题入口 这个题要找到本身的模型就行了 a+c*x=b(mod 2k) -> c*x+2k*y=b-a 求这个方程对于x,y有没有整数解. 这个只要学过拓展欧几里得(好像有的叫扩展欧几里德QA ...
- [BZOJ1031] [JSOI2007] 字符加密Cipher (后缀数组)
Description 喜欢钻研问题的JS同学,最近又迷上了对加密方法的思考.一天,他突然想出了一种他认为是终极的加密办法 :把需要加密的信息排成一圈,显然,它们有很多种不同的读法.例如下图,可以读作 ...
- Redis之Zset
一.Redis之Zset简介 1. 有序集合Zset是String类型的有序集合. 2. Zset中每个元素都会关联一个double类型的分数值,redis通过分数值来为集合中所有成员进行从小到大排序 ...
- AOP面向切面编程在Android中的使用
GitHub地址(欢迎下载完整Demo) https://github.com/ganchuanpu/AOPDemo 项目需求描述 我想类似于这样的个人中心的界面,大家都不会陌生吧.那几个有箭头的地方 ...
- NYOJ一种排序
//最重要的收获就是懂得了,还可以调用库函数直接对结构体进行排序sort(const void *,const void *,cmp) /* bool cmp(rect c,rect d) { if( ...