Skip to content

Commit

Permalink
Socket-based UDP channel transport (#79). (#171)
Browse files Browse the repository at this point in the history
Motivation:
Ported/Re-implemented DatagramChannel/UDP into DotNetty

Modifications:
- Added DatagramChannel/Config into transport project.
- Added DatagramPacket/DefaultAddressedEnvelop/IAddressedEnvelop into transport project.
- Added DatagramPacketEncoder/DatagramPacketDecoder into Codec project.
- Extra: Ported QuoteOfTheMoment UDP example project.

Result:
DatagramChannel/UDP transport is supported by DotNetty.
  • Loading branch information
StormHub authored and nayato committed Nov 23, 2016
1 parent 6d1a58d commit 4488596
Show file tree
Hide file tree
Showing 39 changed files with 2,543 additions and 38 deletions.
17 changes: 17 additions & 0 deletions DotNetty.sln
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "DotNetty.Codecs.ProtocolBuf
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "DotNetty.Codecs.ProtocolBuffers.Tests", "test\DotNetty.Codecs.ProtocolBuffers.Tests\DotNetty.Codecs.ProtocolBuffers.Tests.xproj", "{E33A7419-E4E8-4243-A2E5-7376BA32D608}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Datagram", "Datagram", "{7151B6B9-67C1-4899-9DD5-6E05AF16B743}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "QuoteOfTheMoment.Server", "examples\QuoteOfTheMoment.Server\QuoteOfTheMoment.Server.xproj", "{5E6F211F-A215-409C-8A6D-5AC0251F66B5}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "QuoteOfTheMoment.Client", "examples\QuoteOfTheMoment.Client\QuoteOfTheMoment.Client.xproj", "{81AA23B3-E975-403C-8C90-0AA0E572B539}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -246,6 +252,14 @@ Global
{E33A7419-E4E8-4243-A2E5-7376BA32D608}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E33A7419-E4E8-4243-A2E5-7376BA32D608}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E33A7419-E4E8-4243-A2E5-7376BA32D608}.Release|Any CPU.Build.0 = Release|Any CPU
{5E6F211F-A215-409C-8A6D-5AC0251F66B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E6F211F-A215-409C-8A6D-5AC0251F66B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E6F211F-A215-409C-8A6D-5AC0251F66B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E6F211F-A215-409C-8A6D-5AC0251F66B5}.Release|Any CPU.Build.0 = Release|Any CPU
{81AA23B3-E975-403C-8C90-0AA0E572B539}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81AA23B3-E975-403C-8C90-0AA0E572B539}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81AA23B3-E975-403C-8C90-0AA0E572B539}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81AA23B3-E975-403C-8C90-0AA0E572B539}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -290,5 +304,8 @@ Global
{57AB7999-1705-4F12-A05D-89FEC4EAA305} = {6A0821D4-8A5D-42AD-8E3F-F519100F4AD8}
{676744F7-3B28-4EE5-B3D0-7EBAF418B727} = {F02D7F30-ABA7-4438-8D28-10898E731906}
{E33A7419-E4E8-4243-A2E5-7376BA32D608} = {6A0821D4-8A5D-42AD-8E3F-F519100F4AD8}
{7151B6B9-67C1-4899-9DD5-6E05AF16B743} = {2B766264-D269-415C-8F2A-5AFC44409C01}
{5E6F211F-A215-409C-8A6D-5AC0251F66B5} = {7151B6B9-67C1-4899-9DD5-6E05AF16B743}
{81AA23B3-E975-403C-8C90-0AA0E572B539} = {7151B6B9-67C1-4899-9DD5-6E05AF16B743}
EndGlobalSection
EndGlobal
63 changes: 63 additions & 0 deletions examples/QuoteOfTheMoment.Client/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace QuoteOfTheMoment.Client
{
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using DotNetty.Buffers;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using Examples.Common;

class Program
{
static async Task RunClientAsync()
{
ExampleHelper.SetConsoleLogger();

var group = new MultithreadEventLoopGroup();

try
{
var bootstrap = new Bootstrap();
bootstrap
.Group(group)
.Channel<SocketDatagramChannel>()
.Option(ChannelOption.SoBroadcast, true)
.Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
{
channel.Pipeline.AddLast("Quote", new QuoteOfTheMomentClientHandler());
}));

IChannel clientChannel = await bootstrap.BindAsync(IPEndPoint.MinPort);

Console.WriteLine("Sending broadcast QOTM");

// Broadcast the QOTM request to port.
byte[] bytes = Encoding.UTF8.GetBytes("QOTM?");
IByteBuffer buffer = Unpooled.WrappedBuffer(bytes);
await clientChannel.WriteAndFlushAsync(
new DatagramPacket(
buffer,
new IPEndPoint(IPAddress.Broadcast, ClientSettings.Port)));

Console.WriteLine("Waiting for response.");

await Task.Delay(5000);
Console.WriteLine("Waiting for response time 5000 completed. Closing client channel.");

await clientChannel.CloseAsync();
}
finally
{
await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
}
}

static void Main() => RunClientAsync().Wait();
}
}
22 changes: 22 additions & 0 deletions examples/QuoteOfTheMoment.Client/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("QuoteOfTheMoment.Client")]
[assembly: AssemblyTrademark("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("81aa23b3-e975-403c-8c90-0aa0e572b539")]
21 changes: 21 additions & 0 deletions examples/QuoteOfTheMoment.Client/QuoteOfTheMoment.Client.xproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>81aa23b3-e975-403c-8c90-0aa0e572b539</ProjectGuid>
<RootNamespace>QuoteOfTheMoment.Client</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>

<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
38 changes: 38 additions & 0 deletions examples/QuoteOfTheMoment.Client/QuoteOfTheMomentClientHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace QuoteOfTheMoment.Client
{
using System;
using System.Text;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;

public class QuoteOfTheMomentClientHandler : SimpleChannelInboundHandler<DatagramPacket>
{
protected override void ChannelRead0(IChannelHandlerContext ctx, DatagramPacket packet)
{
Console.WriteLine($"Client Received => {packet}");

if (!packet.Content.IsReadable())
{
return;
}

string message = packet.Content.ToString(Encoding.UTF8);
if (!message.StartsWith("QOTM: "))
{
return;
}

Console.WriteLine($"Quote of the Moment: {message.Substring(6)}");
ctx.CloseAsync();
}

public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
Console.WriteLine("Exception: " + exception);
context.CloseAsync();
}
}
}
3 changes: 3 additions & 0 deletions examples/QuoteOfTheMoment.Client/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"port": "7686"
}
42 changes: 42 additions & 0 deletions examples/QuoteOfTheMoment.Client/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "QuoteOfTheMoment.Client",
"description": "Example of QuoteOfTheMoment Client",
"buildOptions": {
"emitEntryPoint": true,
"copyToOutput": {
"include": [ "appsettings.json", "..\\..\\shared\\dotnetty.com.pfx" ]
}
},
"dependencies": {
"DotNetty.Common": {
"target": "project"
},
"DotNetty.Buffers": {
"target": "project"
},
"DotNetty.Transport": {
"target": "project"
},
"DotNetty.Handlers": {
"target": "project"
},
"DotNetty.Codecs": {
"target": "project"
},
"Examples.Common": {
"target": "project"
}
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0-*",
"type": "platform"
}
},
"imports": "dnxcore50"
},
"net451": {}
}
}
48 changes: 48 additions & 0 deletions examples/QuoteOfTheMoment.Server/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace QuoteOfTheMoment.Server
{
using System;
using System.Threading.Tasks;
using DotNetty.Handlers.Logging;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using Examples.Common;

class Program
{
static async Task RunServerAsync()
{
ExampleHelper.SetConsoleLogger();

var group = new MultithreadEventLoopGroup();
try
{
var bootstrap = new Bootstrap();
bootstrap
.Group(group)
.Channel<SocketDatagramChannel>()
.Option(ChannelOption.SoBroadcast, true)
.Handler(new LoggingHandler("SRV-LSTN"))
.Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
{
channel.Pipeline.AddLast("Quote", new QuoteOfTheMomentServerHandler());
}));

IChannel boundChannel = await bootstrap.BindAsync(ServerSettings.Port);
Console.WriteLine("Press any key to terminate the server.");
Console.ReadLine();

await boundChannel.CloseAsync();
}
finally
{
await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
}
}

static void Main() => RunServerAsync().Wait();
}
}
22 changes: 22 additions & 0 deletions examples/QuoteOfTheMoment.Server/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("QuoteOfTheMoment.Server")]
[assembly: AssemblyTrademark("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("5e6f211f-a215-409c-8a6d-5ac0251f66b5")]
21 changes: 21 additions & 0 deletions examples/QuoteOfTheMoment.Server/QuoteOfTheMoment.Server.xproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>5e6f211f-a215-409c-8a6d-5ac0251f66b5</ProjectGuid>
<RootNamespace>QuoteOfTheMoment.Server</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>

<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
59 changes: 59 additions & 0 deletions examples/QuoteOfTheMoment.Server/QuoteOfTheMomentServerHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace QuoteOfTheMoment.Server
{
using System;
using System.Text;
using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;

public class QuoteOfTheMomentServerHandler : SimpleChannelInboundHandler<DatagramPacket>
{
static readonly Random Random = new Random();

// Quotes from Mohandas K. Gandhi:
static readonly string[] Quotes =
{
"Where there is love there is life.",
"First they ignore you, then they laugh at you, then they fight you, then you win.",
"Be the change you want to see in the world.",
"The weak can never forgive. Forgiveness is the attribute of the strong.",
};

static string NextQuote()
{
int quoteId = Random.Next(Quotes.Length);
return Quotes[quoteId];
}

protected override void ChannelRead0(IChannelHandlerContext ctx, DatagramPacket packet)
{
Console.WriteLine($"Server Received => {packet}");

if (!packet.Content.IsReadable())
{
return;
}

string message = packet.Content.ToString(Encoding.UTF8);
if (message != "QOTM?")
{
return;
}

byte[] bytes = Encoding.UTF8.GetBytes("QOTM: " + NextQuote());
IByteBuffer buffer = Unpooled.WrappedBuffer(bytes);
ctx.WriteAsync(new DatagramPacket(buffer, packet.Sender));
}

public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
Console.WriteLine("Exception: " + exception);
context.CloseAsync();
}
}
}
3 changes: 3 additions & 0 deletions examples/QuoteOfTheMoment.Server/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"port": "7686"
}
Loading

0 comments on commit 4488596

Please sign in to comment.