azure-iot-sdk-rs icon indicating copy to clipboard operation
azure-iot-sdk-rs copied to clipboard

Keep Alive Signal required for C2D receiver

Open StijnVandendriessche1 opened this issue 3 years ago • 6 comments

I am trying to build a stable IOTHub Client with this crate, so long it has been a realy user-friendly experience. However, I found out that if a receiver (client.get_receiver()) doesn't get a keep-alive-signal (C2D) every 10-15 seconds, the program comes in an endless loop of the following error.

FixedHeaderError(IoError(Custom { kind: UnexpectedEof, error: "early eof" }))

At the bottom you will find a test script that will recreate the error. I have looked through the source code, but don't seem to find the problem.

use azure_iot_sdk::{IoTHubClient, DeviceKeyTokenSource, MessageType, Message, DirectMethodResponse};

#[tokio::main] async fn main() -> azure_iot_sdk::Result<()> { let iothub_hostname = "nope.azure-devices.net"; let device_id = "nope"; let key = "nope"; let token_source = DeviceKeyTokenSource::new(iothub_hostname, device_id, key).unwrap(); let mut send_client = IoTHubClient::new(iothub_hostname, device_id.into(), token_source).await?; println!("Client aangemaakt");

let mut recv_client = send_client.clone();
let mut recv = recv_client.get_receiver().await;
let receive_loop = async 
{
    while let Some(msg) = recv.recv().await 
    {
        match msg 
        {
            MessageType::C2DMessage(msg) => println!("Received message: {:?}", msg),
            MessageType::DesiredPropertyUpdate(msg) => println!("Received property update: {:?}", msg),
            MessageType::DirectMethod(msg) => println!("Received Direct method: {:?}", msg),
            MessageType::ErrorReceive(er) => eprintln!("Received error: {:?}", er)
        }
    }
};

let msg = Message::new(b"Hello, world!".to_vec());
let sender = send_client.send_message(msg);

futures::join!(receive_loop, sender);

Ok(())

}`

StijnVandendriessche1 avatar Jan 23 '21 11:01 StijnVandendriessche1

Thank you for this @StijnVandendriessche I would guess the problem is that the library currently doesn't ping the IoT Hub host, so could timeout and drop the tcp connection. Secondly, the current tcp loop doesn't have logic to reconnect on disconnection.

I'll try and have a look to see how we could introduce these at my next chance

damienpontifex avatar Jan 26 '21 12:01 damienpontifex

@StijnVandendriessche1 sorry for the inactivity. I've added a spawned task that regularly pings the hub (currently ever 15s). Let me know if the latest code resolves this issue for you.

I still have to add in reconnect logic, but hopefully the ping should keep the connection alive for longer in your scenario as a first step

damienpontifex avatar May 02 '21 06:05 damienpontifex

I too am running into the exact issue as @StijnVandendriessche1. I had been using the 0.7.1 crate and not only would I get this error after about 20 seconds, but I could only receive messages already queued with the IoT Hub. Upgrading to 0.8.0, I can now get messages while the client is running, but after about 30 seconds of no messages, I too get an endless loop of:

Error received FixedHeaderError(IoError(Kind(UnexpectedEof)))

Happy to give more info if it would help.

5/25: More info - To be clear, my client under test that throws those errors is receive only. Tested the temperature-client in /examples and it ran without so much as a hiccup for 30 minutes. But that sends and receives, keeping the connection hot. The issue appears to be when it gets cold and the connection starts getting these errros in response to ping:

{ code: 32, kind: BrokenPipe, message: "Broken pipe" }

toddpenland avatar May 25 '21 06:05 toddpenland

Hi,

i am running into the same problem. I am using the 0.8.0 crate. First I only was sending data to the Azure IoT Hub. Now tried to receive data which is also working for the first 10 seconds. After that, the error msgs are firing.

Do you need support with this issue? For me it is really important to make it run.

A work around is of course to send from the Hub in periods, that seems to keep the connection alive.

FabianBruenger avatar May 31 '21 17:05 FabianBruenger

Heya! Are there any updates on this?

I keep running into this issue. I can see in the logs that PINGREQs are being sent and that PINGRESP are being received. It then DCs after about 30s and on the next try to PINGREQ it errors out and spams error messages.

TheNeikos avatar Apr 20 '22 11:04 TheNeikos

Hi, my apologies for the inactivity. In my initial issue I stated that the problem didn't appear when I sent a C2D message every 10-15 seconds. I also tried this with a D2CMessage (similar to the ping task), but this didn't do the trick. In my final proof of concept project, I used a "keep alive" message that is sent from the IOT hub to the device every 13 seconds. To accomplish this, I used aa time trigger on an IOT hub function. For me this solved the problem. The code (cloud and device) that I used for this is included underneath.

send keep alive C2DMessage

`using System; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Host; using Microsoft.Extensions.Logging; using Microsoft.Azure.Devices; using System.Text;

namespace CocktailMakerBackend { public static class KeepAliveIOT { [FunctionName("KeepAliveIOT")] public static async void Run([TimerTrigger("0/13 * * * * *")]TimerInfo myTimer, ILogger log) { try { RegistryManager manager = RegistryManager.CreateFromConnectionString(Environment.GetEnvironmentVariable("IOTHubMainConnectionstring")); var twin = await manager.GetTwinAsync("testpi"); if (twin.ConnectionState.ToString() == "Connected") { log.LogInformation("fired"); ServiceClient sCli; string iotconstr = Environment.GetEnvironmentVariable("IOTHubMainConnectionstring"); sCli = ServiceClient.CreateFromConnectionString(iotconstr); string device = "testpi"; var message = new Message(Encoding.ASCII.GetBytes("keep alive")); await sCli.SendAsync(device, message); } } catch(Exception ex) { log.LogError(ex + " --------> keep alive"); } } } }`

receive keep alive message

let mut receiver = recv_client.get_receiver().await; let receive_loop = async { loop { while let Some(msg) = receiver.recv().await { match msg {} } } };

I do believe that there is a solution for this problem that can be solely implemented on the client side, but I stopped looking for it. My apologies.

StijnVandendriessche1 avatar Apr 21 '22 06:04 StijnVandendriessche1