Streams and TCP protocol - javascript

I have this small piece of code. I run it, and then connect with a Telnet client.
var net = require('net');
//create server
var server = net.createServer(function (conn) {
console.log('New connection!')
//Message to the one who connects.
conn.write (
'\n > Welcome to NODE-CHAT!'
+ '\n > ' + count + ' other people are connected at this time.'
+ '\n > Please write your name and press Enter: '
);
//When a connected user writes...
conn.on('data', function (data) {
console.log(data);
});
conn.setEncoding('utf8');
});
//Listen
server.listen(3000, function () {
console.log('Server listening on *:3000');
})
When connected i get the Welcome messages as expected... Then if i write something in the telnet client, it will immediately trigger the conn.on listener... Lets say i write "hi you" the output in the console will be:
h
i
y
o
u
I would like this messages to sent when it is "finished" instead whenever i type character. I guess i could store the data from the conn.on trigger in a string, and output this string when the '\n' character is typed.
But I'm wondering if this is the right way to do it. Is there a way to change what trigger the conn.on? Maybe so it will only trigger (that is output in this case... ) on certain characters?... namely the \n char.

TCP is a stream of data so you should not make any assumptions about how much data you will receive by calling .read() or when listening for data events.
If your input is newline delimited, then you will have to buffer until you see a newline. There could also be multiple newlines in one chunk passed to your data event handlers too.

I don't see any problems in your code. The behavior you describe is related to the Telnet client that sends every keystroke, it does not wait for you to hit enter. If you are on Linux try sending data with wget o open a browser and type
http://localhost:3000/hiyou
and see if you get a complete string instead of one character.

Related

Node.js Net Module, Getting data out of the callback function

So I tried to solve it on my own and googled a lot but I couldn't really solve the problem although I think it shouldn't be that difficult. So what I am doing (or trying to do) is, I establish a simple telnet connection to a server and simulate the beginning of an SMTP session to verify if an email exist (if that gives me correct results or not is another question)
My code to do that looks like this:
const net = require('net');
async function TCPConnection(smtpServer, emailDomain, senderEmail) {
const client = new net.Socket();
client.setEncoding("utf-8");
let completeData = '';
client.connect({port: 25, host: smtpServer}, () => {
console.log("TCP Connection established with the Server.");
})
client.write("ehlo " + emailDomain + "\r\n");
client.write("mail from:<" + senderEmail + ">\r\n");
client.write("rcpt to:<" + senderEmail + ">\r\n");
client.on('data', function (data) {
console.log(data);
});
function dataReceived(data){
completeData += data;
}
client.on('end', function () {
console.log('Requested an end to the TCP connection');
});
}
TCPConnection('aspmx.l.google.com', 'mailtrap.io', 'new-recipient#mailtrap.io');
The responses from the server logged to console looks like this:
TCP Connection established with the Server.
220 mx.google.com ESMTP m17si1129948edf.309 - gsmtp
250-mx.google.com at your service, [217.84.87.66]
250-SIZE 157286400
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
250 2.1.0 OK m17si1129948edf.309 - gsmtp
550-5.1.1 The email account that you tried to reach does not exist. Please try
550-5.1.1 double-checking the recipient's email address for typos or
550-5.1.1 unnecessary spaces. Learn more at
550 5.1.1 https://support.google.com/mail/?p=NoSuchUser m17si1129948edf.309 - gsmtp
So what I want to achieve is to get the complete data (that is logged to the console right now) out of the callback or TCPConnection function. Like for example with "return completeData;" at the end of the TCPConnection function.
The reasyon why I want to do this is, that this function (TCPConnection) gets called over an HTTP Request. And I want to send the result of this back to client. The complete function could look like this (but obviously not working so far):
app.post('/tcpConnection', (req, res) => {
let smtpServer = req.body.smptServer;
let domain = req.body.domain;
let senderEmail = req.body.senderEmail;
async function sendResponse(){
let response = await TCPConnection(smtpServer,domain,senderEmail);
await res.status(200).json(result);
}
})
I read the solution for this could be to do what I want to do with my data after the "end" event got emitted. But my problem with that is, that after my three "write-commands" the server doesn't end the connection immediately. And if I would write this:
client.write("ehlo " + emailDomain + "\r\n");
client.write("mail from:<" + senderEmail + ">\r\n");
client.write("rcpt to:<" + senderEmail + ">\r\n");
client.end();
It is ending the connection before the second and third command gets a response. I tried to "wait" for the reponses with "await client.write..." but that is obviously not gonna work for not sure what reasons.
I hope you get a gist of what I am trying to get done if not feel free to ask. Thank you!
So I think I have done it myself or at least get a solution that is working fine in most cases. What I am doing is that I end the socket in the callback of the third write command like this:
client.write("rcpt to:<" + senderEmail + ">\r\n","utf-8",()=>{
console.log("third command written");
client.end();
});
By doing this, the server "should get enough time" to answer all commands.
Ealier solution was to count the number of responses in the callback function of the 'data' event, like this:
client.on('data', function (data) {
counterResponses++;
console.log("Response Nr: " + counterResponses + '\r\n' + data);
completeData.push(data);
if (counterResponses === 4){
console.log("4 responses received end connection");
client.end();
}
});
Since I wanted to end the socket after receiving 4 responses to my commands I used an if-statement to check the number of responses. But the problem with this is, that sometimes multiple responses could get send as one. For example, the responses could either look like this:
Response Nr: 1
220 mtaproxy104.free.mail.ir2.yahoo.com ESMTP ready
Response Nr: 2
250-mtaproxy104.free.mail.ir2.yahoo.com
250-PIPELINING
250-SIZE 41943040
250-8BITMIME
250 STARTTLS
But also like this:
Response Nr: 1
220 mtaproxy104.free.mail.ir2.yahoo.com ESMTP ready
250-mtaproxy104.free.mail.ir2.yahoo.com
250-PIPELINING
250-SIZE 41943040
250-8BITMIME
250 STARTTLS
So 2 responses get send as one. This makes it hard to evaluate if I got all the responses I needed to get. That's why I just end the socket after the last write-command.
In the client.on('end') function I then send the completeData back to the client.
client.on('end', function () {
console.log('Requested an end to the TCP connection');
console.log(completeData);
res.status(200).json(completeData);
});
Only thing that still bothers me is that my array (where I push the data with each emitted 'data'-event) is variable in its length. This is gonna make it a bit difficult to verify, if the email was found or not. But since this should always be the last response, it should work in the most cases if I just test if the last entry of the array either has the statuscode '250' or '550' in it.
I hope this helps at least somebody. Let me know if it helped you or if you have any questions about it. Also still open for better solutions, since I am sure there are better ways to do this.

unable to broadcast a user disconnection in socket.io

i am new to socket.io & nodejs. i have been writing a tictactoe game and i have a problem in it, my purpose is if someone refresh the page or close the browser let his/her opponent know . i know i should do this with disconnect event and here is what i try, but it is not working .
server side
socket.on('disconnect', function() {
socket.emit('user:disconnect');
});
client side
socket.on('user:disconnect', function() {
var message = player.getPlayerName() + ' leaves';
socket.emit('gameEnded', {room: this.getRoomId(), message: message,id:player.getPlayerId(),gameid: this.getGameId(),winType:"leave"});
});
also, i need to know how to get the room of disconnected user .
i already saw this but i just want to send the users in a room not all users in the whole application.
server.js
socket.on('disconnect', function() {
socket.emit('user:disconnect');
});
In the above code if a user named 'xxx' is disconnected, server is emitting 'user:disconnect' to the same user. You have to find the socket connection of other player and emit the event to that client.
You can achieve your goal by joining both the clients in a room and send message to other user in the room.
socket.join('room1');
io.sockets.in('room1').emit('user:disconnect');
Otherwise you have to store all clients as mentioned below and send message to the specific client using the following code.
var users = {
'client1': [socket object],
'client2': [socket object],
'client3': [socket object]
}
users['client3'].emit('user:disconnect')

Is it possible to pause/suspend (don’t accept(2) on the socket) a Node.js server?

Goal: To have a Node.js server where only one connection is active at a time.
I can temporarily remove the connection event listener on the server, or only set it up once in the first place by calling once instead of on, but then any connection that gets made while there is no connection event listener seems to get lost. From strace, I can see that Node is still accept(2)ing on the socket. Is it possible to get it to not do that, so that the kernel will instead queue up all incoming request until the server is ready to accept them again (or the backlog configured in listen(2) is exceeded)?
Example code that doesn’t work as I want it to:
#!/usr/bin/node
const net = require("net");
const server = net.createServer();
function onConnection(socket) {
socket.on("close", () => server.once("connection", onConnection));
let count = 0;
socket.on("data", (buffer) => {
count += buffer.length;
if (count >= 16) {
socket.end();
}
console.log("read " + count + " bytes total on this connection");
});
}
server.once("connection", onConnection);
server.listen(8080);
Connect to localhost, port 8080, with the agent of your choice (nc, socat, telnet, …).
Send less than 16 bytes, and witness the server logging to the terminal.
Without killing the first agent, connect a second time in another terminal. Try to send any number of bytes – the server will not log anything.
Send more bytes on the first connection, so that the total number of bytes sent there exceeds 16. The server will close this connection (and again log this to the console).
Send yet more bytes on the second connection. Nothing will happen.
I would like the second connection to block until the first one is over, and then to be handled normally. Is this possible?
.. so that the kernel will instead queue up all incoming request until the server is ready to accept them again (or the backlog configured in listen(2) is exceeded)?
...
I would like the second connection to block until the first one is over, and then to be handled normally. Is this possible?
Unfortunately, it is not possible without catching the connection events that are sent and managing the accepted connections in your application rather than with the OS backlog. node calls libuv with an OnConnection callback that will try to accept all connections and make them available in the JS context.

Socket.io disconnection event triggers after long

I am just trying to create a simple app using Socket.io. I am trying to send a notification (technically, emit an event) to all the connected users, if a particular user, User X, has disconnected, that User X has left or has disconnected.
Here is my server side code.
io.on('connection', function(socket){
console.log('Connected! ' + socket.id);
socket.on('disconnect', function(){
for(var i=0;i<onlineUsers.length;i++)
{
if(onlineUsers[i].socketId == socket.id)
{
var newMessage = {};
newMessage.socketId = onlineUsers[i].socketId;
newMessage.fullName = onlineUsers[i].fullName;
newMessage.message = newMessage.fullName + " IS DISCONNETED!";
io.emit('newMessage', newMessage);
onlineUsers.splice(i,1);
console.log('splicing id ' + socket.id);
}
}
}
I guess that the code is executing but takes long to emit the disconnection event. I receive the disconnection message about 2 minutes after the client had disconnected. Why does it take so long? What might be the wrong in this code?
I also cannot understand when exactly the client is considered as disconnected. If the app closes, or the browser closes or the internet goes down?
Thanks in advance.
Update: I have a test deployment on Cloud9 and my live deployment is on Azure Cloud. I have realized that Cloud9 works just perfect, this seems like an issue with Azure.
Update 2: Whenever I disconnect/close/refresh my web client, I immediately get the disconnected event emitted by the server. The same does not happen for my mobile client. Why?

How can I pick out a particular server message from a websocket connection?

I have a WebSocket connection set up for a basic web chat server.
Now, whenever a message is received, it is sent to a function which outputs it on the screen.
socket.onmessage = function(msg){output(msg);}
However, there are certain technical commands which the user can send to the server through the connection which elicit a technical response, not meant to be output on the screen.
How can I grab the server response which immediately follows one of these technical messages?
Do I put a separate socket.onmessage right after the block of code which sends the technical message? I imagine that would take all future messages. I just want the next one.
Ideas?
WebSockets is asynchronous so trying to get the 'next' message received is not the right solution. There can be multiple message in flight in both directions at the same time. Also, most actions in Javascript are triggered by asynchronous events (timeout firing, or user clicking on something) which means you don't have synchronous control over when sends will happen. Also, the onmessage handler is a persistent setting: once it is set it receives all messages until it is unset. You need to have some sort of way of distinguishing control messages from data messages in the message it self. And if you need to correlate responses with sent messages then you will also need some kind of message sequence number (or other form of unique message ID).
For example, this sends a control message to the server and has a message handler which can distinguish between control and message and other messages:
var nextSeqNum = 0;
...
msg = {id: nextSeqNum, mtype: "control", data: "some data"};
waitForMsg = nextSeqNum;
nextSeqNum += 1;
ws.send(JSON.stringify(msg));
...
ws.onmessage = function (e) {
msg = JSON.parse(e.data);
if (msg.mtype === "control") {
if (msg.id === waitForMsg) {
// We got a response to our message
} else {
// We got an async control message from the server
}
} else {
output(msg.data);
}
};
You can packetise the data, I mean, by a special character/s, form a string like this :
"DataNotToBeShown"+"$$"+"DataToBeShown"; //if $$ is separating character
And then, you can split the string in javascript like this :
var recv=msg.data.split('$$');
So, the data not be shown is in recv[0] and data to be shown, in recv[1]. Then use however you want.

Categories