private async Task Receive(CancellationToken cancellationToken) { await Task.Run(() => { using (var consumer = new ConsumerBuilder <Ignore, string>(config).Build()) { Message message; consumer.Subscribe("main"); while (!cancellationToken.IsCancellationRequested) { var consumeResult = consumer.Consume(cancellationToken); var strMessage = consumeResult.Message.Value; try { message = JsonConvert.DeserializeObject <Message>(consumeResult.Message.Value); NewMessage?.BeginInvoke(message, null, null); } catch (Exception ex) { logger.Error(ex.Message); } } consumer.Close(); } }); }
/// <summary> /// Send a message (data) to all server in the dictionnary (wsDict) /// </summary> /// <param name="data"></param> /// <remarks> /// It will close connection with server who didn't respond /// </remarks> public void Broadcast(string data) { var serverClose = new Dictionary <string, WebSocket>(); foreach (var item in MainViewModel.ServerList) { try { item.Value.Send(data); } catch (Exception) { NewMessage.BeginInvoke(this, new EventArgsMessage($"The server {item.Key} is closed."), null, null); serverClose.Add(item.Key, item.Value); } } foreach (var item in serverClose) { MainViewModel.ServerList.Remove(item); var receivers = MainViewModel.ServerListUpdated?.GetInvocationList(); if (receivers != null) { foreach (EventHandler receiver in receivers) { receiver.BeginInvoke(this, EventArgs.Empty, null, null); } } } }
/// <summary> /// Connect to a list of server /// </summary> /// <param name="servers"></param> /// <see cref="Connect"/> public void ConnectToAll(List <string> servers) { NewMessage.BeginInvoke(this, new EventArgsMessage("Connect to the others servers"), null, null); // Connect to those servers foreach (var address in servers) { Connect(address); } }
/// <summary> /// Method used to connect to a server /// It will : /// - Create the "OnMessage" method to answer the server request /// - Send the blockchain in local to the server (to check who have the good one) /// - Ask for new server /// </summary> /// <param name="url"> /// The server address /// </param> /// <remarks> /// It will create a connection only if the server is not the local one /// and if the server is not already in the server list /// </remarks> public void Connect(string url) { // If we know the adress (url) or if it's our server address don't connect if (MainViewModel.ServerList.ContainsKey(url) || url == ServerAddress) { return; } // Message for the console NewMessage.BeginInvoke(this, new EventArgsMessage($"Begin Connection to {url}"), null, null); // Create the webSocket from the url var ws = new WebSocket(url); ws.OnMessage += (sender, e) => { var guid = Guid.NewGuid(); try { MainViewModel.WaitingForBlockchainAccess(guid); #region BlockChain Received if (e.Data.StartsWith(Constants.BLOCKCHAIN_IS_NOT_VALID)) { NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_IS_NOT_VALID), null, null); var chainReceived = JsonConvert.DeserializeObject <KittyChain>(e.Data.Substring(Constants.BLOCKCHAIN_IS_NOT_VALID.Length)); MainViewModel.BlockChain = chainReceived; NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_UPDATED), null, null); } else if (e.Data.StartsWith(Constants.BLOCKCHAIN_MISS_BLOCK)) { NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_MISS_BLOCK), null, null); var chainReceived = JsonConvert.DeserializeObject <KittyChain>(e.Data.Substring(Constants.BLOCKCHAIN_MISS_BLOCK.Length)); MainViewModel.BlockChain = chainReceived; NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_UPDATED), null, null); } else if (e.Data.StartsWith(Constants.BLOCKCHAIN_OVERWRITE)) { NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_OVERWRITE), null, null); var chainReceived = JsonConvert.DeserializeObject <KittyChain>(e.Data.Substring(Constants.BLOCKCHAIN_OVERWRITE.Length)); MainViewModel.BlockChain = chainReceived; NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_UPDATED), null, null); } else if (e.Data.StartsWith(Constants.NEED_BLOCKCHAIN)) { ws.Send(Constants.BLOCKCHAIN + JsonConvert.SerializeObject(MainViewModel.BlockChain)); } else if (e.Data.StartsWith(Constants.BLOCKCHAIN)) { var chainReceived = JsonConvert.DeserializeObject <KittyChain>(e.Data.Substring(Constants.BLOCKCHAIN.Length)); if (!MainViewModel.BlockChain.IsValid() && chainReceived.IsValid()) { MainViewModel.BlockChain = chainReceived; NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_UPDATED), null, null); } } #endregion #region Server List Request Receive // The request want our server list else if (e.Data.StartsWith("ServerList")) { NewMessage.BeginInvoke(this, new EventArgsMessage("Server list received"), null, null); // Deserialize the server send in the request // The Substring cut "GetServers" var servers = JsonConvert.DeserializeObject <List <string> >(e.Data.Substring(10)); ConnectToAll(servers); } #endregion // Unknow request else { NewMessage.BeginInvoke(this, new EventArgsMessage("Unknown message"), null, null); } } catch (Exception ex) { NewMessage.BeginInvoke(this, new EventArgsMessage(ex.Message), null, null); } finally { MainViewModel.BlockChainWaitingList.Remove(guid); var receivers = MainViewModel.BlockChainUpdated?.GetInvocationList(); if (receivers != null) { foreach (EventHandler receiver in receivers) { receiver.BeginInvoke(this, EventArgs.Empty, null, null); } } } }; ws.Connect(); var receiversServerList = MainViewModel.ServerListUpdated?.GetInvocationList(); if (receiversServerList != null) { foreach (EventHandler receiver in receiversServerList) { receiver.BeginInvoke(this, EventArgs.Empty, null, null); } } ws.Send(Constants.BLOCKCHAIN + JsonConvert.SerializeObject(MainViewModel.BlockChain)); ws.Send(Constants.GET_SERVERS + JsonConvert.SerializeObject(new List <string>(GetServers()) { ServerAddress })); MainViewModel.ServerList.Add(url, ws); }
/// <summary> /// Method called when the server receive a message /// </summary> /// <param name="e"></param> protected override void OnMessage(MessageEventArgs e) { var guid = Guid.NewGuid(); try { MainViewModel.WaitingForBlockchainAccess(guid); #region BlockChain Receive // The request send the entire blockchain if (e.Data.StartsWith(Constants.BLOCKCHAIN)) { // Deserialize the blockchain received // The Substring cut "BlockChain" or "BlockChainOverwrite" var chainReceived = JsonConvert.DeserializeObject <KittyChain>(e.Data.Substring(e.Data.StartsWith("BlockChainOverwrite") ? 19 : 10)); NewMessage.BeginInvoke(this, new EventArgsMessage("Check blockchain"), null, null); /* If chain received and local is not valid * OR * If same blockchain received and local * OR * If same chain and same pending transfers list * => Do nothing */ if (MainViewModel.BlockChain.Equals(chainReceived)) { MainViewModel.BlockChainWaitingList.Remove(guid); return; } var localIsValid = MainViewModel.BlockChain.IsValid(); var receivedIsValid = chainReceived.IsValid(); if (!localIsValid && !receivedIsValid) { MainViewModel.BlockChain.InitializeChain(); } // If chain received is not valid but local is // => Send that the received blockchain is not valid if (!receivedIsValid && localIsValid) { NewMessage.BeginInvoke(this, new EventArgsMessage("Blockchain receive not valid but local is"), null, null); Send(Constants.BLOCKCHAIN_IS_NOT_VALID + JsonConvert.SerializeObject(MainViewModel.BlockChain)); } // If chain received is valid but local is not // Copy the received blockchain if (receivedIsValid && !localIsValid) { NewMessage.BeginInvoke(this, new EventArgsMessage("Blockchain receive is valid and local is not"), null, null); MainViewModel.BlockChain = chainReceived; NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_UPDATED), null, null); } // If the received chain is bigger than local // => Copy the received blockchain else if (chainReceived.Chain.Count > MainViewModel.BlockChain.Chain.Count) { NewMessage.BeginInvoke(this, new EventArgsMessage("Blockchain is bigger than local"), null, null); MainViewModel.BlockChain = chainReceived; NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_UPDATED), null, null); } // If the received chain is lower than local // => Send the local blockchain else if (chainReceived.Chain.Count < MainViewModel.BlockChain.Chain.Count) { NewMessage.BeginInvoke(this, new EventArgsMessage("Blockchain receive lower than local"), null, null); Send(Constants.BLOCKCHAIN_MISS_BLOCK + JsonConvert.SerializeObject(MainViewModel.BlockChain)); } // If the chain are equals but the pending transfer list are different // => Get the pending transfer not in local and send the list of transfer else if (chainReceived.Chain.SequenceEqual(MainViewModel.BlockChain.Chain) && !chainReceived.PendingTransfers.SequenceEqual(MainViewModel.BlockChain.PendingTransfers)) { NewMessage.BeginInvoke(this, new EventArgsMessage("Chain equals but different pending transfers/nWaiting for transfer message"), null, null); } else { // If the sender force to overwrite the local if (e.Data.StartsWith(Constants.BLOCKCHAIN_OVERWRITE)) { NewMessage.BeginInvoke(this, new EventArgsMessage("Overwrite BlockChain from sender"), null, null); MainViewModel.BlockChain = chainReceived; NewMessage.BeginInvoke(this, new EventArgsMessage(Constants.BLOCKCHAIN_UPDATED), null, null); } // Send a overwrite force to the sender else { NewMessage.BeginInvoke(this, new EventArgsMessage("BlockChain receive is same size than actual but different information"), null, null); Send(Constants.BLOCKCHAIN_OVERWRITE + JsonConvert.SerializeObject(MainViewModel.BlockChain)); } } } else if (e.Data.StartsWith(Constants.NEED_BLOCKCHAIN)) { Send(Constants.BLOCKCHAIN + JsonConvert.SerializeObject(MainViewModel.BlockChain)); } #endregion #region Block Receive else if (e.Data.StartsWith(Constants.BLOCK)) { // Deserialize the block // The Substring cut "Block" var newBlock = JsonConvert.DeserializeObject <Block>(e.Data.Substring(5)); NewMessage.BeginInvoke(this, new EventArgsMessage("New Block received"), null, null); if (!MainViewModel.BlockChain.IsValid() || newBlock.PreviousHash != MainViewModel.BlockChain.LastBlock.Hash || newBlock.Index != MainViewModel.BlockChain.LastBlock.Index + 1) { Send(Constants.NEED_BLOCKCHAIN); } else { MainViewModel.BlockChain.Chain.Add(newBlock); foreach (var tr in newBlock.Transfers) { MainViewModel.BlockChain.PendingTransfers.Remove(tr); } if (newBlock.Index % Constants.NUMBER_OF_BLOCKS_TO_CHECK_DIFFICULTY == 0) { NewMessage.BeginInvoke(this, new EventArgsMessage(MainViewModel.BlockChain.CheckDifficulty()), null, null); } } } #endregion #region Transfer Receive // The request send a new transfer else if (e.Data.StartsWith(Constants.TRANSFER)) { // Deserialize the transfer // The Substring cut "Transfer" var newTransfer = JsonConvert.DeserializeObject <Transfer>(e.Data.Substring(8)); NewMessage.BeginInvoke(this, new EventArgsMessage("New transfer received"), null, null); var chain = MainViewModel.BlockChain.Chain.ToArray(); var transactions = MainViewModel.BlockChain.PendingTransfers.ToArray(); // If we already have it (in pending transfer or already validated) or it's not a valid transfer don't add it if (transactions.Any(t => t.Equals(newTransfer)) || chain.Any(b => b.Transfers.Any(t => t.Equals(newTransfer))) || !newTransfer.IsValid() || newTransfer.Amount <= 0 || newTransfer.Biscuit < 0 || MainViewModel.BlockChain.GetBalance(newTransfer.FromAddress, chain, transactions) < newTransfer.Amount + newTransfer.Biscuit) { NewMessage.BeginInvoke(this, new EventArgsMessage("New Transfer not valid or already in local"), null, null); } else { // If already is Ok add it to our pending transfer list MainViewModel.BlockChain.PendingTransfers.Add(newTransfer); NewMessage.BeginInvoke(this, new EventArgsMessage("New transfer added from server"), null, null); } } // The request send a list of transfer else if (e.Data.StartsWith(Constants.TRANSFERS)) { // Deserialize the transfer // The Substring cut "Transfer" var newTransfers = JsonConvert.DeserializeObject <List <Transfer> >(e.Data.Substring(9)); NewMessage.BeginInvoke(this, new EventArgsMessage("New list of transfer received"), null, null); foreach (var newTransfer in newTransfers) { // If we already have it or it's not a valid transfer don't add it if (MainViewModel.BlockChain.PendingTransfers.Any(t => t.Equals(newTransfer)) || MainViewModel.BlockChain.Chain.Any(b => b.Transfers.Any(t => t.Equals(newTransfer))) || !newTransfer.IsValid()) { NewMessage.BeginInvoke(this, new EventArgsMessage("New Transfer not valid or already in local"), null, null); } else { // If already is Ok add it to our pending transfer list MainViewModel.BlockChain.PendingTransfers.Add(newTransfer); NewMessage.BeginInvoke(this, new EventArgsMessage("New transfer added from server"), null, null); } } } #endregion #region GetServers Receive else if (e.Data.StartsWith(Constants.GET_SERVERS)) { NewMessage.BeginInvoke(this, new EventArgsMessage("Get Servers request received"), null, null); var listWs = JsonConvert.DeserializeObject <List <string> >(e.Data.Substring(10)); ServerUpdate.BeginInvoke(this, new EventArgsObject(listWs), null, null); if (MainViewModel.ServerList.Any()) { Send("ServerList" + JsonConvert.SerializeObject(MainViewModel.ServerList.Select(x => x.Key))); } } #endregion else { NewMessage.BeginInvoke(this, new EventArgsMessage("Unknown message"), null, null); } } catch (Exception ex) { NewMessage.BeginInvoke(this, new EventArgsMessage(ex.Message), null, null); } finally { MainViewModel.BlockChainWaitingList.Remove(guid); var receivers = MainViewModel.BlockChainUpdated?.GetInvocationList(); if (receivers != null) { foreach (EventHandler receiver in receivers) { receiver.BeginInvoke(this, EventArgs.Empty, null, null); } } } }