I have two backends. Backend A and Backend B.
Backend B sends and receives info using a socket server running at port 4243.
Then, with Backend A, I need to catch that info and save it. But I have to also have a socket server on Backend A running at port 4243.
The problem is that, when I run Backend A after running Backend B I receive the error "EADDRINUSE
", because I'm using the same host:port on both apps.
If, for Backend A I use Python, the problem dissapear because I have a configuration for sockets that's called SO_REUSEADDR
.
Here we have some examples:
-
But, with the Python code, the socket connects successfully:
I don't know what to do :(
I hope I get some help here.
Edit 1
By the way, the
lsof
command, throws me this output for the JavaScript backend:And this other output for the Python backend:
Edit 2
It really seems to be a problem with JavaScript. I also found this snippet:
var net = require('net'); function startServer(port, host, callback) { var server = net.createServer(); server.listen(port, host, function() { callback(undefined, server); }); server.on('error', function(error) { console.error('Ah damn!', error); callback(error); }); } startServer(4000, '0.0.0.0', function(error, wildcardServer) { if (error) return; startServer(4000, '127.0.0.1', function(error, localhostServer) { if (error) return; console.log('Started both servers!'); }); });
From this post: https://medium.com/@eplawless/node-js-is-a-liar-sometimes-8a28196d56b6
As the author says:
Well, that prints “Started both servers!” which is exactly what we don’t want.
But for me, instead of printing that, I get an error:
Ah damn! Error: listen EADDRINUSE: address already in use 127.0.0.1:4000 at Server.setupListenHandle [as _listen2] (node:net:1319:16) at listenInCluster (node:net:1367:12) at doListen (node:net:1505:7) at processTicksAndRejections (node:internal/process/task_queues:84:21) { code: 'EADDRINUSE', errno: -98, syscall: 'listen', address: '127.0.0.1', port: 4000 }
I really cannot make it to run and print "Started both servers!".
Because that's what I want my code to do.
Edit 3
This is the Python server socket: https://gitlab.com/d3tn/ud3tn/-/blob/master/tools/aap/aap_receive.py
This is the important part:
addr = (args.tcp[0], int(args.tcp[1])) # args.tcp[0] = "localhost", args.tcp[1] = "4243" with AAPTCPClient(address=addr) as aap_client: aap_client.register(args.agentid) # args.agentid = "bundlesink" run_aap_recv(aap_client, args.count, args.verify_pl)
It creates an AAPTCPClient, and the only thing that AAPTCPClient does, is the following:
def __init__(self, socket, address): self.socket = socket self.address = address self.node_eid = None self.agent_id = None def register(self, agent_id=None): """Attempt to register the specified agent identifier. Args: agent_id: The agent identifier to be registered. If None, uuid.uuid4() is called to generate one. """ self.agent_id = agent_id or str(uuid.uuid4()) logger.info(f"Sending REGISTER message for '{agent_id}'...") msg_ack = self.send( AAPMessage(AAPMessageType.REGISTER, self.agent_id) ) assert msg_ack.msg_type == AAPMessageType.ACK logger.info("ACK message received!") def send(self, aap_msg): """Serialize and send the provided `AAPMessage` to the AAP endpoint. Args: aap_msg: The `AAPMessage` to be sent. """ self.socket.send(aap_msg.serialize()) return self.receive() def receive(self): """Receive and return the next `AAPMessage`.""" buf = bytearray() msg = None while msg is None: data = self.socket.recv(1) if not data: logger.info("Disconnected") return None buf = data try: msg = AAPMessage.parse(buf) except InsufficientAAPDataError: continue return msg
I don't see any bind, and I don't understand why the python code can call "
socket.recv
", but in my JavaScript code I can't do "netServer.listen
". I think it should be the same.
CodePudding user response:
There are things to clarify. 1.) The client uses the bind syscall where the kernel selects the source port automatically.
It does so by checking sys local_portrange sysctl settings.
1.) If you want to bind the client to a static source port, be sure to select a TCP port outside the local_portrange range !
2.) You cannot subscribe to event "*", instead you've to subscribe to the event "data" to receive messages.
For best practice you should also subscribe to the "error" event in case of errors !
These links will get you started right away:
How do SO_REUSEADDR and SO_REUSEPORT differ?
https://idea.popcount.org/2014-04-03-bind-before-connect/
So, for all beginners, who want to dig deeper into networking using node.js…
A working server example:
// Step 0: Create the netServer and the netClient // var HOST = 'localhost'; var PORT = 4243; var AGENT_ID = 'SO_REUSEADDR DEMO'; var net = require('net'); console.log(`[DEBUG] Server will listen to: ${HOST}:${PORT}`); console.log(`[DEBUG] Server will register with: ${AGENT_ID}`); const netServer = net.createServer((c) => { console.log('[netServer] Client connected'); c.on('data', (msg) => { console.log('[netServer] Received `message`, MSG:', msg.toString()); }); c.on('end', () => { console.log('client disconnected'); }); c.on('error', function (e) { console.log('Error: ' e.code); }); c.write('hello\r\n'); c.pipe(c); }).listen({ host: HOST, port: PORT, family: 4, // ipv4, same as socket.AF_INET for python }); // Code copied from nodejs documentation page (doesn't make any difference) netServer.on('error', function (e) { console.log('Error: ' e.code); if (e.code == 'EADDRINUSE') { console.log('Address in use, retrying...'); setTimeout(function () { netServer.close(); netServer.listen(HOST, PORT); }, 1000); } if ( e.code = 'ECONNRESET' ){ console.log('Connection reset by peer...'); setTimeout(function () { netServer.close(); netServer.listen(HOST, PORT); }, 1000); } });
The Client:
/* Or use this example tcp client written in node.js. (Originated with example code from http://www.hacksparrow.com/tcp-socket-programming-in-node-js.html.) */ var net = require('net'); var HOST = 'localhost'; var PORT = 4243; var client = new net.Socket(); client.setTimeout(3000); client.connect(PORT, HOST, function() { console.log("Connected to " client.address().address " Source Port: " client.address().port " Family: " client.address().family); client.write('Hello, server! Love, Client.'); }); client.on('data', function(data) { console.log('Received: ' data); client.end(); }); client.on('error', function(e) { console.log('Error: ' e.code); }); client.on('timeout', () => { console.log('socket timeout'); client.end(); }); client.on('close', function() { console.log('Connection closed'); });
Best Hannes
CodePudding user response:
Steffen Ullrich was completely right.
In my JavaScript code, I was trying to create a server to listen to the port 4243.
But you don't need to have a server in order to listen to some port, you can listen with a client too! (At least that's what I understood)You can create a client connection as following:
const netClient = net.createConnection(PORT, HOST, () => { console.log('[netClient] Connected'); }); netClient.on('data', (data) => { console.log('[netClient] Received data:', data.toString('utf8')); });
And with "client.on", then you can receive messages as well, as if it were a server.
I hope this is useful to someone else.