Why is server.close() executing earlier than server.on('connection')?

149 views Asked by At

I'm learning the Node.js "Net" library. Can anyone explain to me why in this example server.close() executing earlier than server.on('connection')?

I know how to make everything work properly, but I do not understand why the client socket connects to the server after the server is closed, and not before.

let server = require('./server') // let server = new net.Server().listen(2000)
let client = require('./client') // let client = new net.Socket()
let client2 = require('./client2') // let client2 = new net.Socket()

server.on('listening', () => console.log('Server listening'))

client.on('connect', () => console.log('Client connected'))
client2.on('connect', () => console.log('Client2 connected'))

client.connect(2000)
client2.connect(2000)

server.on('error', (err) => console.log('Server error: ' + err))
client.on('error', (err) => console.log('Client error: ' + err))
client2.on('error', (err) => console.log('Client2 error: ' + err))

server.close(() => console.log('Server closed'))

Running the file will display the following in the terminal:

Server closed
Client2 error: Error: connect ECONNREFUSED ::1:2000
Client error: Error: connect ECONNREFUSED ::1:2000

But, if I comment on the last line, then the terminal will display:

Server listening
Client connected
Client2 connected
1

There are 1 answers

8
Heiko Theißen On

All these commands are executed asynchronously, that's why they have a callback function as second parameter, this will be called when the asynchronous operation is finished. Determining which of the parallel asynchronous executions is faster is a fool's game.

To be sure that an aynchronous command B is executed only after another asynchronous command A has finished, you must execute B in the callback of A. In your case this means

  • Connect client in the callback of server
  • Connect client2 in the callback of server
  • Close server in the callback of client and of client2

The last bullet point requires that you wait for both callbacks to be invoked, which can be done with Promise.all if you "promisify" the client connection.

function connect(client, port) {
  return new Promise(function(resolve, reject) {
    client.connect(port)
    client.on('connect', resolve)
    client.on('error', reject)
  })
}
let server = new net.Server().listen(2000, function() {
  let client = new net.Socket()
  let client2 = new net.Socket()
  Promise.all([
    connect(client, 2000).then(
      () => console.log('Client connected'),
      (err) => console.log('Client error: ' + err)
    ),
    connect(client2, 2000).then(
      () => console.log('Client2 connected'),
      (err) => console.log('Client2 error: ' + err)
    )
  ]).then(() => server.close(() => console.log('Server closed')))
})
server.on('listening', () => console.log('Server listening'))
server.on('error', (err) => console.log('Server error: ' + err))

This code produces the three lines that you expect. You will not see Server closed, however, because the existing connections prevent the server from being closed.