mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-22 01:26:20 +08:00
405 lines
16 KiB
C#
405 lines
16 KiB
C#
/* Copyright (c) 1996-2016, OPC Foundation. All rights reserved.
|
|
The source code in this file is covered under a dual-license scenario:
|
|
- RCL: for OPC Foundation members in good-standing
|
|
- GPL V2: everybody else
|
|
RCL license terms accompanied with this source code. See http://opcfoundation.org/License/RCL/1.00/
|
|
GNU General Public License as published by the Free Software Foundation;
|
|
version 2 of the License are accompanied with this source code. See http://opcfoundation.org/License/GPLv2
|
|
This source code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
using System;
|
|
|
|
namespace Opc.Ua.Bindings
|
|
{
|
|
/// <summary>
|
|
/// Creates a new CustomTransportChannel with ITransportChannel interface.
|
|
/// </summary>
|
|
public class CustomTransportChannelFactory : ITransportChannelFactory
|
|
{
|
|
/// <summary>
|
|
/// The method creates a new instance of a custom transport channel
|
|
/// </summary>
|
|
/// <returns> the transport channel</returns>
|
|
public ITransportChannel Create()
|
|
{
|
|
return new CustomTransportChannel();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wraps the CustomTransportChannel and provides an ITransportChannel implementation.
|
|
/// </summary>
|
|
public class CustomTransportChannel : ITransportChannel
|
|
{
|
|
#region IDisposable Members
|
|
/// <summary>
|
|
/// Frees any unmanaged resources.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// An overrideable version of the Dispose.
|
|
/// </summary>
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
Utils.SilentDispose(m_channel);
|
|
m_channel = null;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region ITransportChannel Members
|
|
/// <summary>
|
|
/// A masking indicating which features are implemented.
|
|
/// </summary>
|
|
public TransportChannelFeatures SupportedFeatures
|
|
{
|
|
get { return TransportChannelFeatures.Open | TransportChannelFeatures.BeginOpen | TransportChannelFeatures.Reconnect | TransportChannelFeatures.BeginSendRequest; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the description for the endpoint used by the channel.
|
|
/// </summary>
|
|
public EndpointDescription EndpointDescription
|
|
{
|
|
get { return m_settings.Description; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the configuration for the channel.
|
|
/// </summary>
|
|
public EndpointConfiguration EndpointConfiguration
|
|
{
|
|
get { return m_settings.Configuration; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the context used when serializing messages exchanged via the channel.
|
|
/// </summary>
|
|
public ServiceMessageContext MessageContext
|
|
{
|
|
get { return m_quotas.MessageContext; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default timeout for requests send via the channel.
|
|
/// </summary>
|
|
public int OperationTimeout
|
|
{
|
|
get { return m_operationTimeout; }
|
|
set { m_operationTimeout = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a secure channel with the endpoint identified by the URL.
|
|
/// </summary>
|
|
/// <param name="url">The URL for the endpoint.</param>
|
|
/// <param name="settings">The settings to use when creating the channel.</param>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
public void Initialize(
|
|
Uri url,
|
|
TransportChannelSettings settings)
|
|
{
|
|
SaveSettings(url, settings);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens a secure channel with the endpoint identified by the URL.
|
|
/// </summary>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
public void Open()
|
|
{
|
|
// opens when the first request is called to preserve previous behavoir.
|
|
}
|
|
|
|
/// <summary>
|
|
/// Begins an asynchronous operation to open a secure channel with the endpoint identified by the URL.
|
|
/// </summary>
|
|
/// <param name="callback">The callback to call when the operation completes.</param>
|
|
/// <param name="callbackData">The callback data to return with the callback.</param>
|
|
/// <returns>
|
|
/// The result which must be passed to the EndOpen method.
|
|
/// </returns>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <seealso cref="Open"/>
|
|
public IAsyncResult BeginOpen(AsyncCallback callback, object callbackData)
|
|
{
|
|
lock (m_lock)
|
|
{
|
|
// create the channel.
|
|
m_channel = new TcpClientChannel(
|
|
Guid.NewGuid().ToString(),
|
|
m_bufferManager,
|
|
new TcpMessageSocketFactory(),
|
|
m_quotas,
|
|
m_settings.ClientCertificate,
|
|
m_settings.ServerCertificate,
|
|
m_settings.Description);
|
|
|
|
// begin connect operation.
|
|
return m_channel.BeginConnect(this.m_url, m_operationTimeout, callback, callbackData);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Completes an asynchronous operation to open a secure channel.
|
|
/// </summary>
|
|
/// <param name="result">The result returned from the BeginOpen call.</param>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <seealso cref="Open"/>
|
|
public void EndOpen(IAsyncResult result)
|
|
{
|
|
m_channel.EndConnect(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Closes any existing secure channel and opens a new one.
|
|
/// </summary>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <remarks>
|
|
/// Calling this method will cause outstanding requests over the current secure channel to fail.
|
|
/// </remarks>
|
|
public void Reconnect()
|
|
{
|
|
Utils.Trace("CustomTransportChannel RECONNECT: Reconnecting to {0}.", m_url);
|
|
|
|
lock (m_lock)
|
|
{
|
|
// the new channel must be created first because WinSock will reuse sockets and this
|
|
// can result in messages sent over the old socket arriving as messages on the new socket.
|
|
// if this happens the new channel is shutdown because of a security violation.
|
|
TcpClientChannel channel = m_channel;
|
|
m_channel = null;
|
|
|
|
// reconnect.
|
|
OpenOnDemand();
|
|
|
|
// begin connect operation.
|
|
IAsyncResult result = m_channel.BeginConnect(m_url, m_operationTimeout, null, null);
|
|
m_channel.EndConnect(result);
|
|
|
|
// close existing channel.
|
|
if (channel != null)
|
|
{
|
|
try
|
|
{
|
|
channel.Close(1000);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// do nothing.
|
|
}
|
|
finally
|
|
{
|
|
channel.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Begins an asynchronous operation to close the existing secure channel and open a new one.
|
|
/// </summary>
|
|
/// <param name="callback">The callback to call when the operation completes.</param>
|
|
/// <param name="callbackData">The callback data to return with the callback.</param>
|
|
/// <returns>
|
|
/// The result which must be passed to the EndReconnect method.
|
|
/// </returns>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <seealso cref="Reconnect"/>
|
|
public IAsyncResult BeginReconnect(AsyncCallback callback, object callbackData)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Completes an asynchronous operation to close the existing secure channel and open a new one.
|
|
/// </summary>
|
|
/// <param name="result">The result returned from the BeginReconnect call.</param>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <seealso cref="Reconnect"/>
|
|
public void EndReconnect(IAsyncResult result)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Closes the secure channel.
|
|
/// </summary>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
public void Close()
|
|
{
|
|
if (m_channel != null)
|
|
{
|
|
lock (m_lock)
|
|
{
|
|
if (m_channel != null)
|
|
{
|
|
m_channel.Close(1000);
|
|
m_channel = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Begins an asynchronous operation to close the secure channel.
|
|
/// </summary>
|
|
/// <param name="callback">The callback to call when the operation completes.</param>
|
|
/// <param name="callbackData">The callback data to return with the callback.</param>
|
|
/// <returns>
|
|
/// The result which must be passed to the EndClose method.
|
|
/// </returns>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <seealso cref="Close"/>
|
|
public IAsyncResult BeginClose(AsyncCallback callback, object callbackData)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Completes an asynchronous operation to close the secure channel.
|
|
/// </summary>
|
|
/// <param name="result">The result returned from the BeginClose call.</param>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <seealso cref="Close"/>
|
|
public void EndClose(IAsyncResult result)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends a request over the secure channel.
|
|
/// </summary>
|
|
/// <param name="request">The request to send.</param>
|
|
/// <returns>The response returned by the server.</returns>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
public IServiceResponse SendRequest(IServiceRequest request)
|
|
{
|
|
IAsyncResult result = BeginSendRequest(request, null, null);
|
|
return EndSendRequest(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Begins an asynchronous operation to send a request over the secure channel.
|
|
/// </summary>
|
|
/// <param name="request">The request to send.</param>
|
|
/// <param name="callback">The callback to call when the operation completes.</param>
|
|
/// <param name="callbackData">The callback data to return with the callback.</param>
|
|
/// <returns>
|
|
/// The result which must be passed to the EndSendRequest method.
|
|
/// </returns>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <seealso cref="SendRequest"/>
|
|
public IAsyncResult BeginSendRequest(IServiceRequest request, AsyncCallback callback, object callbackData)
|
|
{
|
|
TcpClientChannel channel = m_channel;
|
|
|
|
if (channel == null)
|
|
{
|
|
lock (m_lock)
|
|
{
|
|
if (m_channel == null)
|
|
{
|
|
OpenOnDemand();
|
|
}
|
|
|
|
channel = m_channel;
|
|
}
|
|
}
|
|
|
|
return channel.BeginSendRequest(request, m_operationTimeout, callback, callbackData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Completes an asynchronous operation to send a request over the secure channel.
|
|
/// </summary>
|
|
/// <param name="result">The result returned from the BeginSendRequest call.</param>
|
|
/// <returns></returns>
|
|
/// <exception cref="ServiceResultException">Thrown if any communication error occurs.</exception>
|
|
/// <seealso cref="SendRequest"/>
|
|
public IServiceResponse EndSendRequest(IAsyncResult result)
|
|
{
|
|
TcpClientChannel channel = m_channel;
|
|
|
|
if (channel == null)
|
|
{
|
|
throw ServiceResultException.Create(StatusCodes.BadSecureChannelClosed, "Channel has been closed.");
|
|
}
|
|
|
|
return channel.EndSendRequest(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the settings so the channel can be opened later.
|
|
/// </summary>
|
|
/// <param name="url">The URL.</param>
|
|
/// <param name="settings">The settings.</param>
|
|
private void SaveSettings(Uri url, TransportChannelSettings settings)
|
|
{
|
|
// save the settings.
|
|
m_url = url;
|
|
m_settings = settings;
|
|
m_operationTimeout = settings.Configuration.OperationTimeout;
|
|
|
|
// initialize the quotas.
|
|
m_quotas = new TcpChannelQuotas();
|
|
|
|
m_quotas.MaxBufferSize = m_settings.Configuration.MaxBufferSize;
|
|
m_quotas.MaxMessageSize = m_settings.Configuration.MaxMessageSize;
|
|
m_quotas.ChannelLifetime = m_settings.Configuration.ChannelLifetime;
|
|
m_quotas.SecurityTokenLifetime = m_settings.Configuration.SecurityTokenLifetime;
|
|
|
|
m_quotas.MessageContext = new ServiceMessageContext();
|
|
|
|
m_quotas.MessageContext.MaxArrayLength = m_settings.Configuration.MaxArrayLength;
|
|
m_quotas.MessageContext.MaxByteStringLength = m_settings.Configuration.MaxByteStringLength;
|
|
m_quotas.MessageContext.MaxMessageSize = m_settings.Configuration.MaxMessageSize;
|
|
m_quotas.MessageContext.MaxStringLength = m_settings.Configuration.MaxStringLength;
|
|
m_quotas.MessageContext.NamespaceUris = m_settings.NamespaceUris;
|
|
m_quotas.MessageContext.ServerUris = new StringTable();
|
|
m_quotas.MessageContext.Factory = m_settings.Factory;
|
|
|
|
m_quotas.CertificateValidator = settings.CertificateValidator;
|
|
|
|
// create the buffer manager.
|
|
m_bufferManager = new BufferManager("Client", (int)Int32.MaxValue, settings.Configuration.MaxBufferSize);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens the channel before sending the request.
|
|
/// </summary>
|
|
private void OpenOnDemand()
|
|
{
|
|
// create the channel.
|
|
m_channel = new TcpClientChannel(
|
|
Guid.NewGuid().ToString(),
|
|
m_bufferManager,
|
|
new TcpMessageSocketFactory(),
|
|
m_quotas,
|
|
m_settings.ClientCertificate,
|
|
m_settings.ServerCertificate,
|
|
m_settings.Description);
|
|
}
|
|
#endregion
|
|
|
|
#region Private Fields
|
|
private object m_lock = new object();
|
|
private Uri m_url;
|
|
private int m_operationTimeout;
|
|
private TransportChannelSettings m_settings;
|
|
private TcpChannelQuotas m_quotas;
|
|
private BufferManager m_bufferManager;
|
|
private TcpClientChannel m_channel;
|
|
#endregion
|
|
}
|
|
}
|