Rapsssito / react-native-tcp-socket

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

Weird buffer size limit-like behavior #147

Closed Aensdottir closed 2 years ago

Aensdottir commented 2 years ago

Description

Trouble with sending a long base64 string, usually gets limited to 2072 characters (bytes) but occasionally also sends 3108 or 1036 characters, the full string is over 10 thousand characters. The server is written in C# but the issue cannot be caused by that as i have tried everything server-side i could, buffer size is not a limitation on the server.

Only thing i can think of is a buffer size limitation in react-native-tcp-socket

Steps to reproduce

Sending a very long string in one write() - string long over 10 thousand chars.

Current behavior

Sent data limited to 2072 bytes (usually)

Expected behavior

Buffer size only limited by memory or the network itself

Relevant information

| OS | Android 11 | | react-native | 0.64.3 | | react-native-cli | 2.0.1 | | react-native-tcp-socket | 5.5.0 |

Rapsssito commented 2 years ago

@Aensdottir, have you tested your implementation with a NodeJS script? This library's behavior should match Node's net API. If they do not match, please, provide a minimal reproducible code so I can run it in both React Native and NodeJS in order to match the expected behavior.

Aensdottir commented 2 years ago

@Rapsssito I have created a quick NodeJS script running very similar code to my React Native application and it worked flawlessly. The string i'm sending (an image in b64) is long over 5 million characters.

NodeJS Client code - Working

console.log("Start");

const net = require("net");
const fs = require("fs");

var image = base64_encode("F:/TEST.jpg");
console.log(image.length);

Connect(image);

function Connect(command) {
  const client = net.createConnection({ port: 10144 }, () => {
    console.log("connected to server!");
    client.write(command);
  });
  client.on("data", (data) => {
    console.log(data.toString());
    client.end();
  });
  client.on("end", () => {
    console.log("disconnected from server");
  });
}

function base64_encode(file) {
  var bitmap = fs.readFileSync(file);
  return new Buffer(bitmap).toString("base64");
}

React Native Implementation

function SendImage() { // RN Package for photos
    const imgOptions = {
      mediaType: "photo",
      includeBase64: true,
    };
    launchImageLibrary(imgOptions, callback);

    var image = "";
    function callback(props) {
      image = props.assets[0].base64; // Base64 String
      TcpConnect(image);
    }
  }
function TcpConnect(command) {
  // Connect
  const client = TcpSocket.createConnection(options, () => {
    client.write(command); // Sends only a small portion of the string
  });

  // On data received
  client.on("data", function (data) {
    console.log(bin2String(data));
    client.destroy();
  });
  client.on("error", function (error) {
    console.log("error:", error);
  });
  client.on("timeout", function () {
    console.log("socket timeout");
  });
  client.on("close", function () {
    console.log("Connection closed!");
    client.destroy();
  });
}
Rapsssito commented 2 years ago

@Aensdottir, thanks a lot. I will try to match the behavior ASAP. In the meantime, a workaround could be sending the data in smaller slices.

Rapsssito commented 2 years ago

@Aensdottir, I have been testing your code in a both NodeJS server/client config and the buffer size limitation is still present. However, NodeJS implementation has a larger buffer and has a more direct connection with the OS so, in your case, this results in the sending pipe not filling up and the data being "perceived" as one large chunk.

In RN, I have more limitations since I have to use the bridge between JS and the native OS, so I am forced to use smaller chunks. Since TCP is stream oriented protocol, this should not be an issue, you will receive the same information but in smaller chunks. In your protocol, you can indicate the start and end of the file with any special bytes and concatenate all the chunks as they are received. react-native-tcp-socket uses the drain event for this kind of data transfers.

Aensdottir commented 2 years ago

@Rapsssito, Thank you for the clarification, i have looked into the drain event and slicing data into chunks however i am not able to get this implementation to work with neither a C# server nor a NodeJS server, it seems the drain event is triggered but never resumes writing.

Would you, please, be able to provide me with some minimal code or further guidance/explanation as to how this would work?

Rapsssito commented 2 years ago

@Aensdottir, I have uploaded a new example code. You can find it here. I hope it helps!

You can also check the example for pause/resume writing.