Click or drag to resize

ProtocolInstance Class

This is one instance of a protocol connection (either a client or a server connection), or a protocol server. This class represents one link in the overall protocol stream. Created by Protocol, it manages the protocol for just this link in the stream. Uses the services of, and provides services to, its downstream and upstream instances.
Inheritance Hierarchy

Namespace:  Demo3D.Net
Assembly:  Demo3D.IO (in Demo3D.IO.dll) Version: 18.03.00
Syntax
C#
public abstract class ProtocolInstance : ProtocolSocket

The ProtocolInstance type exposes the following members.

Constructors
  NameDescription
Protected methodProtocolInstance
Constructs a new ProtocolInstance.
Top
Properties
  NameDescription
Public propertyAddress
The protocol address used by this instance.
(Overrides ProtocolSocketAddress.)
Public propertyDownStream
The downstream socket.
(Inherited from ProtocolSocket.)
Public propertyID
A description of this instance.
(Overrides ProtocolSocketID.)
Protected propertyInternalRunning
Indicates whether the instance is still running.
Public propertyLog
Log messages.
(Inherited from ProtocolSocket.)
Public propertyProtocol
The protocol.
Public propertyProtocolProperties
Protocol property bag.
Public propertyRunning
Returns whether the instance is still running.
(Overrides ProtocolSocketRunning.)
Top
Methods
  NameDescription
Public methodAddAspect
Adds an aspect to a socket.
(Inherited from ProtocolSocket.)
Public methodAddOrUpdateAspect
Adds or updates an aspect of the socket.
(Inherited from ProtocolSocket.)
Public methodBeginEditAsync
Start batch editing.
(Inherited from ProtocolSocket.)
Public methodCloseAsync
Closes the socket.
(Overrides ProtocolSocket.CloseAsync(Boolean).)
Public methodEndEditAsync
End batch editing.
(Inherited from ProtocolSocket.)
Public methodFindAspect(Type)
Returns an aspect of the given type, or return null.
(Inherited from ProtocolSocket.)
Public methodFindAspectT
Returns an aspect of the given type, or return null.
(Inherited from ProtocolSocket.)
Public methodFindService(Type, ServiceBindingFlags, String)
Returns an object that implements a specific API, or null. For example, an IO API such as IPacketIOService.
(Overrides ProtocolSocketFindService(Type, ServiceBindingFlags, String).)
Public methodFindServiceT(ServiceBindingFlags, String)
Returns an object that implements a specific API, or null. For example, an IO API such as IPacketIOService.
(Inherited from ProtocolSocket.)
Protected methodGetDownStream
Returns the downstream socket.
(Inherited from ProtocolSocket.)
Public methodGetHead
Returns the protocol head.
(Overrides ProtocolSocketGetHead.)
Public methodGetOrAddAspect
Adds an aspect to a socket by using the specified function, if the key does not already exist.
(Inherited from ProtocolSocket.)
Public methodGetServiceT
Returns an object that implements a specific API, or throws an exception. For example, an IO API such as IPacketIOService.
(Inherited from ProtocolSocket.)
Public methodGetStream
Returns the head of the protocol stream (the first protocol instance).
(Inherited from ProtocolSocket.)
Protected methodInitializeAsync
When overridden in a derived class, initializes the socket.
(Inherited from ProtocolSocket.)
Protected methodInternalCloseAsync
Performs a controlled close.
Protected methodInternalOpenAsync
Opens or reopens the instance.
Protected methodInternalShutdownAsync
Forcibly shuts down the underlying protocol, causing all other users to throw an error.
Protected methodNotifyEditBegunAsync
Notify EditBegunAsync raised.
(Overrides ProtocolSocketNotifyEditBegunAsync(Boolean).)
Protected methodNotifyEditEndedAsync
Notify EditEndedAsync raised.
(Overrides ProtocolSocketNotifyEditEndedAsync(Boolean).)
Protected methodNotifyPropertyChanged(PropertyChangedEventArgs)
Raises the PropertyChanged event.
(Inherited from ProtocolSocket.)
Protected methodNotifyPropertyChanged(String)
Raises the PropertyChanged event.
(Inherited from ProtocolSocket.)
Protected methodOpenAsync
Opens (or reopens) the socket.
(Overrides ProtocolSocketOpenAsync(Boolean, Flags).)
Public methodRegisterClosing
Registers a function to call when Close has been called but before the socket has been closed.
(Inherited from ProtocolSocket.)
Public methodRegisterOpen
Registers a function to call after the socket is opened, but before the OnOpenedAsync event is fired.
(Inherited from ProtocolSocket.)
Public methodRemoveAspect
Removes an aspect from a socket.
(Inherited from ProtocolSocket.)
Public methodSetDownStream
Sets the downstream socket.
(Overrides ProtocolSocketSetDownStream(ProtocolSocket).)
Public methodSetLog
Sets the current log.
(Inherited from ProtocolSocket.)
Public methodShutdown(Boolean)
Forcibly shuts down the socket, without logging an error.
(Inherited from ProtocolSocket.)
Public methodShutdown(Exception)
Forcibly shuts down the socket, and then logs an error.
(Inherited from ProtocolSocket.)
Public methodShutdown(String, Boolean)
Forcibly shuts down the socket, and then logs an error.
(Inherited from ProtocolSocket.)
Public methodShutdown(LogMessageLogLevel, String, Object)
Forcibly shuts down the socket, and then logs an error.
(Inherited from ProtocolSocket.)
Public methodShutdownAsync(Boolean, Boolean)
Forcibly shuts down the socket, without logging an error.
(Inherited from ProtocolSocket.)
Public methodShutdownAsync(Boolean, Exception)
Forcibly shuts down the socket, and then logs an error.
(Inherited from ProtocolSocket.)
Public methodShutdownAsync(Boolean, String, Boolean)
Forcibly shuts down the socket, and then logs an error.
(Inherited from ProtocolSocket.)
Public methodShutdownAsync(Boolean, LogMessageLogLevel, String, Object)
Forcibly shuts down the socket, and then logs an error.
(Inherited from ProtocolSocket.)
Public methodToString
Returns a description of this socket.
(Overrides ProtocolSocketToString.)
Protected methodTryCreateService
Returns an object that implements a specific API, or null. For example, an IO API such as IPacketIOService.
Public methodUnregisterClosing
Unregister a previously registered function.
(Inherited from ProtocolSocket.)
Public methodUnregisterOpen
Unregister a previously registered function.
(Inherited from ProtocolSocket.)
Top
Events
  NameDescription
Public eventEditBegunAsync
Raised on the first call to BeginEdit.
(Inherited from ProtocolSocket.)
Public eventEditEndedAsync
Raised on the last call to EndEdit.
(Inherited from ProtocolSocket.)
Public eventOnClosedAsync
Occurs after the socket is closed.
(Inherited from ProtocolSocket.)
Public eventOnDisposed
Occurs when the socket is disposed. A socket is disposed when it's closed and uncached from the connection registry. User script may retain a reference, and may resurrect the socket by calling OpenAsync(Boolean, OpenFlags).
(Inherited from ProtocolSocket.)
Public eventOnOpenedAsync
Occurs after the socket is opened.
(Inherited from ProtocolSocket.)
Public eventOnShutdownAsync
Occurs after the socket is shutdown.
(Inherited from ProtocolSocket.)
Public eventPropertyChanged
Occurs when a property value changes.
(Inherited from ProtocolSocket.)
Top
Examples

The following example shows the most basic implementation of a ProtocolInstance.

C#
using System;
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>
    public 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 Task InternalShutdownAsync(bool sync) {
            // Forcibly shut the connection down.
            return Task.CompletedTask;
        }

        /// <summary>
        /// Performs a controlled close.
        /// </summary>
        protected override Task InternalCloseAsync(bool sync) {
            // Called to close the connection.
            return InternalShutdownAsync(sync);
        }

        // 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>
    sealed 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 IDisposable Register() => Registry.Register(new ExampleProtocol());
    }
}
Examples

Example showing a TCP client.

C#
using System;
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 {
        sealed 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 Task InternalShutdownAsync(bool sync) {
                try { tcpClient?.Client?.Shutdown(SocketShutdown.Both); }
                catch { }
                return Task.CompletedTask;
            }

            protected override async Task InternalCloseAsync(bool sync) {
                if (tcpClient == null) return;

                await InternalShutdownAsync(sync).ConfigureAwait(false);

                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 IDisposable Register() => Registry.Register(new TcpClientProtocol());

        protected override ProtocolInstance NewInstance(ProtocolHead head, ProtocolAddress protocolAddress) {
             return new TcpClientConnection(this, head, protocolAddress);
        }
    }
}
Examples

Example showing a TCP server.

C#
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 {
        sealed 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 Task InternalShutdownAsync(bool sync) {
                try { tcpClient?.Client?.Shutdown(SocketShutdown.Both); }
                catch { }
                return Task.CompletedTask;
            }

            protected override async Task InternalCloseAsync(bool sync) {
                if (tcpClient == null) return;

                await InternalShutdownAsync(sync).ConfigureAwait(false);

                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 Task InternalShutdownAsync(bool sync) {
                try { listener?.Server.Close(); }
                catch { }
                return Task.CompletedTask;
            }

            protected override Task InternalCloseAsync(bool sync) {
                try { listener?.Stop(); }
                catch { }
                listener = null;
                return Task.CompletedTask;
            }

            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 IDisposable Register() => Registry.Register(new TcpServerProtocol());

        protected override ProtocolInstance NewInstance(ProtocolHead head, ProtocolAddress protocolAddress) {
            return new TcpServer(this, head, protocolAddress);
        }
    }
}
See Also