package org.rockyang.blockchain.net.client; import com.google.common.base.Optional; import org.rockyang.blockchain.conf.AppConfig; import org.rockyang.blockchain.db.DBAccess; import org.rockyang.blockchain.event.FetchNextBlockEvent; import org.rockyang.blockchain.net.ApplicationContextProvider; import org.rockyang.blockchain.net.base.MessagePacket; import org.rockyang.blockchain.net.base.MessagePacketType; import org.rockyang.blockchain.net.base.Node; import org.rockyang.blockchain.net.conf.TioProps; import org.rockyang.blockchain.utils.SerializeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import org.tio.client.AioClient; import org.tio.client.ClientChannelContext; import org.tio.client.ClientGroupContext; import org.tio.core.Aio; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.List; /** * 客户端启动程序 * @author yangjian * */ @Component public class AppClient { @Resource private ClientGroupContext clientGroupContext; @Autowired private TioProps tioProps; private AioClient aioClient; @Autowired private DBAccess dbAccess; @Autowired AppConfig appConfig; private static Logger logger = LoggerFactory.getLogger(AppClient.class); /** * 网络客户端程序入口 */ @PostConstruct public void clientStart() throws Exception { // 这里判断是否启用节点发现,如果没有则以单机节点运行,不去尝试连接种子节点 if (!appConfig.isNodeDiscover()) { return; } aioClient = new AioClient(clientGroupContext); //加载数据库中的节点数据 Optional<List<Node>> nodeList = dbAccess.getNodeList(); List<Node> nodes = null; if (nodeList.isPresent()) { nodes = nodeList.get(); //初始化配置 properties 中的节点 } else if (null != tioProps.getNodes()) { nodes = tioProps.getNodes(); } // 添加节点 for (Node node : nodes) { connectNode(node); } } /** * 发送消息到一个group * @param messagePacket */ public void sendGroup(MessagePacket messagePacket) { // 关闭同步功能 if (!appConfig.isNodeDiscover()) { return; } Aio.sendToGroup(clientGroupContext, tioProps.getClientGroupName(), messagePacket); } /** * 添加节点 * @param serverIp * @param port */ public void addNode(String serverIp, int port) throws Exception { if (!appConfig.isNodeDiscover()) { return; } Node node = new Node(serverIp, port); // determine if the node is already exists Optional<List<Node>> nodeList = dbAccess.getNodeList(); if (nodeList.isPresent() && nodeList.get().contains(node)) { return; } if (dbAccess.addNode(node)) { connectNode(node); } } /** * 连接节点 * @param node * @throws Exception */ public void connectNode(Node node) throws Exception { ClientChannelContext channelContext = aioClient.connect(node); Aio.send(channelContext, new MessagePacket(SerializeUtils.serialize(MessagePacket.HELLO_MESSAGE))); Aio.bindGroup(channelContext, tioProps.getClientGroupName()); logger.info("连接节点成功, {}", node); } /** * 向所有连接的节点发起同步区块请求 */ @EventListener(ApplicationReadyEvent.class) public void fetchNextBlock() { ApplicationContextProvider.publishEvent(new FetchNextBlockEvent(0)); } /** * 同步节点列表 */ @EventListener(ApplicationReadyEvent.class) public void fetchNodeList() { logger.info("++++++++++++++++++++++++++ 开始获取在线节点 +++++++++++++++++++++++++++"); MessagePacket packet = new MessagePacket(); packet.setType(MessagePacketType.REQ_NODE_LIST); packet.setBody(SerializeUtils.serialize(MessagePacket.FETCH_NODE_LIST_SYMBOL)); sendGroup(packet); } }