-
Notifications
You must be signed in to change notification settings - Fork 100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WSS: Add Web Socket Server #795
Closed
Closed
Changes from 3 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
e00d22d
add wss for block
Jim8y 9fd3737
add mssing file
Jim8y ab152d2
fix client management issue
Jim8y c61a280
Merge branch 'master' into wss
vncoelho 855c7d2
Merge branch 'master' into wss
Jim8y 0a40340
update the web socket server
Jim8y 1ec353b
Merge branch 'master' into wss
Jim8y 0b2e51a
subscribe and unsubscribe
Jim8y 14aad77
Merge branch 'wss' of github.com:Liaojinghui/neo-modules into wss
Jim8y 114d40e
fix subscription manager
Jim8y 3d1b14b
Finish Server
Jim8y 5126d4a
fix `UnSubscribe`
Jim8y 4c97742
Delete src/WebSocketClient/AtomicBool.cs
Jim8y 8b34b3a
Code format optimization
Jim8y 197f6bf
change method name
Jim8y 1b28667
max connection limitation
Jim8y 3061e6d
remove dependency to rpcserver and rpcclient
Jim8y b372ca1
project restructure and add copyright
Jim8y e80254c
code format
Jim8y 09af414
Merge branch 'master' into wss
Jim8y 765011f
Merge branch 'master' into wss
Jim8y File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
using Neo.Json; | ||
using WebSocketSharp; | ||
using WebSocketSharp.Server; | ||
|
||
namespace Neo.Plugins.WebSocketServer.Behaviors; | ||
|
||
public class BlockWebSocketBehavior : WebSocketBehavior | ||
{ | ||
private Guid ClientId { get; set; } | ||
|
||
public BlockWebSocketBehavior() | ||
{ | ||
ClientId = Guid.NewGuid(); | ||
} | ||
|
||
protected override void OnOpen() | ||
{ | ||
base.OnOpen(); | ||
WebSocketServerPlugin.AddClient(ClientId, this); | ||
} | ||
|
||
protected override void OnClose(CloseEventArgs e) | ||
{ | ||
base.OnClose(e); | ||
WebSocketServerPlugin.RemoveClient(ClientId); | ||
} | ||
|
||
// public async Task SendPersistedBlockMessage(string message) | ||
// { | ||
// await SendAsync(message, completed => { }); | ||
// } | ||
|
||
protected override void OnMessage(MessageEventArgs e) | ||
{ | ||
var request = JToken.Parse(e.Data); | ||
|
||
var action = request?["action"]?.ToString(); | ||
if (action == "subscribe") | ||
{ | ||
// The 'message' parameter will be passed from the WebSocketServerPlugin class | ||
// Task.Run(() => SendPersistedBlockMessage(message)); | ||
} | ||
} | ||
|
||
public Task SendPersistedBlockMessage(string message) | ||
{ | ||
var response = new JObject | ||
{ | ||
["action"] = "blockPersisted", | ||
["block"] = message | ||
}; | ||
Send(response.ToString()); | ||
return Task.CompletedTask; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>net7.0</TargetFramework> | ||
<RootNamespace>Neo.Plugins.WebSocketServer</RootNamespace> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<PackageReference Include="Neo.ConsoleService" Version="1.2.0" /> | ||
<PackageReference Include="WebSocketSharp" Version="1.0.3-rc11" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Neo.ConsoleService; | ||
using Newtonsoft.Json.Linq; | ||
using Neo.Ledger; | ||
using Neo.Network.P2P.Payloads; | ||
using Neo.Plugins.WebSocketServer.Behaviors; | ||
|
||
namespace Neo.Plugins.WebSocketServer; | ||
|
||
public class WebSocketServerPlugin : Plugin | ||
{ | ||
public override string Name => "NeoWebSocketServer"; | ||
public override string Description => "Enables WebSocket notifications for the node"; | ||
|
||
private Settings _settings; | ||
private static readonly Dictionary<Guid, BlockWebSocketBehavior> Handlers = new(); | ||
private static WebSocketSharp.Server.WebSocketServer _server; | ||
private NeoSystem _system; | ||
|
||
public WebSocketServerPlugin() | ||
{ | ||
Blockchain.Committed += OnCommitted; | ||
} | ||
|
||
protected override void Configure() | ||
{ | ||
_settings ??= new Settings(GetConfiguration()); | ||
} | ||
|
||
protected override void OnSystemLoaded(NeoSystem system) | ||
{ | ||
_system = system; | ||
} | ||
|
||
public override void Dispose() | ||
{ | ||
Blockchain.Committed -= OnCommitted; | ||
_server?.Stop(); | ||
base.Dispose(); | ||
} | ||
|
||
[ConsoleCommand("start wss", Category = "wss", Description = "Open Web Socket Server")] | ||
private void OnStart() | ||
{ | ||
if (_server is { IsListening: true }) return; | ||
|
||
var s = _settings.Servers.FirstOrDefault(p => p.Network == _system.Settings.Network); | ||
if (s == null) return; | ||
|
||
var useSsl = !string.IsNullOrEmpty(s.SslCert) && !string.IsNullOrEmpty(s.SslCertPassword); | ||
_server = new WebSocketSharp.Server.WebSocketServer(s.BindAddress, s.Port, useSsl); | ||
if (useSsl) | ||
{ | ||
_server.SslConfiguration.ServerCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(s.SslCert, s.SslCertPassword); | ||
} | ||
_server.AddWebSocketService<BlockWebSocketBehavior>("/block", () => new BlockWebSocketBehavior()); | ||
_server.Start(); | ||
} | ||
|
||
[ConsoleCommand("close wss", Category = "wss", Description = "Close Web Socket Server")] | ||
private void OnClose() | ||
{ | ||
if (_server is { IsListening: true }) _server.Stop(); | ||
ConsoleHelper.Info("Web Socket Server closed"); | ||
} | ||
|
||
private static async void OnCommitted(NeoSystem system, Block block) | ||
{ | ||
using var snapshot = system.GetSnapshot(); | ||
var blockJson = JObject.FromObject(block); | ||
await SendMessageToClients(blockJson.ToString()); | ||
} | ||
|
||
private static async Task SendMessageToClients(string message) | ||
{ | ||
var sendTasks = new List<Task>(); | ||
|
||
lock (Handlers) | ||
{ | ||
sendTasks.AddRange(Handlers.Values.Select(handler => handler.SendPersistedBlockMessage(message))); | ||
} | ||
|
||
await Task.WhenAll(sendTasks); | ||
} | ||
|
||
public static void AddClient(Guid clientId, BlockWebSocketBehavior client) | ||
{ | ||
lock (Handlers) | ||
{ | ||
Handlers[clientId] = client; | ||
} | ||
} | ||
|
||
public static void RemoveClient(Guid clientId) | ||
{ | ||
lock (Handlers) | ||
{ | ||
Handlers.Remove(clientId); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using Microsoft.Extensions.Configuration; | ||
|
||
namespace Neo.Plugins.WebSocketServer; | ||
|
||
public record WebSocketServerSetting | ||
{ | ||
public uint Network { get; init; } | ||
public IPAddress BindAddress { get; init; } | ||
public ushort Port { get; init; } | ||
public string SslCert { get; init; } | ||
public string SslCertPassword { get; init; } | ||
public List<string> TrustedAuthorities { get; init; } | ||
public int MaxConcurrentConnections { get; init; } | ||
public bool SessionEnabled { get; init; } | ||
public TimeSpan SessionExpirationTime { get; init; } | ||
|
||
public static WebSocketServerSetting Default { get; } = new() | ||
{ | ||
Network = 5195086u, | ||
BindAddress = IPAddress.None, | ||
MaxConcurrentConnections = 40, | ||
SessionEnabled = false, | ||
SessionExpirationTime = TimeSpan.FromSeconds(60) | ||
}; | ||
|
||
public static WebSocketServerSetting Load(IConfigurationSection section) => new() | ||
{ | ||
Network = section.GetValue("Network", Default.Network), | ||
BindAddress = IPAddress.Parse(section.GetSection("BindAddress").Value), | ||
Port = ushort.Parse(section.GetSection("Port").Value), | ||
SslCert = section.GetValue("SslCert", ""), | ||
SslCertPassword = section.GetValue("SslCertPassword", ""), | ||
TrustedAuthorities = section.GetSection("TrustedAuthorities").Get<List<string>>() ?? new List<string>(), | ||
MaxConcurrentConnections = section.GetValue("MaxConcurrentConnections", Default.MaxConcurrentConnections), | ||
SessionEnabled = section.GetValue("SessionEnabled", Default.SessionEnabled), | ||
SessionExpirationTime = TimeSpan.FromSeconds(section.GetValue("SessionExpirationTime", (int)Default.SessionExpirationTime.TotalSeconds)) | ||
}; | ||
} | ||
|
||
public class Settings | ||
{ | ||
public IReadOnlyList<WebSocketServerSetting> Servers { get; init; } | ||
|
||
public Settings(IConfigurationSection section) | ||
{ | ||
Servers = section.GetSection(nameof(Servers)).GetChildren().Select(p => WebSocketServerSetting.Load(p)).ToArray(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"PluginConfiguration": { | ||
"Servers": [ | ||
{ | ||
"Network": 860833102, | ||
"BindAddress": "127.0.0.1", | ||
"Port": 10334, | ||
"SslCert": "", | ||
"SslCertPassword": "", | ||
"TrustedAuthorities": [], | ||
"MaxConcurrentConnections": 40, | ||
"SessionEnabled": false, | ||
"SessionExpirationTime": 60 | ||
Jim8y marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
] | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should be using ConcurrentDictionary, so you dont have to use Lock. Reason being if have, let's say a million clients using "lock" will have a delay and lock the thread for new clients connecting to the server. You can use ConcurrentDictionary to fix this problem. because like i said new clients wont get a response. especially if there is blocks with 5k transactions in it. They would be waiting for lock to be lifted.