TCP support in Azure IoT Hub TCP support in Azure IoT Hub azure azure

TCP support in Azure IoT Hub


The default Protocol Gateway sample are indeed somewhat confusing because of all the MQTT code.The protocol gateway works by 'simulating' a IoTHub connection for each custom protocol device you connect to the gateway.

To do this translation from the TCP device to an IoTHub device you first need to have a connection to the IoTHub on behalf of the device. This is the gateway part.Below is the core essentials for this IoTHubConnection.

namespace GatewayTest{    using System;    using System.Text;    using System.Threading;    using System.Threading.Tasks;    using DotNetty.Buffers;    using Microsoft.Azure.Devices.ProtocolGateway.Identity;    using Microsoft.Azure.Devices.ProtocolGateway.IotHubClient;    using Microsoft.Azure.Devices.ProtocolGateway.Messaging;    public class IoTHubConnection : IMessagingChannel<IMessage>    {        private readonly string iotHubHostName;        private readonly Func<IDeviceIdentity, Task<IMessagingServiceClient>> deviceClientFactory;        private readonly Func<string, Task> onMessage;        private IMessagingServiceClient deviceClient;        private IDeviceIdentity deviceIdentity;        public IoTHubConnection(            string iotHubHostName,            Func<IDeviceIdentity, Task<IMessagingServiceClient>> deviceClientFactory,            Func<string, Task> onMessage)        {            this.iotHubHostName = iotHubHostName;            this.deviceClientFactory = deviceClientFactory;            this.onMessage = onMessage;        }        public event EventHandler CapabilitiesChanged;        public async Task OpenAsync(string deviceId, string deviceKey)        {            this.deviceIdentity = this.GetDeviceIdentity(deviceId, deviceKey);            if (this.deviceIdentity != UnauthenticatedDeviceIdentity.Instance)            {                this.deviceClient = await this.deviceClientFactory(this.deviceIdentity);                this.deviceClient.BindMessagingChannel(this);            }        }        public async Task CloseAsync()        {            await this.deviceClient.DisposeAsync(null);            this.deviceClient = null;        }        public void Handle(IMessage message)        {            var messageBody = message.Payload.ToString(Encoding.UTF8);            this.onMessage(messageBody);            this.deviceClient.CompleteAsync(message.Id);        }        public Task SendMessage(string message)        {            var buffer = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(message));            var deviceMessage = this.deviceClient.CreateMessage($"devices/{this.deviceIdentity.Id}/messages/events", buffer);            return this.deviceClient.SendAsync(deviceMessage);        }        protected virtual void OnCapabilitiesChanged(EventArgs e)        {            this.CapabilitiesChanged?.Invoke(this, e);        }        private IDeviceIdentity GetDeviceIdentity(string userName, string deviceKey)        {            IotHubDeviceIdentity ideviceIdentity;            if (!IotHubDeviceIdentity.TryParse($"{this.iotHubHostName}/{userName}", out ideviceIdentity))            {                return UnauthenticatedDeviceIdentity.Instance;            }            ideviceIdentity.WithDeviceKey(deviceKey);            return ideviceIdentity;        }    }}

The deviceClientFactory callback method should be implemented as shown below and in this line in the ProtocolGateway repo in Github.

deviceClientFactory = IotHubClient.PreparePoolFactory(    "IotHubConnectionString",    400,    TimeSpan.FromMinutes(3),    iotHubClientSettings,    PooledByteBufferAllocator.Default,    new ConfigurableMessageAddressConverter("TopicNameConversion"));

When a Tcp Device connects to the protocol, you should create an instance of this IoTHubConnection and send messages from the Device to the IoTHubConnection and vica versa.The code below shows a very simple version of how this should be done.

private const int BufferSize = 1024;private byte[] buffer = new byte[BufferSize];private IoTHubConnection ioTHubConnection;private NetworkStream stream;private async Task Start(){    listener = new TcpListener(IPAddress.Any, port);    listener.Start();    var client = await listener.AcceptTcpClientAsync();    ioTHubConnection = new IoTHubConnection("IoTHubName", deviceClientFactory, OnIoTHubMessage);    stream = client.GetStream();    // Read DeviceId and DeviceKey from some sort of StartConnection-message send by the TcpClient.    await ioTHubConnection.OpenAsync("DeviceId", "DeviceKey");    stream.BeginRead(buffer, 0, BufferSize, ReadTcpStreamCallback, null);}private void ReadTcpStreamCallback(IAsyncResult ar){    var bytesRead = stream.EndRead(ar);    if (bytesRead > 0)    {        var message = System.Text.Encoding.ASCII.GetString(result);        ioTHubConnection.SendMessage(message);        // Read again.        stream.BeginRead(buffer, 0, BufferSize, ReadTcpStreamCallback, null);    }}private async Task OnIoTHubMessage(string message){    // Potentially do some translation on the IoTHub message    // and send it to the Device    var byteData = Encoding.UTF8.GetBytes(message);    stream.BeginWrite(byteData, 0, byteData.Length, SendTcpCallback, null);}private void SendTcpCallback(IAsyncResult ar){    stream.EndWrite(ar);}


I know I am late to this conversation. However I have interesting add on or might be a solution for some.

Azure IoT Gateway is now known as Azure IoT Edge, this is clearly mentioned in the following Azure github repo

enter image description here

https://github.com/Azure/iot-edge-modbus.git

On the other hand, Azure IoT Edge supports TCP for some protocols which can be found in the following links

  1. https://docs.microsoft.com/en-us/azure/iot-edge/deploy-modbus-gateway
  2. https://docs.microsoft.com/en-us/azure/iot-edge/iot-edge-as-gateway