67583

NodeJS: TCP socket server only returns data the first time

I'm attempting to write a small relay script in node.js that listens for incoming TCP connections on a local socket, and when it gets one, forwards the traffic to a 3rd party. It must also take any returned data from that 3rd party and send it back to the original local socket. I've tried code like http://delog.wordpress.com/2011/07/19/a-tcp-relay-mechanism-with-node-js/ and it does work, but it requires the sender be a server that is listening on a socket itself, and my utility is intended to work with any program that tries to create an outbound TCP connection. Unfortunately, the problem I'm running into is that everything works great the first time with the client sending the data to the "router" program, and the router forwarding it to another server, and then returning the data from the client. However, when the client program ends or is terminated and attempts to reconnect, I get this:

events.js:72 throw er; // Unhandled 'error' event ^ Error: This socket has been ended by the other party at Socket.writeAfterFIN [as write] (net.js:275:12) at Socket.<anonymous> (/root/tcp_loop.js:37:17) at Socket.emit (events.js:117:20) at Socket.<anonymous> (_stream_readable.js:748:14) at Socket.emit (events.js:92:17) at emitReadable_ (_stream_readable.js:410:10) at emitReadable (_stream_readable.js:406:5) at readableAddChunk (_stream_readable.js:168:9) at Socket.Readable.push (_stream_readable.js:130:10) at TCP.onread (net.js:528:21)

I ripped out all of the logic and distilled the test case into a small bit of code: one server that acts as both the router (listening on port 8124) as well as the "remote" server (on port 9999), though my testing indicates it makes no difference weather the remote server is on the same machine, on the Internet, etc. Here is the server code:

var net = require('net'), util = require('util') ; // The loop_server simulates a remote service. // The error occurs whether using it here, or actually forwarding // the data to a remote host. var loop_server = net.createServer(function(loop) { console.log("Loop server connected"); loop.on("end", function() { console.log("Loop server disconnected"); }); loop.on("data", function(data) { console.log("Loop got data: " + data); loop.write(data); }); }).listen(9999, function() { console.log("Loop server bound"); }); var remote_socket = net.connect(9999, function() { console.log("Remote connected"); var local_server = net.createServer(function(local_socket) { //'connection' listener console.log('Local server connected'); local_socket.on('end', function() { console.log('Local server disconnected'); // local_socket.destroy(); }); local_socket.on('data', function(ldata) { console.log("Local socket got data: " + ldata); remote_socket.write(ldata); }); remote_socket.on('data', function(rdata) { console.log("Remote socket got data: " + rdata); local_socket.write(rdata); }); local_socket.write('hello\r\n'); }).listen(8124, function() { //'listening' listener console.log('Local server bound'); }); }); // remote_socket

The thing that's failing is the local_socket.write(rdata); in the remote_socket.on('data', ... handler. It works the first time the router is started and the client connects, but never again.

For reference, here is the code for the little client app that I've been using. I get the same result with a perl script, telnet, etc.:

var net = require('net'); var client = new net.Socket(); client.connect(8124, function() { console.log('CONNECTED TO: localhost:8124'); client.write('Single text message from the client app'); }); client.on('data', function(data) { console.log('DATA: ' + data); }); client.on('close', function() { sconsole.log('Connection closed'); });

Any insight would be greatly appreciated. I feel like I must be missing something extremely simple here...

<strong>Update:</strong>

Nitzin's solution below is a better way to do this, but in my particular example below, the solution is to remove old remote_socket.on('data') listeners before creating new ones, e.g.:

var remote_socket = net.connect(9999, function() { console.log("Remote connected"); var local_server = net.createServer(function(local_socket) { //'connection' listener console.log('Local server connected'); remote_socket.removeAllListeners('data'); ... remote_socket.on('data', function(rdata) { console.log("Remote socket got data: " + rdata); local_socket.write(rdata); });

Answer1:

You should not destroy the socket. It closes both ends of the socket. You should only .end() it, which closes your writing end.

<strong>EDIT</strong>

Destroying the socket is bad, as I originally wrote, but your real problem is something completely different: you got your proxy (what you call "local") and echo (what you call "remote") servers backwards: the proxy server should make a new connection to the echo server for each new connection the proxy server gets, not the other way around as you have it now.

The only end() needed is in the client, to let the server know you're done writing.

Here is client.js:

var net = require('net'); var client = new net.Socket(); client.connect(8124, function() { console.log('CLIENT: CONNECTED: localhost:8124'); client.write('single text message from the client app'); client.end(); }); client.on('data', function(data) { console.log('CLIENT: GOT DATA: ' + data); }); client.on('close', function() { console.log('CLIENT: CONNECTION CLOSED'); });

And here is servers.js:

var net = require('net'), util = require('util'); net.createServer(function(conn) { console.log('ECHO_SERVER: CONN: new connection'); conn.on('end', function() { console.log('ECHO_SERVER: CONN: disconnected'); }); conn.on('data', function(data) { console.log('ECHO_SERVER: CONN: GOT DATA: ' + data); conn.write(data); }); }).listen(9999, function() { console.log('ECHO_SERVER STARTED'); }); net.createServer(function(conn) { console.log('PROXY_SERVER: CONN: new connection'); var remote = net.connect(9999, function() { console.log('PROXY_SERVER: CONNECTED TO ECHO_SERVER'); conn.on('end', function() { console.log('PROXY_SERVER: CONN: disconnected'); remote.end(); }); conn.on('data', function(data) { console.log('PROXY_SERVER: CONN: GOT DATA FOR ECHO_SERVER: ' + data); remote.write(data); }); remote.on('data', function(data) { console.log('PROXY_SERVER: CONN: GOT DATA FROM ECHO_SERVER: ' + data); conn.write(data); }); }); }).listen(8124, function() { console.log('PROXY_SERVER STARTED'); });

As you can see, for each conn to the proxy server, there is a new remote going to the echo server.

Recommend

  • java socket timeout behaviour
  • Zookeeper -Kafka: ConnectException - Connection refused
  • mysql_* to MySQLi
  • starttls on node.js > 0.4.0
  • “Permission denied” while connecting to a page, even with INTERNET permission
  • Reconnection in socket.io problem in `socket.on('message',function(){})`
  • inter thread comunication using ZeroMQ messages [closed]
  • synology php ftp_ssl_connect - Call to undefined function
  • How to send an std::vector of unsigned char over an UDP socket using boost asio?
  • How to read data from socket connection - android
  • Cannot send user message with Spring Websocket
  • Symfony2 plaintext users don't work
  • Removing event listeners on automatically created multiple elements
  • How to detect left mouse click but not when the click occur on a UI Button component [closed]
  • Arduino making decision according to a packet received from serial port
  • Error in making a socket connection
  • Get a trait object reference from a vector
  • select function not working in 3.5.4 version of d3.js
  • How to make R's read_csv2() recognise the text characters properly
  • AJAX Html Editor Extender upload image appearing blank
  • d3 v4 drag and drop with TypeScript
  • JQuery Internet Explorer and ajaxstop
  • Is there a javascript serializer for JSON.Net?
  • Opengl-es onTouchEvents problem or a draw problem? [closed]
  • Get object from AWS S3 as a stream
  • Cross-Platform Protobuf Serialization
  • Validaiting emails with Net.Mail MailAddress
  • Build own AppleScript numerical error handling
  • Websockets service method fails during R startup
  • Do I've to free mysql result after storing it?
  • Google cloud sdk not working when python points python3
  • Is there a mandatory requirement to switch app.yaml?
  • Hits per day in Google Big Query
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Linking SubReports Without LinkChild/LinkMaster
  • XCode 8, some methods disappeared ? ex: layoutAttributesClass() -> AnyClass
  • costura.fody for a dll that references another dll
  • Observable and ngFor in Angular 2
  • UserPrincipal.Current returns apppool on IIS
  • java string with new operator and a literal