Skip to content

Commit

Permalink
Implemented a UDP example to use with NZSmartie.CoAPNet
Browse files Browse the repository at this point in the history
  • Loading branch information
NZSmartie committed Aug 23, 2017
1 parent 2b25172 commit 9037770
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 1 deletion.
27 changes: 27 additions & 0 deletions CoAPNet.Udp/CoAPNet.Udp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
<PackageId>NZSmartie.CoAPNet.Udp</PackageId>
<Version>0.2.3</Version>
<Authors>Roman Vaughan</Authors>
<Company>NZSmartie</Company>
<Description>UDP Socket implementation for NZSmartie.CoAPNet</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<PackageProjectUrl>https://github.com/NZSmartie/CoAP.Net</PackageProjectUrl>
<RepositoryUrl>https://github.com/NZSmartie/CoAP.Net</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>CoAP IoT sensors devices hardware network protocol udp socket</PackageTags>
<PackageReleaseNotes>v0.2.3
- Implemented a UDP example to use with NZSmartie.CoAPNet</PackageReleaseNotes>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
<FileVersion>0.2.3.0</FileVersion>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\CoAPNet\CoAPNet.csproj" />
</ItemGroup>

</Project>
116 changes: 116 additions & 0 deletions CoAPNet.Udp/CoapUdpEndPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace CoAPNet.Udp
{
public class CoapConnectionInformation : ICoapConnectionInformation
{
public ICoapEndpoint LocalEndpoint { get; set; }
public ICoapEndpoint RemoteEndpoint { get; set; }
}

public class CoapUdpEndPoint : ICoapEndpoint
{
private readonly IPEndPoint _endpoint;
private readonly IPAddress _multicastAddressIPv4 = IPAddress.Parse(Coap.MulticastIPv4);
private readonly IPAddress[] _multicastAddressIPv6 = Enumerable.Range(1,13).Select(n => IPAddress.Parse(Coap.GetMulticastIPv6ForScope(n))).ToArray();

public IPEndPoint Endpoint => (IPEndPoint)Client?.Client.LocalEndPoint ?? _endpoint;

public UdpClient Client { get; private set; }

internal bool Bindable { get; set; } = true;

public Uri BaseUri { get; }

public bool CanReceive => Client?.Client.LocalEndPoint != null;

public bool IsMulticast { get; }

public bool IsSecure => false;

public CoapUdpEndPoint(UdpClient udpClient)
{
Client = udpClient ?? throw new ArgumentNullException(nameof(udpClient));
_endpoint = (IPEndPoint)Client.Client.LocalEndPoint;

BaseUri = new UriBuilder()
{
Scheme = "coap://",
Host = _endpoint.Address.ToString(),
Port = _endpoint.Port != Coap.Port ? _endpoint.Port : -1
}.Uri;
}

public CoapUdpEndPoint(IPEndPoint endpoint)
{
_endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint));
IsMulticast = endpoint.Address.Equals(_multicastAddressIPv4) || _multicastAddressIPv6.Contains(endpoint.Address);

BaseUri = new UriBuilder()
{
Scheme = "coap://",
Host = _endpoint.Address.ToString(),
Port = _endpoint.Port != Coap.Port ? _endpoint.Port : -1
}.Uri;
}

public Task BindAsync()
{
if (Client != null)
throw new InvalidOperationException($"Can not bind {nameof(CoapUdpEndPoint)} as it is already bound");
if(!Bindable)
throw new InvalidOperationException("Can not bind to remote endpoint");

Client = new UdpClient(_endpoint) { EnableBroadcast = true };
return Task.CompletedTask;
}

public void Dispose()
{
Client?.Dispose();
}

public async Task<CoapPacket> ReceiveAsync(CancellationToken token = default (CancellationToken))
{
if (Client == null)
throw new InvalidOperationException();

var result = await Client.ReceiveAsync();
return new CoapPacket
{
Payload = result.Buffer,
Endpoint = new CoapUdpEndPoint(result.RemoteEndPoint) {Bindable = false},
};
}

public async Task SendAsync(CoapPacket packet, CancellationToken token = default (CancellationToken))
{
if (Client == null)
throw new InvalidOperationException();

var udpDestEndpoint = packet.Endpoint as CoapUdpEndPoint;
if (udpDestEndpoint == null)
throw new ArgumentException();

try
{
await Client.SendAsync(packet.Payload, packet.Payload.Length, udpDestEndpoint.Endpoint);
}
catch (SocketException se)
{
Debug.WriteLine($"Failed to send data. {se.GetType().FullName} (0x{se.HResult:x}): {se.Message}");
}
}

public override string ToString()
{
return _endpoint.ToString();
}
}
}
85 changes: 85 additions & 0 deletions CoAPNet.Udp/CoapUdpTransport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace CoAPNet.Udp
{
public class CoapUdpTransportFactory : ICoapTransportFactory
{
public ICoapTransport Create(ICoapEndpoint endPoint, ICoapHandler handler)
{
return new CoapUdpTransport(endPoint as CoapUdpEndPoint ?? throw new InvalidOperationException(), handler);
}
}

public class CoapUdpTransport : ICoapTransport
{
private CoapUdpEndPoint _endPoint;

private readonly ICoapHandler _coapHandler;

private Task _listenTask;

public CoapUdpTransport(CoapUdpEndPoint endPoint, ICoapHandler coapHandler)
{
_endPoint = endPoint;
_coapHandler = coapHandler;
}

public async Task BindAsync()
{
if(_listenTask != null)
throw new InvalidOperationException($"{nameof(CoapUdpTransport)} has already started");

try
{
await _endPoint.BindAsync().ConfigureAwait(false);

_listenTask = RunRequestsLoopAsync();
}
catch (SocketException e) when (e.SocketErrorCode == SocketError.AddressAlreadyInUse)
{
throw new Exception($"Can not bind to enpoint as address is already in use. {e.Message}", e);
}
}

public async Task UnbindAsync()
{
if (_endPoint == null)
return;

var endPoint = _endPoint;
_endPoint = null;

endPoint.Dispose();
await _listenTask.ConfigureAwait(false);
_listenTask = null;
}

public Task StopAsync()
{
return Task.CompletedTask;
}

private async Task RunRequestsLoopAsync()
{
try
{
while (true)
{
var request = await _endPoint.ReceiveAsync();
_ = _coapHandler.ProcessRequestAsync(new CoapConnectionInformation
{
LocalEndpoint = _endPoint,
RemoteEndpoint = request.Endpoint,
}, request.Payload);
}
}
catch (Exception)
{
if (_endPoint != null)
throw;
}
}
}
}
19 changes: 18 additions & 1 deletion CoAPNet.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.16
VisualStudioVersion = 15.0.26730.8
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoAPNet", "CoAPNet\CoAPNet.csproj", "{0D43122B-4D99-41B1-B855-5D780F85464A}"
EndProject
Expand All @@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Readme.md = Readme.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoAPNet.Udp", "CoAPNet.Udp\CoAPNet.Udp.csproj", "{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -49,8 +51,23 @@ Global
{F0CD0A8A-9772-4482-9526-D2EEE29E37D1}.Release|x64.Build.0 = Release|Any CPU
{F0CD0A8A-9772-4482-9526-D2EEE29E37D1}.Release|x86.ActiveCfg = Release|Any CPU
{F0CD0A8A-9772-4482-9526-D2EEE29E37D1}.Release|x86.Build.0 = Release|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Debug|x64.ActiveCfg = Debug|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Debug|x64.Build.0 = Debug|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Debug|x86.ActiveCfg = Debug|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Debug|x86.Build.0 = Debug|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Release|Any CPU.Build.0 = Release|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Release|x64.ActiveCfg = Release|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Release|x64.Build.0 = Release|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Release|x86.ActiveCfg = Release|Any CPU
{5A4DEFAA-29CB-432F-83BF-5970304DCD6C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B787D906-3CA5-478D-BCE3-E8B7556B55B8}
EndGlobalSection
EndGlobal

0 comments on commit 9037770

Please sign in to comment.