public
class Message implements Serializable {
/**
* 消息内容,就是我们的区块链、交易池等所需要的信息,使用JSON.toString转化到的json字符串
*/
private String data;
/**
* 消息类型
*/
private int type;
}
/**
* 查询最新的区块
*/
private final
static
int QUERY_LATEST_BLOCK =
0;
/**
* 查询整个区块链
*/
private final
static
int QUERY_BLOCK_CHAIN =
1;
/**
* 查询交易集合
*/
private final
static
int QUERY_TRANSACTION =
2;
/**
* 查询已打包的交易集合
*/
private final
static
int QUERY_PACKED_TRANSACTION =
3;
/**
* 查询钱包集合
*/
private final
static
int QUERY_WALLET =
4;
/**
* 返回区块集合
*/
private final
static
int RESPONSE_BLOCK_CHAIN =
5;
/**
* 返回交易集合
*/
private final
static
int RESPONSE_TRANSACTION =
6;
/**
* 返回已打包交易集合
*/
private final
static
int RESPONSE_PACKED_TRANSACTION =
7;
/**
* 返回钱包集合
*/
private final
static
int RESPONSE_WALLET =
8;
P2PClient p2PClient =
new P2PClient();
String url =
"ws://localhost:"+args[
0]+
"/test";
p2PClient.connectToPeer(url);
P2PClient
中的
connectToPeer
方法:
public void connectToPeer(String url) throws IOException, DeploymentException {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
URI uri = URI.create(url);
this.session = container.connectToServer(P2PClient.class, uri);
}
P2PClient
中,
WebSocketContainer.connectToServer
的时候会回调
onOpen
函数,假设我们只查询钱包公钥信息,此时服务端会接收到相应的请求。
@OnOpen
public void onOpen(Session session) {
this.session = session;
p2PService.sendMsg(session, p2PService.queryWalletMsg());
}
queryWalletMsg
方法:
public String queryWalletMsg() {
return JSON.toJSONString(
new Message(QUERY_WALLET));
}
sendMsg
方法:
@
Override
public void sendMsg(Session session, String msg) {
session
.getAsyncRemote()
.sendText(
msg);
}
P2PServer
中
OnMessage
方法
/**
* 收到客户端发来消息
* @param msg 消息对象
*/
@OnMessage
public void onMessage(Session session, String msg) {
p2PService.handleMessage(session, msg);
}
p2PService.handleMessage
就是解析接收到的消息(Msg),根据类型的不同调用其他的方法(一个巨型Switch语句,这里就介绍一小部分),这里我们接收到了Client传来的信息码
QUERY_WALLET
。
@Override
public void handleMessage(Session session, String msg) {
Message message = JSON.parseObject(msg, Message.class);
switch (message.getType()){
case QUERY_WALLET:
sendMsg(session, responseWallets());
break;
case RESPONSE_WALLET:
handleWalletResponse(message.getData());
break;
......
}
QUERY_WALLET
,调用
responseWallets()
方法,得到数据。
private
String responseWallets() {
String wallets = blockService.findAllWallets();
return
JSON.toJSONString(
new Message(RESPONSE_WALLET, wallets));
}
findAllWallets
的具体实现,其实就是遍历钱包集合,统计钱包公钥,没有什么难度。
@Override
public String findAllWallets() {
List<Wallet> wallets =
new ArrayList<>();
myWalletMap.forEach(
(address, wallet) ->{
wallets.add(Wallet.builder().publicKey(wallet.getPublicKey()).build());
});
otherWalletMap.forEach(
(address, wallet) ->{
wallets.add(wallet);
});
return JSON.toJSONString(wallets);
}
responseWallets()
方法中,最后一句话新建了一个Message对象,并设置了信息码为
RESPONSE_WALLET
,在
handleMessage
中调用了
sendmsg
方法回传给Client。
case QUERY_WALLET:
sendMsg(session, responseWallets());
break;
P2PClient
中的
OnMessage
方法:
@OnMessage
public void onMessage(String msg) {
p2PService.handleMessage(
this.session, msg);
}
p2PService.handleMessage
方法,此时收到的信息码为
RESPONSE_WALLET
,进入
handleWalletResponse
方法:
case RESPONSE_WALLET:
handleWalletResponse(
message.getData());
break;
handleWalletResponse
的实现, 解析接收到的钱包公钥信息,并存储到Client节点的
blockService
中。
private void handleWalletResponse(String msg) {
List<Wallet> wallets =
"\"[]\"".
equals(msg)?
new ArrayList<>():JSON.parseArray(msg, Wallet.class);
wallets.forEach(wallet -> {
blockService.addOtherWallet(walletService.getWalletAddress(wallet.getPublicKey()),wallet );
});
}
public
class
SpringUtil
implements
ApplicationContextAware {
public
static ApplicationContext applicationContext;
@
Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext !=
null) {
SpringUtil.applicationContext = applicationContext;
}
}
/**
* 获取applicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean.
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
*/
public
static <T>
T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
*/
public
static <T>
T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
SpringUtil
中的
applicationContext
,下面给出启动类(为了简单测试,两个节点共用一个启动类,根据Args的不同来分别处理)以及相关节点的配置。
public static void main(String[] args) {
System.
out.println(
"Hello world");
SpringUtil.applicationContext = SpringApplication.run(Hello.class, args);
if (args.length>
0){
P2PClient p2PClient =
new P2PClient();
String url =
"ws://localhost:"+args[
0]+
"/test";
try {
p2PClient.connectToPeer(url);
}
catch (Exception e) {
e.printStackTrace();
}
}
//之前是这样
//@Autowired
//private P2PService p2PService;
//改正后,去掉Autowired,每次使用都手动获取beanprivate P2PService p2PService;@OnOpenpublic void onOpen(Session session) {
//如果不使用那些,在这里会报空指针异常,p2PService 为
null p2PService = SpringUtil.getBean(P2PService.class);
//新增这句话从IVO容器中获取bean p2PService.sendMsg(session, p2PService.queryWalletMsg());}
☞不用掉一根头发!用 Flutter + Dart 快速构建一款绝美移动 App
☞看我发现了什么好东西?Java Optional,绝对值得一学 | 原力计划
☞腾讯提结合ACNet进行细粒度分类,效果达到最新SOTA | CVPR 2020
☞返鄂复工人员自述:回武汉上班,要先飞合肥,再由公司包车接回去