Skip to content

Commit 2c0c109

Browse files
StormHubnayato
authored andcommitted
WebSocket support. (#400)
1 parent 9e3a841 commit 2c0c109

119 files changed

Lines changed: 9718 additions & 6 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DotNetty.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Codecs.Http", "src
103103
EndProject
104104
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetty.Codecs.Http.Tests", "test\DotNetty.Codecs.Http.Tests\DotNetty.Codecs.Http.Tests.csproj", "{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}"
105105
EndProject
106+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSockets.Server", "examples\WebSockets.Server\WebSockets.Server.csproj", "{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}"
107+
EndProject
108+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSockets.Client", "examples\WebSockets.Client\WebSockets.Client.csproj", "{3326DB6E-023E-483F-9A1C-5905D3091B57}"
109+
EndProject
106110
Global
107111
GlobalSection(SolutionConfigurationPlatforms) = preSolution
108112
Debug|Any CPU = Debug|Any CPU
@@ -273,6 +277,14 @@ Global
273277
{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
274278
{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
275279
{16C89E7C-1575-4685-8DFA-8E7E2C6101BF}.Release|Any CPU.Build.0 = Release|Any CPU
280+
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
281+
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Debug|Any CPU.Build.0 = Debug|Any CPU
282+
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Release|Any CPU.ActiveCfg = Release|Any CPU
283+
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A}.Release|Any CPU.Build.0 = Release|Any CPU
284+
{3326DB6E-023E-483F-9A1C-5905D3091B57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
285+
{3326DB6E-023E-483F-9A1C-5905D3091B57}.Debug|Any CPU.Build.0 = Debug|Any CPU
286+
{3326DB6E-023E-483F-9A1C-5905D3091B57}.Release|Any CPU.ActiveCfg = Release|Any CPU
287+
{3326DB6E-023E-483F-9A1C-5905D3091B57}.Release|Any CPU.Build.0 = Release|Any CPU
276288
EndGlobalSection
277289
GlobalSection(SolutionProperties) = preSolution
278290
HideSolutionNode = FALSE
@@ -319,6 +331,8 @@ Global
319331
{A7CACAE7-66E7-43DA-948B-28EB0DDDB582} = {F716F1EF-81EF-4020-914A-5422A13A9E13}
320332
{5F68A5B1-7907-4B16-8AFE-326E9DD7D65B} = {126EA539-4B28-4B07-8B5D-D1D7F794D189}
321333
{16C89E7C-1575-4685-8DFA-8E7E2C6101BF} = {541093F6-616E-43D9-B671-FCD1F9C0A181}
334+
{EA387B4B-DAD0-4E34-B8A3-79EA4616726A} = {F716F1EF-81EF-4020-914A-5422A13A9E13}
335+
{3326DB6E-023E-483F-9A1C-5905D3091B57} = {F716F1EF-81EF-4020-914A-5422A13A9E13}
322336
EndGlobalSection
323337
GlobalSection(ExtensibilityGlobals) = postSolution
324338
{9FE6A783-C20D-4097-9988-4178E2C4CE75} = {126EA539-4B28-4B07-8B5D-D1D7F794D189}

examples/Examples.Common/ClientSettings.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,14 @@ public static bool IsSsl
2121
public static int Port => int.Parse(ExampleHelper.Configuration["port"]);
2222

2323
public static int Size => int.Parse(ExampleHelper.Configuration["size"]);
24+
25+
public static bool UseLibuv
26+
{
27+
get
28+
{
29+
string libuv = ExampleHelper.Configuration["libuv"];
30+
return !string.IsNullOrEmpty(libuv) && bool.Parse(libuv);
31+
}
32+
}
2433
}
2534
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace WebSockets.Client
5+
{
6+
using System;
7+
using System.IO;
8+
using System.Net;
9+
using System.Net.Security;
10+
using System.Security.Cryptography.X509Certificates;
11+
using System.Threading.Tasks;
12+
using DotNetty.Buffers;
13+
using DotNetty.Codecs.Http;
14+
using DotNetty.Codecs.Http.WebSockets;
15+
using DotNetty.Codecs.Http.WebSockets.Extensions.Compression;
16+
using DotNetty.Handlers.Tls;
17+
using DotNetty.Transport.Bootstrapping;
18+
using DotNetty.Transport.Channels;
19+
using DotNetty.Transport.Channels.Sockets;
20+
using DotNetty.Transport.Libuv;
21+
using Examples.Common;
22+
23+
class Program
24+
{
25+
static async Task RunClientAsync()
26+
{
27+
var builder = new UriBuilder
28+
{
29+
Scheme = ClientSettings.IsSsl ? "wss" : "ws",
30+
Host = ClientSettings.Host.ToString(),
31+
Port = ClientSettings.Port
32+
};
33+
34+
string path = ExampleHelper.Configuration["path"];
35+
if (!string.IsNullOrEmpty(path))
36+
{
37+
builder.Path = path;
38+
}
39+
40+
Uri uri = builder.Uri;
41+
ExampleHelper.SetConsoleLogger();
42+
43+
bool useLibuv = ClientSettings.UseLibuv;
44+
Console.WriteLine("Transport type : " + (useLibuv ? "Libuv" : "Socket"));
45+
46+
IEventLoopGroup group;
47+
if (useLibuv)
48+
{
49+
group = new EventLoopGroup();
50+
}
51+
else
52+
{
53+
group = new MultithreadEventLoopGroup();
54+
}
55+
56+
X509Certificate2 cert = null;
57+
string targetHost = null;
58+
if (ClientSettings.IsSsl)
59+
{
60+
cert = new X509Certificate2(Path.Combine(ExampleHelper.ProcessDirectory, "dotnetty.com.pfx"), "password");
61+
targetHost = cert.GetNameInfo(X509NameType.DnsName, false);
62+
}
63+
try
64+
{
65+
var bootstrap = new Bootstrap();
66+
bootstrap
67+
.Group(group)
68+
.Option(ChannelOption.TcpNodelay, true);
69+
if (useLibuv)
70+
{
71+
bootstrap.Channel<TcpChannel>();
72+
}
73+
else
74+
{
75+
bootstrap.Channel<TcpSocketChannel>();
76+
}
77+
78+
// Connect with V13 (RFC 6455 aka HyBi-17). You can change it to V08 or V00.
79+
// If you change it to V00, ping is not supported and remember to change
80+
// HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline.
81+
var handler =new WebSocketClientHandler(
82+
WebSocketClientHandshakerFactory.NewHandshaker(
83+
uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));
84+
85+
bootstrap.Handler(new ActionChannelInitializer<IChannel>(channel =>
86+
{
87+
IChannelPipeline pipeline = channel.Pipeline;
88+
if (cert != null)
89+
{
90+
pipeline.AddLast("tls", new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ClientTlsSettings(targetHost)));
91+
}
92+
93+
pipeline.AddLast(
94+
new HttpClientCodec(),
95+
new HttpObjectAggregator(8192),
96+
WebSocketClientCompressionHandler.Instance,
97+
handler);
98+
}));
99+
100+
IChannel ch = await bootstrap.ConnectAsync(new IPEndPoint(ClientSettings.Host, ClientSettings.Port));
101+
await handler.HandshakeCompletion;
102+
103+
Console.WriteLine("WebSocket handshake completed.\n");
104+
Console.WriteLine("\t[bye]:Quit \n\t [ping]:Send ping frame\n\t Enter any text and Enter: Send text frame");
105+
while (true)
106+
{
107+
string msg = Console.ReadLine();
108+
if (msg == null)
109+
{
110+
break;
111+
}
112+
else if ("bye".Equals(msg.ToLower()))
113+
{
114+
await ch.WriteAndFlushAsync(new CloseWebSocketFrame());
115+
break;
116+
}
117+
else if ("ping".Equals(msg.ToLower()))
118+
{
119+
var frame = new PingWebSocketFrame(Unpooled.WrappedBuffer(new byte[] { 8, 1, 8, 1 }));
120+
await ch.WriteAndFlushAsync(frame);
121+
}
122+
else
123+
{
124+
WebSocketFrame frame = new TextWebSocketFrame(msg);
125+
await ch.WriteAndFlushAsync(frame);
126+
}
127+
}
128+
129+
await ch.CloseAsync();
130+
}
131+
finally
132+
{
133+
await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
134+
}
135+
}
136+
137+
static void Main() => RunClientAsync().Wait();
138+
}
139+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace WebSockets.Client
5+
{
6+
using System;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using DotNetty.Codecs.Http;
10+
using DotNetty.Codecs.Http.WebSockets;
11+
using DotNetty.Common.Concurrency;
12+
using DotNetty.Common.Utilities;
13+
using DotNetty.Transport.Channels;
14+
15+
public class WebSocketClientHandler : SimpleChannelInboundHandler<object>
16+
{
17+
readonly WebSocketClientHandshaker handshaker;
18+
readonly TaskCompletionSource completionSource;
19+
20+
public WebSocketClientHandler(WebSocketClientHandshaker handshaker)
21+
{
22+
this.handshaker = handshaker;
23+
this.completionSource = new TaskCompletionSource();
24+
}
25+
26+
public Task HandshakeCompletion => this.completionSource.Task;
27+
28+
public override void ChannelActive(IChannelHandlerContext ctx) =>
29+
this.handshaker.HandshakeAsync(ctx.Channel).LinkOutcome(this.completionSource);
30+
31+
public override void ChannelInactive(IChannelHandlerContext context)
32+
{
33+
Console.WriteLine("WebSocket Client disconnected!");
34+
}
35+
36+
protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
37+
{
38+
IChannel ch = ctx.Channel;
39+
if (!this.handshaker.IsHandshakeComplete)
40+
{
41+
try
42+
{
43+
this.handshaker.FinishHandshake(ch, (IFullHttpResponse)msg);
44+
Console.WriteLine("WebSocket Client connected!");
45+
this.completionSource.TryComplete();
46+
}
47+
catch (WebSocketHandshakeException e)
48+
{
49+
Console.WriteLine("WebSocket Client failed to connect");
50+
this.completionSource.TrySetException(e);
51+
}
52+
53+
return;
54+
}
55+
56+
57+
if (msg is IFullHttpResponse response)
58+
{
59+
throw new InvalidOperationException(
60+
$"Unexpected FullHttpResponse (getStatus={response.Status}, content={response.Content.ToString(Encoding.UTF8)})");
61+
}
62+
63+
if (msg is TextWebSocketFrame textFrame)
64+
{
65+
Console.WriteLine($"WebSocket Client received message: {textFrame.Text()}");
66+
}
67+
else if (msg is PongWebSocketFrame)
68+
{
69+
Console.WriteLine("WebSocket Client received pong");
70+
}
71+
else if (msg is CloseWebSocketFrame)
72+
{
73+
Console.WriteLine("WebSocket Client received closing");
74+
ch.CloseAsync();
75+
}
76+
}
77+
78+
public override void ExceptionCaught(IChannelHandlerContext ctx, Exception exception)
79+
{
80+
Console.WriteLine("Exception: " + exception);
81+
this.completionSource.TrySetException(exception);
82+
ctx.CloseAsync();
83+
}
84+
}
85+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>netcoreapp1.1;net451</TargetFrameworks>
6+
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">1.6.1</NetStandardImplicitPackageVersion>
7+
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
8+
</PropertyGroup>
9+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net451' ">
10+
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
11+
</PropertyGroup>
12+
<ItemGroup>
13+
<None Include="..\..\shared\dotnetty.com.pfx" Link="dotnetty.com.pfx">
14+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
15+
</None>
16+
<None Update="appsettings.json">
17+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
18+
</None>
19+
</ItemGroup>
20+
<ItemGroup>
21+
<ProjectReference Include="..\..\src\DotNetty.Buffers\DotNetty.Buffers.csproj" />
22+
<ProjectReference Include="..\..\src\DotNetty.Codecs.Http\DotNetty.Codecs.Http.csproj" />
23+
<ProjectReference Include="..\..\src\DotNetty.Codecs\DotNetty.Codecs.csproj" />
24+
<ProjectReference Include="..\..\src\DotNetty.Common\DotNetty.Common.csproj" />
25+
<ProjectReference Include="..\..\src\DotNetty.Handlers\DotNetty.Handlers.csproj" />
26+
<ProjectReference Include="..\..\src\DotNetty.Transport.Libuv\DotNetty.Transport.Libuv.csproj" />
27+
<ProjectReference Include="..\..\src\DotNetty.Transport\DotNetty.Transport.csproj" />
28+
<ProjectReference Include="..\Examples.Common\Examples.Common.csproj" />
29+
</ItemGroup>
30+
31+
</Project>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"ssl": "false",
3+
"host": "127.0.0.1",
4+
"port": "8080",
5+
"path": "/websocket",
6+
"libuv": "true"
7+
}

0 commit comments

Comments
 (0)