Rapsssito / react-native-tcp-socket

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

Connection close is never emitted after opening/destroying 100 sockets #96

Closed ewc003 closed 3 years ago

ewc003 commented 3 years ago

Description

I am calling a connect to TCP function with setInterval. After many calls of the connect function, the socket emits a null error and the socket.destroy() is not called.

Or code:

export const initSocket = () => {
  return async (dispatch, getState) => { 
    let interval = setInterval(async () => {
      getTCPData()
          .then(tcpData => {
            // process data
          })
          .catch(e => {
            console.log('getTCPData error: ', e); // e is null
          });
      }
    }, 250);
  };
};

function getTCPData() {
  return new Promise((resolve, reject) => {
    const tcpTimeout = setTimeout(() => {
      reject(null);
    }, 1000);
    const socket = TcpSocket.createConnection(
      {
        port: 10000,
        host: '192.168.100.100',
        interface: 'wifi',
        reuseAddress: true,
      },
      () => {
        clearTimeout(tcpTimeout);
        // Write on the socket
        socket.write('Ready');
      },
    );

    socket.on('error', function(error) {
      console.log(error); // throws null error
      socket.destroy(); // never called when error is null?
    });

    socket.on('data', function(data) {
      // get data from server then destroy
      socket.destroy(); 
    });

    socket.on('close', function() {
      // if data invalid or error
        reject('error');
      // valid data
        resolve(data);
    });
  });
}

Current behavior

Interval runs for a while (socket successfully gets data or gets invalid data), then randomly emits null error. Unable to connect to server unless app is restarted. Continues to timeout and throw null error.

Expected behavior

Throw error then emit close event and try to establish new connection with server.

Screenshots If applicable, add screenshots to help explain your problem.

Relevant information

| OS | Android, Samsung Tab A | | react-native | "0.62.2" | | react-native-tcp-socket | "4.5.4" |

ewc003 commented 3 years ago

@Rapsssito I've narrowed the problem down. Sorry I am fairly new to socket communication in general and would appreciate any advice. It looks like after I open the 100th socket, any following socket will throw a null error. Is there a better way to open/close sockets or handle many incoming messages? Thanks for your time!

ewc003 commented 3 years ago

I also forgot to note that I call socket.destroy() after getting data from the server so there is only 1 active socket at a time.

Rapsssito commented 3 years ago

@ewc003, could you provide the stack error trace?

ewc003 commented 3 years ago

@Rapsssito If I understand stack error trace correctly, my application is not crashing/throwing unhandled errors. When attempting to connect to the socket, it throws an error which is null and the application continues to run.

Rapsssito commented 3 years ago

@ewc003, could you copy the stack trace here?

ewc003 commented 3 years ago

tcptest 1 @Rapsssito Is this sufficient?

Rapsssito commented 3 years ago

@ewc003, the error trace does not mention react-native-tcp-socket, are you sure the issue is related to this library? Could you narrow down the code causing this issue?

Rapsssito commented 3 years ago

Is there a better way to open/close sockets or handle many incoming messages?

@ewc003, you shouldn't open a new socket each time you want to send a TCP request if the time interval is so low. You should refactor your code to reuse the same socket in getTCPData() instead of creating a new one.

ewc003 commented 3 years ago

@Rapsssito Thanks! I will look into the bug deeper and see if I find more clues. I will close issue for now.

cgo123 commented 1 year ago

bump

cgo123 commented 1 year ago

@ewc003 what was the solution? can you help me out? i have the same problem

adrianzwergel commented 1 year ago

@cgo123 and @ewc003 i found out, if you bind to a spezific network and connect up to 99 tcp socket(open or already closed, dosn´t madder). You get an error with message null at Socket with Id 98. When i remove the option interface at TcpSocket.createConnection, i am able to create more connections.

So I modified the Java code to print the stacktrace. The problem is that the Class android.net.Network throws at Method bindSocket without a Message. So I don´t understand the main reason.

android.net.ConnectivityManager.convertServiceException(ConnectivityManager.java:4034) android.net.ConnectivityManager.sendRequestForNetwork(ConnectivityManager.java:4226) android.net.ConnectivityManager.sendRequestForNetwork(ConnectivityManager.java:4233) android.net.ConnectivityManager.requestNetwork(ConnectivityManager.java:4371) android.net.ConnectivityManager.requestNetwork(ConnectivityManager.java:4349) com.asterinet.react.tcpsocket.TcpSocketModule.requestNetwork(TcpSocketModule.java:210) com.asterinet.react.tcpsocket.TcpSocketModule.selectNetwork(TcpSocketModule.java:252) com.asterinet.react.tcpsocket.TcpSocketModule.access$300(TcpSocketModule.java:31) com.asterinet.react.tcpsocket.TcpSocketModule$1.run(TcpSocketModule.java:81) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)

But i fixed by modify TcpSocketModule.ts:

add function: `private Network getNetwork(String interfaceName) { if(interfaceName == null) return null;

    Network network = null;
    ConnectivityManager connectivityManager = (ConnectivityManager) mReactContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    Network[] networks = connectivityManager.getAllNetworks();
    Log.i("ABB", "Start");

    for (Network n : networks) {
        LinkProperties linkProperties = connectivityManager.getLinkProperties(n);
        if (linkProperties != null && linkProperties.getInterfaceName().equals(interfaceName)) {
            network = n;
            break;
        }
    }

    return network;
}`

and edit connect function: client.connect(mReactContext, host, port, options, getNetwork(iface)); instead of: client.connect(mReactContext, host, port, options, currentNetwork.getNetwork()); and you can comment out: //selectNetwork(iface, localAddress);

As hit you need the interfaceName("eth0" or "wlan0") instead of using transportname("ethernet" or "wifi") , in my application i required by bind the right interface. This is not the nicest solution, but it works fine for my case.

@Rapsssito thank you for this great package, i hope you can fix this in a newer version :)