Protocol Class |
Namespace: Demo3D.Net
[TypeConverterAttribute(typeof(ExpandableObjectConverter))] public abstract class Protocol : IDisposable
The Protocol type exposes the following members.
| Name | Description | |
|---|---|---|
| Protocol(String, Type) |
Creates a new protocol.
| |
| Protocol(String, ServiceProvider, ProtocolRequiredService) |
Creates a new protocol.
| |
| Protocol(String, IEnumerableType, ProtocolRequiredService) |
Creates a new protocol.
| |
| Protocol(String, IReadOnlyListChannel, ProtocolRequiredService) |
Creates a new protocol.
| |
| Protocol(String, Type, ProtocolRequiredService) |
Creates a new protocol.
|
| Name | Description | |
|---|---|---|
| Channels |
List of all channels.
| |
| DefaultChannel |
The default channel.
| |
| Name |
The protocol name.
| |
| Registry |
A register of all protocols.
| |
| Required |
Required protocols and services.
| |
| SupportedServices |
Returns a list of supported service types. These are the services that ProtocolSocket.FindService can return.
|
| Name | Description | |
|---|---|---|
| AddChannel |
Adds a channel.
| |
| CreateCanonicalAddress | Returns the full address. Eg add in a default port number. Should return an address that uniquely identifies the peer. The address should explicitly contain any information required to connect (including any defaults), but exclude any connection parameters, routing information, etc, unless required to identify the peer. Ambiguous parts of the address should be standardised - eg where information is case insensitive, it should be converted to lower case. Any parts of the address not required should be removed. (The framework will automatically convert hostnames to an appropriate IP address, and will set the scheme correctly, so no need to modify those parts.) | |
| CreateDownStreamAddress |
Creates the downstream address given an upstream address and a downstream protocol.
Normally this does not need to be overridden, but there are some cases where it's required.
For example, the downstream address returned by COTP for "cotp://host/tsap" would be "tpkt://host:102".
Or you can return null to stop the protocol stack from building downstream protocol instances. You might
do this if you want to take control of building the downstream protocol instances, or nor build them at all.
| |
| Dispose |
Releases all protocol resources.
| |
| FindChannel |
Returns the named channel, or null.
| |
| NewInstance |
Create a new server/client protocol instance.
| |
| RegisterService |
Registers a service.
| |
| RemoveChannel |
Removes a channel.
| |
| SupportsService |
Returns whether a particular service is supported.
| |
| ToString |
Returns the name of the protocol.
(Overrides ObjectToString.) | |
| UnregisterService |
Unregisters a service.
|
The following example shows the most basic implementation of a Protocol.
using System.Threading.Tasks; using Demo3D.Net; namespace Examples.Net.ExampleProtocol { /// <summary> /// An interface that describes the services (the IO methods) that your protocol supports. /// Typically a protocol would implement one of the standard services (such as <see cref="IPacketIOService"/>). /// </summary> interface IExampleService { // Put whatever methods users of your protocol would expect in here. } /// <summary> /// An instance of a protocol. Must inherit from <see cref="ProtocolInstance"/>, and should /// support a set of services. The simplest way to support services is to implement the /// services interface (<see cref="IExampleService"/>) and to advertise those services in the /// <see cref="ExampleProtocol.NewInstance(ProtocolHead, ProtocolAddress)"/> constructor. /// </summary> /// <remarks> /// See <see cref="TCPExample.Client.TcpClientProtocol.TcpClientConnection"/> for an /// example implementing the TCP protocol. /// </remarks> class ExampleProtocolInstance : ProtocolInstance, IExampleService { public ExampleProtocolInstance(Protocol protocol, ProtocolHead head, ProtocolAddress address) : base(protocol, head, address, false, null) { } /// <summary> /// Initializes the socket. /// </summary> /// <param name="sync">If true, the Task returned must be guaranteed to be complete.</param> protected override Task InitializeAsync(bool sync) { // Any initialization code goes here. return Task.CompletedTask; } /// <summary> /// Indicates whether the instance is still running. /// </summary> protected override bool InternalRunning => base.InternalRunning; // Return true if the connection is established /// <summary> /// Opens or reopens the instance. /// </summary> /// <param name="sync">If true, the Task returned must be guaranteed to be complete.</param> /// <param name="flags">Additional flags.</param> protected override Task InternalOpenAsync(bool sync, Flags flags) { // Open your connection here. return Task.CompletedTask; } /// <summary> /// Forcibly shuts down the underlying protocol, causing all other users to throw an error. /// </summary> protected override void InternalShutdown() { // Forcibly shut the connection down. } /// <summary> /// Performs a controlled close. /// </summary> protected override void InternalClose() { // Called to close the connection. InternalShutdown(); } // Implement any IExampleService methods here. } /// <summary> /// A protocol inherits from <see cref="Protocol"/>. /// </summary> /// See <see cref="TCPExample.Client.TcpClientProtocol"/> for an example implementing the TCP protocol, /// or <see cref="TCPExample.Server.TcpServerProtocol"/> for an example implementing a TCP server. /// </remarks> class ExampleProtocol : Protocol { /// <summary> /// Constructs a new protocol. /// You need to give your protocol a name, and advertise which services it supports. /// </summary> public ExampleProtocol() : base("Example", typeof(IExampleService)) { } /// <summary> /// Create a new server/client protocol instance. /// At the minimum, you need to implement this method to return a new instance of your protocol. /// </summary> /// <param name="head">The socket head which is a required parameter to the ProtocolInstance constructor.</param> /// <param name="protocolAddress">The protocol address.</param> /// <returns>A new ProtocolInstance.</returns> protected override ProtocolInstance NewInstance(ProtocolHead head, ProtocolAddress protocolAddress) { return new ExampleProtocolInstance(this, head, protocolAddress); } /// <summary> /// Registers the protocol with Demo3D.Net. /// You need to call this method somewhere in your code in order to register this protocol with Demo3D.Net. /// </summary> public static void Register() { Registry.Register(new ExampleProtocol()); } } }
Example showing a TCP client.
using System.ComponentModel; using System.IO; using System.Net.Sockets; using System.Threading.Tasks; using Demo3D.IO; using Demo3D.Net; using TcpClient = System.Net.Sockets.TcpClient; namespace Examples.Net.TCPExample.Client { /// <summary> /// TCP address editor. /// </summary> public class TcpAddressEditor : ProtocolAddressPropertyBagEditor { string host; int port; /// <summary> /// The server host. /// </summary> [Description("The server host.")] [Category("Connection")] public string Host { get { return host; } set { if (host != value) { host = value; NotifyPropertyChanged(); } } } /// <summary> /// The server port. /// </summary> [Description("The server port.")] [Category("Connection")] public int Port { get { return port; } set { if (port != value) { port = value; NotifyPropertyChanged(); } } } /// <summary> /// Returns a ProtocolAddress that represents the configuration defined by this object. /// </summary> /// <returns>The protocol address according to the current setting of the editor properties.</returns> public override ProtocolAddress GetAddress() { return new ProtocolAddressBuilder("tcp", host, port).Address; } /// <summary> /// Sets this configuration object properties from the ProtocolAddress given. /// </summary> /// <param name="address">The current address.</param> public override void SetAddress(ProtocolAddress address) { host = address.Host; port = address.Port; } /// <summary> /// Returns the next property that needs to be edited to complete the address. /// </summary> /// <returns>The name of the property, or null.</returns> public override string NextProperty() { if (string.IsNullOrWhiteSpace(this.Host)) return nameof(this.Host); if (this.Port <= 0) return nameof(this.Port); return null; } } [ProtocolAddressEditor(Editor = typeof(TcpAddressEditor))] public sealed class TcpClientProtocol : Protocol { class TcpClientConnection : ProtocolInstance, IByteStreamService { TcpClient tcpClient; NetworkStream stream; internal TcpClientConnection(Protocol protocol, ProtocolHead head, ProtocolAddress peerAddress) : base(protocol, head, peerAddress, true, null) { } protected override bool InternalRunning { get { return tcpClient != null && tcpClient.Connected; } } protected override async Task InternalOpenAsync(bool sync, Flags flags) { string host = this.Address.Host; int port = this.Address.IsDefaultPort ? 0 : this.Address.Port; if (!this.Address.HasHost || port <= 0) { throw Exceptions.ProtocolError("Host or port not configured", this); } tcpClient = new TcpClient(); if (sync) tcpClient.Connect(host, port); else await tcpClient.ConnectAsync(host, port).ConfigureAwait(false); stream = tcpClient.GetStream(); } protected override void InternalShutdown() { try { tcpClient?.Client?.Shutdown(SocketShutdown.Both); } catch { } } protected override void InternalClose() { if (tcpClient == null) return; InternalShutdown(); try { tcpClient?.Client?.Close(); } catch { } try { tcpClient?.Close(); } catch { } stream = null; tcpClient = null; } bool IByteStreamService.DataAvailable { get { return stream.DataAvailable; } } Stream IByteStreamService.Stream { get { return stream; } } } TcpClientProtocol() : base("TCP", typeof(IByteStreamService)) { } public static void Register() { Registry.Register(new TcpClientProtocol()); } protected override ProtocolInstance NewInstance(ProtocolHead head, ProtocolAddress protocolAddress) { return new TcpClientConnection(this, head, protocolAddress); } } }
Example showing a TCP server.
using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; using Demo3D.IO; using Demo3D.Net; using TcpClient = System.Net.Sockets.TcpClient; namespace Examples.Net.TCPExample.Server { public sealed class TcpServerProtocol : Protocol { class TcpServerConnection : ProtocolInstance, IByteStreamService { TcpClient tcpClient; NetworkStream stream; internal TcpServerConnection(TcpClient tcpClient, Protocol protocol, ProtocolHead head, ProtocolAddress peerAddress) : base(protocol, head, peerAddress, true, null) { this.tcpClient = tcpClient; stream = tcpClient.GetStream(); } protected override bool InternalRunning { get { return tcpClient != null && tcpClient.Connected; } } protected override Task InternalOpenAsync(bool sync, Flags flags) { throw new Exception("Cannot reconnect"); } protected override void InternalShutdown() { try { tcpClient?.Client?.Shutdown(SocketShutdown.Both); } catch { } } protected override void InternalClose() { if (tcpClient == null) return; InternalShutdown(); try { tcpClient?.Client?.Close(); } catch { } try { tcpClient?.Close(); } catch { } stream = null; tcpClient = null; } bool IByteStreamService.DataAvailable { get { return stream.DataAvailable; } } Stream IByteStreamService.Stream { get { return stream; } } } sealed class TcpServer : ProtocolInstance, IProtocolServerService { TcpListener listener; internal TcpServer(Protocol protocol, ProtocolHead head, ProtocolAddress protocolAddress) : base(protocol, head, protocolAddress, false, null) { } protected override bool InternalRunning { get { return listener != null; } } protected override async Task InternalOpenAsync(bool sync, Flags flags) { int port = this.Address.IsDefaultPort ? 0 : this.Address.Port; IPAddress ipAddress; switch (this.Address.Host) { case "": case ProtocolAddress.AnyHost: ipAddress = IPAddress.Any; break; default: ipAddress = await IPv4.HostToIPAddressAsync(sync, this.Address.Host).ConfigureAwait(false); break; } listener = new TcpListener(ipAddress, port); listener.Start(); } protected override void InternalShutdown() { try { listener?.Server.Close(); } catch { } } protected override void InternalClose() { try { listener?.Stop(); } catch { } listener = null; } async Task<ProtocolInstance> IProtocolServerService.InternalAcceptAsync(bool sync, ProtocolHead head, ProtocolInstance downStreamConnection) { TcpClient client; if (sync) client = listener.AcceptTcpClient(); else client = await listener.AcceptTcpClientAsync().ConfigureAwait(false); var endPoint = (IPEndPoint)client.Client.RemoteEndPoint; var address = new ProtocolAddressBuilder("tcp", endPoint.Address.ToString(), endPoint.Port).Address; return new TcpServerConnection(client, this.Protocol, head, address); } } TcpServerProtocol() : base("TCP", typeof(IProtocolServerService), typeof(IByteStreamService)) { } public static void Register() { Registry.Register(new TcpServerProtocol()); } protected override ProtocolInstance NewInstance(ProtocolHead head, ProtocolAddress protocolAddress) { return new TcpServer(this, head, protocolAddress); } } }