Rapsssito / react-native-tcp-socket

React Native TCP socket API for Android, iOS & macOS with SSL/TLS support.
MIT License
315 stars 80 forks source link

Starting a Server always returns EADDRINUSE on every port #88

Closed h110m closed 3 years ago

h110m commented 3 years ago

Description

I just followed the example from the npm page for creating a tcp server. If i try to run this it always loggs following: image I've tried multiple ports but nothing helped. I always get the "Adress already in use" error.

Steps to reproduce

Steps to reproduce the behavior:

  1. Start a new RN app with the example from the React Nativ Get Started Documentation
  2. Download and install react-nativ-tcp-socket as shown on the github page
  3. Implement the Server example as shown in the image in the Description section of this bug report
  4. Start the app on youre smartphone with npx react-native run-android
  5. A node winow should have poped up where you can now see logs and bundle infos for debugging. It also should now show the error "adress already in use"

The Code of the App:

import * as React from 'react';
import {Text, View} from 'react-native';
import TcpSocket from 'react-native-tcp-socket';

export interface IMainViewProps {}

export interface IMainViewState {
  log: string;
}

const Port = 12345;

export default class MainView extends React.Component<
  IMainViewProps,
  IMainViewState
> {
  constructor(props: IMainViewProps) {
    super(props);

    this.state = {
      log: '',
    };
  }

  writeToLog(msg: string, callback?: () => void) {
    this.setState({log: this.state.log + msg + '\n'}, callback);
  }

  componentDidMount() {
    this.writeToLog('Starting Server on port ' + Port + '...', () => {
      const server = TcpSocket.createServer((socket) => {
        socket.on('data', (data) => {
          this.writeToLog(data);
        });

        socket.on('error', (error) => {
          console.log('An error ocurred with client socket ', error);
        });

        socket.on('close', (error) => {
          console.log('Closed connection with ', socket.address());
        });
      }).listen({port: Port, host: 'localhost'});

      server.on('error', (error) => {
        console.log('An error ocurred with the server', error);
      });

      server.on('close', () => {
        console.log('Server closed connection');
      });
    });
  }

  public render() {
    return (
      <View style={{padding: 10}}>
        <Text style={{color: '#3D9970'}}>{this.state.log}</Text>
      </View>
    );
  }
}

Current behavior

After the App executed it shows the "Starting Server on port..." message and in the console log of the app i can see a "EADDRINUSE" error which means that the given adress is already in use.

Expected behavior

It should just start the tcp server.

Screenshots I have added them in the documentation section of this bug report.

Relevant information

OS Windows 10 Pro N & Androidversion 10QKQ1.191215.002 (Android 10)
react-native 0.63.3
@types/react-native ^0.63.2
react-native-tcp-socket ^4.5.4
Rapsssito commented 3 years ago

@MaxGER2019, thanks for the feedback! Which ports have you tested? Does this issue still occur if you set the server address to '0.0.0.0' instead of localhost?

h110m commented 3 years ago

Hey @Rapsssito thanks a lot for the fast reply. I first tried it with 0.0.0.0 and after that with localhost on diffrent random ports. All seem to be used already. Mostly ports that are 4 to 5 digits long. Example: 12345, 5208, 1122, 62758, 6278, 46225, 9655

Rapsssito commented 3 years ago

@MaxGER2019, could you check if you experience the same issue with the example code?

h110m commented 3 years ago

I will try it

Edit: @Rapsssito ok it looks like its working but i dont know why my app doesnt. A few things that i think are also diffrent to my app:

h110m commented 3 years ago

New Update:

It looks like if i use the exact code as in the template it does work. Even with the before tested ports that didn't work it now works. And i realy dont know why. I am trying a few things and if i stumble upon the issue i will post it here but for now this worked for me. Thanks a lot @Rapsssito

h110m commented 3 years ago

Update #2: Another thing that i noticed is if i set a port (for example 5208) it starts the server but if i reload the app it says EADDRINUSE again.

h110m commented 3 years ago

Update #3: Found a way to fix the reload app port in use error by adding this.server.close(); before the this.server = null; in the componentWillUnmount() function.

Edit: Looks like i thought i fixed it by adding this but it depends on randomness. Sometimes it works, sometimes not.

Rapsssito commented 3 years ago

@MaxGER2019 have you tried setting the reuse address option to true?

h110m commented 3 years ago

@Rapsssito Yes like I said I just copy and paste the example from the template into my app.

h110m commented 3 years ago

@Rapsssito A new little Update:

I tried following with the example from this Github repo:

I've changed the const serverPort to something static (in my case to 5208) and deleted all client code so that just the server should start. After doing this it started the server but when I reload (and I mean reload not hot reload) it gives me the EADDRINUSE error again. I think you could try to reproduce this.

Edit: It also looks like by just running this code with the typescript template from react-native it also crashes on hot reload

h110m commented 3 years ago

Another question I got for @Rapsssito is: why are you JSON stringify the socket. address() variable in the example if it returns a string anyway? This doesn't make any sense to me.

h110m commented 3 years ago

Ok, so I at least found a way to fix this so that you don't have to reload the react-native console because it looks like when you close the app and open it again it works again.

h110m commented 3 years ago

Ok, I think I finally got a solution. I just added this to the componentWillUnmount() function:

  componentWillUnmount() {
    this.server.close();
    this.server.destroy();
    this.server = null;
  }

This somehow works at least for hot-reloads but not for a complete reload (like shaking your phone and then clicking reload or typing r in the dev console). However, if you want to reload your app you need to close the app completely on you're phone (just on your phone not in the dev console) and then reopen the app on your phone.

This is my last post for now and I will close this bug since this should work as a workaround. If someone finds any "real" solution you can post it here.

Rapsssito commented 3 years ago

Glad you found a workaround. I might have an idea on how to fix that, but it requires more testing.

Another question I got for @Rapsssito is: why are you JSON stringify the socket. address() variable in the example if it returns a string anyway? This doesn't make any sense to me.

You are right, it is a remainder from the old example code, I will correct it on the next update.

nishmeht7 commented 3 years ago

@Rapsssito what is your idea to fix this? I might be able to help if you can provide some direction

Rapsssito commented 3 years ago

@nishmeht7, take a look at the following code from react-native-tcp:

    @Override
    public void onCatalystInstanceDestroy() {
        mShuttingDown = true;

        try {
            new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) {
                @Override
                protected void doInBackgroundGuarded(Void... params) {
                    socketManager.closeAllSockets();
                }
            }.execute().get();
        } catch (InterruptedException ioe) {
            FLog.e(TAG, "onCatalystInstanceDestroy", ioe);
        } catch (ExecutionException ee) {
            FLog.e(TAG, "onCatalystInstanceDestroy", ee);
        }
    }

My idea was to investigate about this method, which might do what we intend. However, I have low expectations and this is not a high priority issue: there is not a clear behavior for the TCP sockets expected when fast-refreshing the app.

maximilize commented 1 year ago

@Rapsssito This seems like a good solution. As far as I can see, there is no onCatalystInstanceDestroy cleanup function at all in the current code.

jlong4223 commented 6 months ago

Im running into this issue as well even with calling the .close() method. This only happens when I close and reopen my app. I have to rebuild my app to get past the error