react-native-zeroconf icon indicating copy to clipboard operation
react-native-zeroconf copied to clipboard

Unspecified error when phone put to sleep during zeroconf scan

Open jdpigeon opened this issue 9 years ago • 21 comments

Been using the library with a recent project and very satisfied with how easy to implement it was.

However, in tightening things up recently, I've noticed that zeroconf seems to be responsible for two types of crashes in my app.

  1. When the app is minimized as the user navigates to the home screen. Error is encountered on resuming the app.

  2. When the phone is put to sleep by pressing the power button. Error once again encountered on resuming the app.

In both cases, the error is an 'Uncaught, unspecified "error" event. (10)' with the following log

Unhandled JS Exception: Uncaught, unspecified "error" event. (-72000)
2017-01-05 16:01:20.156716 Tavern[310:44322] [] nw_socket_get_input_frames recvmsg(fd 7, 1024 bytes): [57] Socket is not connected
2017-01-05 16:01:20.241348 Tavern[310:44322] [] nw_socket_get_input_frames recvmsg(fd 10, 1024 bytes): [57] Socket is not connected
2017-01-05 16:01:20.248243 Tavern[310:44322] [] nw_endpoint_handler_add_write_request [8.1 192.168.1.25:8081 failed socket-flow (satisfied)] cannot accept write requests
2017-01-05 16:01:20.249545 Tavern[310:43888] [] __tcp_connection_write_eof_block_invoke Write close callback received error: [22] Invalid argument

Both errors only occur when a react-native-zeroconf scan is actively running. Disabling zeroconf or stopping the scan prevents the errors.

I was able to work around the first type of crash by writing some code to stop the zeroconf scan when the app is moved to the background. This was easy enough to do with React Native AppState. However, it seems significantly trickier to write such a workaround for handling when the phone's screen is turned off, at least using pure React Native.

Any ideas as to how the library could be updated to prevent these errors when the app state is changed during a zeroconf scan?

jdpigeon avatar Jan 05 '17 21:01 jdpigeon

Hum not sure actually, what does your hook look like exactly? It seems a bit related to #12.

balthazar avatar Jan 05 '17 22:01 balthazar

Hey there,

Thanks for getting back so soon.

We're not doing anything fancy when starting the scan (which is what I assume you're referring to by 'hook'). In addition to registering some listeners (onScanStart, onScanStop, and onGetDevices), this function is called in componentDidMount():

function startZeroConfScan () {
  try {
  zeroconf.scan(type = 'http', protocol = 'tcp', domain = 'local.');
  } catch (e) {parseZeroConfError(e)}
}

With regard to the other issue, this morning I tried toggling the wifi on and off and didn't encounter any crashes. I also notice that the error with the phone coming back from sleep only happened about 50% of the time. I couldn't find any pattern as to what made it either work or fail, however.

jdpigeon avatar Jan 06 '17 17:01 jdpigeon

An update:

I managed to fix the red screen crashes by including a handler for the error event.

The error log are still the same as above, including that [57] Socket is not connected error, which I think is important. I've been running this app on iOS 10.2 and socket issues when the app goes to the background in iOS 10 have been noted elsewhere: http://stackoverflow.com/questions/39743686/web-service-calls-no-longer-work-in-background-when-background-audio-mode-is-ena

jdpigeon avatar Jan 23 '17 18:01 jdpigeon

Hum thanks for the update! Do you have this issue only on physical devices or are you also able to reproduce on simulator? Still not sure what I can do about it.

balthazar avatar Jan 23 '17 19:01 balthazar

I've only seen the error on the one physical device I'm using to develop. I have a feeling based on that stack overflow thread that it may be specifically an iOS 10 issue.

jdpigeon avatar Jan 24 '17 19:01 jdpigeon

I am having the same issue as described here and I do believe it is related to #12 as well.

I am doing a new scan on network change and/or when the app state is changed to "active". I can trigger it by being in the app, double tapping the home button and then choose the app again. If I do this many times quite fast the error eventually appears and the service that is supposed to be resolved is set to nil for some reason.

At the moment I have added a check: if (service != nil) before doing anything with the service and everything seem to work fine. Of course that is not really solving the problem. Do you guys have any suggestions on why the service is nil?

@jdpigeon - why do you think it is related to the "socket is not connected" issue that lots of people got with iOS 10?

pellepersson avatar Feb 13 '17 13:02 pellepersson

@pellepersson Landed a similar change in the latest 0.8.0 version. @SiteFlo and @jamsesso did the same thing in their forks too, and might be interested.

balthazar avatar Mar 25 '17 21:03 balthazar

@jdpigeon Have you had the chance to try reproducing using 0.8.0?

balthazar avatar Apr 08 '17 17:04 balthazar

Just started working on this project again, actually. I'll take a look when I'm in the office on Monday

jdpigeon avatar Apr 08 '17 18:04 jdpigeon

Cool! Have a good weekend 😃

balthazar avatar Apr 08 '17 18:04 balthazar

Still noticing the same problems, unfortunately.

If I don't stop the zeroconf scan before minimizing the app I'll get an Unhandled JS Exception: Uncaught, unspecified "error" event. (-72000).

I'm no longer so sure that my problems related to that iOS10 Socket issue since that error doesn't always pop up at the same time as the zeroconf one.

In any case, I have been able to work around the error by stopping the scan when the app is put to sleep and creating an error handler.

jdpigeon avatar Apr 11 '17 15:04 jdpigeon

Hum, and what happens if you keep scanning with the error handler? I would assume the error get caught right?

balthazar avatar Apr 11 '17 15:04 balthazar

Yup! Error gets caught.

Unfortunately, it looks like the scan stops working properly. Changes to the list of zeroconf devices aren't detected after the error's been thrown until the component is remounted

jdpigeon avatar Apr 18 '17 21:04 jdpigeon

Hey! We are now doing a sanity check on the service to prevent some errors which might catch your issue. Do you mind trying that?

balthazar avatar Sep 07 '17 20:09 balthazar

It's still happening on 0.8.3 ...

edudel avatar Sep 26 '17 01:09 edudel

Same here, and it happens very randomly

rdev avatar Sep 29 '17 07:09 rdev

Hi, any update on this? Having the same issue as listed above.

LaurieWilliamsNZ avatar Jan 10 '18 15:01 LaurieWilliamsNZ

We went around this issue it by stopping all zeroconf operations on app minimize and resuming when app goes back to foreground. No idea if there's an actual fix for that.

rdev avatar Jan 11 '18 07:01 rdev

NSNetServicesUnknownError corresponds to the 72000 status code, which is not really helpful in itself. However, it's likely the OS is putting some of the app net services on pause while in the background and resuming them on foreground, resulting in the error you guys are seeing.

The idea of @rdev is actually a good one. It might be possible to stop it seamlessly inside the module, but while waiting for that people should just pause and resume the scan themselves at the right times in order to avoid this.

balthazar avatar Dec 03 '18 15:12 balthazar

@balthazar @jdpigeon I found a fix/solution to this, you can listen for the "AppState" and remove the listener when the app becomes inactive and re-add them when the app becomes active again.

Here's an example:

import {AppState} from 'react-native';

AppState.addEventListener('change', this.handleAppStateChange.bind(this));
 private appState: string = 'active';

  private handleAppStateChange(nextAppState: string): void {
    if (this.appState === 'active' && nextAppState === 'active') {
      return;
    }

    this.appState = nextAppState;

    if (nextAppState === 'active') {
      return this.zeroConf.addDeviceListeners();
    }

    return this.zeroConf.removeDeviceListeners();
  }

The first line in the function prevents the listener to be added twice (This triggers an error as-well). Hope this helps 😄

DennisSnijder avatar Aug 25 '20 07:08 DennisSnijder

Awesome, @DennisSnijder! That worked for me as well. This is the hook-based variation that I came up with:

const [appState, setAppState] = useState(AppState.currentState);
const previousAppState = usePrevious(appState);
const [rnZeroconf, setZeroConf] = useState<RNZeroconf | null>(null);

const handleAppStateChange = (state: AppStateStatus) => {
    setAppState(state);
};

useEffect(() => {
    AppState.addEventListener('change', handleAppStateChange);
    return () => AppState.removeEventListener('change', handleAppStateChange);
}, []);

useEffect(() => {
    if (appState === 'active' && previousAppState === 'active') {
        return;
    }

    if (appState === 'active') {
        rnZeroconf?.addDeviceListeners();
    }

    rnZeroconf?.removeDeviceListeners();
}, [appState, previousAppState, rnZeroconf]);

with helper:

function usePrevious<T>(value: T) {
  const ref = useRef<T>();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

Vannevelj avatar Nov 30 '20 20:11 Vannevelj