protected override async Task OnOpenAsync(CancellationToken token) { token.ThrowIfCancellationRequested(); this.sendBuffer = new byte[MinBufferSize]; this.receiveBuffer = new byte[MinBufferSize]; this.tcpClient = new TcpClient { NoDelay = true }; var uri = new UriBuilder(this.RemoteEndpoint.EndpointUrl); await this.tcpClient.ConnectAsync(uri.Host, uri.Port).ConfigureAwait(false); this.stream = this.tcpClient.GetStream(); // send 'hello'. int count; var encoder = new BinaryEncoder(new MemoryStream(this.sendBuffer, 0, MinBufferSize, true, false)); try { encoder.WriteUInt32(null, UaTcpMessageTypes.HELF); encoder.WriteUInt32(null, 0u); encoder.WriteUInt32(null, ProtocolVersion); encoder.WriteUInt32(null, this.LocalReceiveBufferSize); encoder.WriteUInt32(null, this.LocalSendBufferSize); encoder.WriteUInt32(null, this.LocalMaxMessageSize); encoder.WriteUInt32(null, this.LocalMaxChunkCount); encoder.WriteString(null, uri.ToString()); count = encoder.Position; encoder.Position = 4; encoder.WriteUInt32(null, (uint)count); encoder.Position = count; await this.SendAsync(this.sendBuffer, 0, count, token).ConfigureAwait(false); } finally { encoder.Dispose(); } // receive response count = await this.ReceiveAsync(this.receiveBuffer, 0, MinBufferSize, token).ConfigureAwait(false); if (count == 0) { throw new ObjectDisposedException("socket"); } // decode 'ack' or 'err'. var decoder = new BinaryDecoder(new MemoryStream(this.receiveBuffer, 0, count, false, false)); try { var type = decoder.ReadUInt32(null); var len = decoder.ReadUInt32(null); if (type == UaTcpMessageTypes.ACKF) { var remoteProtocolVersion = decoder.ReadUInt32(null); if (remoteProtocolVersion < ProtocolVersion) { throw new ServiceResultException(StatusCodes.BadProtocolVersionUnsupported); } this.RemoteSendBufferSize = decoder.ReadUInt32(null); this.RemoteReceiveBufferSize = decoder.ReadUInt32(null); this.RemoteMaxMessageSize = decoder.ReadUInt32(null); this.RemoteMaxChunkCount = decoder.ReadUInt32(null); return; } else if (type == UaTcpMessageTypes.ERRF) { var statusCode = decoder.ReadUInt32(null); var message = decoder.ReadString(null); if (message != null) { throw new ServiceResultException(statusCode, message); } throw new ServiceResultException(statusCode); } throw new InvalidOperationException("UaTcpTransportChannel.OnOpenAsync received unexpected message type."); } finally { decoder.Dispose(); } }
/// <summary> /// Opens the connection. This includes the hello message handshake. /// </summary> /// <remakes> /// The underlying network client is already opened before this class is /// constructed. The /// connection is closed with <see cref="IAsyncDisposable.DisposeAsync"/> /// method. /// </remakes> /// <param name="protocolVersion">The protocol version.</param> /// <param name="localOptions">The requested transport connection options.</param> /// <param name="token">A cancellation token used to propagate notification that this operation should be canceled.</param> /// <returns>The transport connection options to be used.</returns> /// <seealso href="https://reference.opcfoundation.org/v104/Core/docs/Part6/7.1.2/">OPC UA specification Part 6: Mappings, 7.1.2</seealso> public async Task <TransportConnectionOptions> OpenAsync(uint protocolVersion, TransportConnectionOptions localOptions, CancellationToken token) { var sendBuffer = new byte[MinBufferSize]; var receiveBuffer = new byte[MinBufferSize]; // send 'hello'. int count; var encoder = new BinaryEncoder(new MemoryStream(sendBuffer, 0, MinBufferSize, true, false)); try { encoder.WriteUInt32(null, UaTcpMessageTypes.HELF); encoder.WriteUInt32(null, 0u); encoder.WriteUInt32(null, protocolVersion); encoder.WriteUInt32(null, localOptions.ReceiveBufferSize); encoder.WriteUInt32(null, localOptions.SendBufferSize); encoder.WriteUInt32(null, localOptions.MaxMessageSize); encoder.WriteUInt32(null, localOptions.MaxChunkCount); encoder.WriteString(null, Uri.ToString()); count = encoder.Position; encoder.Position = 4; encoder.WriteUInt32(null, (uint)count); encoder.Position = count; await SendAsync(sendBuffer, 0, count, token).ConfigureAwait(false); } finally { encoder.Dispose(); } // receive response count = await ReceiveAsync(receiveBuffer, 0, MinBufferSize, token).ConfigureAwait(false); if (count == 0) { throw new ObjectDisposedException("socket"); } // decode 'ack' or 'err'. var decoder = new BinaryDecoder(new MemoryStream(receiveBuffer, 0, count, false, false)); try { var type = decoder.ReadUInt32(null); var len = decoder.ReadUInt32(null); if (type == UaTcpMessageTypes.ACKF) { var remoteProtocolVersion = decoder.ReadUInt32(null); if (remoteProtocolVersion < protocolVersion) { throw new ServiceResultException(StatusCodes.BadProtocolVersionUnsupported); } var remoteOptions = new TransportConnectionOptions { SendBufferSize = decoder.ReadUInt32(null), ReceiveBufferSize = decoder.ReadUInt32(null), MaxMessageSize = decoder.ReadUInt32(null), MaxChunkCount = decoder.ReadUInt32(null) }; return(remoteOptions); } else if (type == UaTcpMessageTypes.ERRF) { var statusCode = decoder.ReadUInt32(null); var message = decoder.ReadString(null); if (message != null) { throw new ServiceResultException(statusCode, message); } throw new ServiceResultException(statusCode); } throw new InvalidOperationException($"{nameof(UaClientConnection)}.{nameof(OpenAsync)} received unexpected message type."); } finally { decoder.Dispose(); } }