I setup two PC,
one is client in local network behide NAT,
another is server on public network.
The test steps are --
1) client listen udp on port 33333
2) server listen udp on port 22222
1) client send udp to server
2) server received the data and send back
When I test the code on my test network, it's OK.
If put the server on real internet, server can get the message from client,
client can not get response from server.
What's wrong?
Here's testing code with nodejs.
server
var dgram = require('dgram');
var socket = dgram.createSocket('udp4');
socket.on('message', function (message, remote) {
console.log('client ip:' + remote.address + ', port:' + remote.port +', message:' + message);
//send response to client
var message = new Buffer("hello, client!");
socket.send(message, 0, message.length, remote.port, remote.address);
});
//listening port
socket.bind(22222);
client
var dgram = require('dgram');
var socket = dgram.createSocket('udp4');
socket.on('message', function (message, remote) {
//display message from server
console.log('server ip:' + remote.address + ', port:' + remote.port +', message:' + message);
});
//listening port
socket.bind(33333);
//send message to server
function send(server){
var message = new Buffer("hello, server!");
socket.send(message, 0, message.length, 22222, server, function(){
//send again after 1 seconds
setTimeout(function(){
send(server);
}, 1000);
});
};
//suppose that server address is public.server.com
send("public.server.com");
NATed computers cannot be reached from outside and this is particularly painful for peer-to-peer or friend-to-friend software. Basically because your PC has not a public IP address but you NAT device has. So, the NAT is visible, your PC isn't.
The server gets the package from the NAT device and send the response to it. Yes, the NAT receives the response and it has to relay it to your PC, that's the trick. To do so you have to configure a port forwarding in the NAT.
The NAT has a table like the following:
+----------+---------------------+---------------+
| NAT PORT | INTERNAL IP ADDRESS | INTERNAL PORT |
+----------+---------------------+---------------+
| 33333 | 198.162.0.3 (pc ip) | 33333 |
It can be read as: when NAT receives a package in its port #33333 it has to redirected to the internal IP 198.162.0.3 (your PC IP address) and port# 33333.
If your PC uses a fixed IP, you can do this mapping by hand in your NAT. However, if you use a DHCP server, your PC's IP can change after each reboot so you need to do this mapping by software in you project. Most of the NATs support Universal Plug & Play, Port Mapping Protocol or Port Control Protocol to achieve this mapping and you can do it with nodejs given that all you need are the appropiated HTTP request to the NAT.
Yes, you can do it by yourself but it is not so easy. In fact, the discovery process requires you broadcast udp messages in the LAN in specific port. I strongly recommend you to use a third-party component to do it.
I hope this helps you.
Related
I want to make a UDP connection for voice calls between two applications. To decrease transactions over the server, I need to send the UDP packet directly from one client to another client without sending packets over the server. But I faced the below situation:
When a packet is sent to a server, the server will receive it from the router IP and a random port.
I tried to send a response from the server to the client through the router IP and port by opening a new UDP socket connection, but the client didn't receive the response.
I do the same by sending my response over the socket that already received the client message, and this time it received the client.
I failed to send UDP packets from other UDP sockets (both on the server and other clients) to my first client, even over the router IP and opened port on it.
I'm curious to know if it is possible to make client-to-client UDP connection or not?
The below code is the correct model to return respond to the client:
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`* server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
const message = Buffer.from('Some bytes to '+rinfo.address+":"+rinfo.port);
//////↓///////////This is the receiver socket
server.send(message, rinfo.port, rinfo.address);
//////↑///////////
});
server.bind(8090);
By the below code, the client will not receive any response, even from the server which received the client message right now!
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
const serverResponse = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`* server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
const message = Buffer.from('Some bytes to '+rinfo.address+":"+rinfo.port);
///////↓///////// This is the different socket instance
serverResponse .send(message, rinfo.port, rinfo.address);
///////↑//////////
});
server.bind(8090);
is this true? "you send a message from your client and the server receive it but when you send response to your client the client did not receive the response".
If this is true then i think that you need use a same socket object for sending and receiving because you cannot create two socket object over same port on a device.
I am very new to Udp Socket programming, here i implemented echo UDP Client which connects to UDP server
var buffer = require('buffer');
var udp = require('dgram');
// creating a client socket
var client = udp.createSocket('udp4');
//buffer msg
var data = Buffer.from('Pradip Shinde');
client.on('message',function(msg,info){
console.log('Data received from server : ' + msg.toString());
console.log('Received %d bytes from %s:%d\n',msg.length, info.address, info.port);
});
//sending msg
client.send(data,9300,'192.168.1.187',function(error){
if(error){
client.close();
}else{
console.log('Data sent from client!!!');
}
});
when this client send msg to server, operating system assign the random port to this client but in my scenario i want static port which will never change, is it possible to assign static port to udp client?
As mentioned in the documentation, you can use bind method to do this,
For UDP sockets, causes the dgram.Socket to listen for datagram messages on a named port and optional address that are passed as properties of an options object passed as the first argument. If port is not specified or is 0, the operating system will attempt to bind to a random port. If address is not specified, the operating system will attempt to listen on all addresses. Once binding is complete, a 'listening' event is emitted and the optional callback function is called.
Try using
// Creating a client socket
var client = udp.createSocket('udp4');
// Bind your port here
client.bind({
address: 'localhost',
port: 8000,
exclusive: true
});
For more information follow this documentation.
I have created a GCP compute engine instance with a static external ip address. Machine type: n1-standard-2 (2 vCPUs, 7.5 GB memory). OS is Linux/Debian.
My intention is to create a plain Node.js TCP server on the machine. The code is as follows:
var net = require('net');
var HOST = '0.0.0.0';
var PORT = 110;
net.createServer(function(sock) {
console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort);
sock.on('data', function(data) {
console.log('DATA ' + sock.remoteAddress + ': ' + data);
sock.write('You said "' + data + '"');
});
}).listen(PORT, HOST);
console.log('Server listening on ' + HOST +':'+ PORT);
The client is:
var net = require('net');
var HOST = '104.197.23.132';
var PORT = 110;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
client.write('I am Chuck Norris!');
});
client.on('data', function(data) {
console.log('DATA: ' + data);
client.destroy();
});
client.on('close', function() {
console.log('Connection closed');
});
My firewall rules are as follows:
PLEASE NOTE: I am listening on port 110, and the client is trying to connect to the static external ip address. Itt appears that I am enabling TCP traffic over 110 according to firewall rules. The error I see is
Error: connect ETIMEDOUT 104.197.23.132:110
When I ssh into the instance, and run tcp client, I connect successfully. So the final question is, why can't local tcp client (my computer) connect to compute instance? Is there something wrong with my firewall rules / source filters / IP forwarding?
I just solved this problem.
You have the wrong targets. Go to the edit page and click the select menu of "Targets", and then you can select the first option "Apply to all instance" that is the easiest way.
You need to first add firewall rule according to your host's IP, as internal traffic needs to be received from that particular host (your machine)
Then you should be able to ping to GCP Compute Instance.
You should also be able to telnet at the particular port which you configured in your program.
This should be okay.
Also - the customized rule should be added in the Network Tags of instance, to make the rule associated to that instance, after this the instance uses that particular rule.
I'm a nooby mobile developer trying to take advantage of cloudfoundry's service to run my server to handle some chats and character movements.
I'm using Noobhub to achieve this (TCP connection between server and client using Node.js and Corona SDK's TCP connection API)
So basically I'm trying a non-http TCP connection between Cloudfoundry(Node.js) and my machine(lua).
Link to Noobhub(There is a github repo with server AND client side implementation.
I am doing
Client
...
socket.connect("myappname.cloudfoundry.com", 45234)
...
(45234 is from server's process.env.VCAP_APP_PORT value I retrieved from console output I got through "vmc logs myappname" after running the application.)
Server
...
server.listen(process.env.VCAP_APP_PORT)
When I try to connect, it just times out.
On my local machine, doing
Client
...
socket.connect("localhost",8989)
Server
...
server.listen(8989)
works as expected. It's just on cloudfoundry that it doesn't work.
I tried a bunch of other ways of doing this such as setting the client's port connection to 80 and a bunch of others. I saw a few resources but none of them solved it.
I usually blow at asking questions so if you need more information, please ask me!
P.S.
Before you throw this link at me with an angry face D:< , here's a question that shows a similar problem that another person posted.
cannot connect to TCP server on CloudFoundry (localhost node.js works fine)
From here, I can see that this guy was trying to do a similar thing I was doing.
Does the selected answer mean that I MUST use host header (i.e. use http protocol) to connect? Does that also mean cloudfoundry will not support a "TRUE" TCP socket much like heroku or app fog?
Actually, process.env.VCAP_APP_PORT environment variable provides you the port, to which your HTTP traffic is redirected by the Cloud Foundry L7 router (nginx) based on the your apps route (e.g. nodejsapp.vcap.me:80 is redirected to the process.env.VCAP_APP_PORT port on the virtual machine), so you definitely should not use it for the TCP connection. This port should be used to listen HTTP traffic. That is why you example do work locally and do not work on Cloud Foundry.
The approach that worked for me is to listen to the port provided by CF with an HTTP server and then attach Websocket server (websocket.io in my example below) to it. I've created sample echo server that works both locally and in the CF. The content of my Node.js file named example.js is
var host = process.env.VCAP_APP_HOST || "localhost";
var port = process.env.VCAP_APP_PORT || 1245;
var webServerApp = require("http").createServer(webServerHandler);
var websocket = require("websocket.io");
var http = webServerApp.listen(port, host);
var webSocketServer = websocket.attach(http);
function webServerHandler (req, res) {
res.writeHead(200);
res.end("Node.js websockets.");
}
console.log("Web server running at " + host + ":" + port);
//Web Socket part
webSocketServer.on("connection", function (socket) {
console.log("Connection established.");
socket.send("Hi from webSocketServer on connect");
socket.on("message", function (message) {
console.log("Message to echo: " + message);
//Echo back
socket.send(message);
});
socket.on("error", function(error){
console.log("Error: " + error);
});
socket.on("close", function () { console.log("Connection closed."); });
});
The dependency lib websocket.io could be installed running npm install websocket.io command in the same directory. Also there is a manifest.yml file which describes CF deploy arguments:
---
applications:
- name: websocket
command: node example.js
memory: 128M
instances: 1
host: websocket
domain: vcap.me
path: .
So, running cf push from this directory deployed app to my local CFv2 instance (set up with the help of cf_nise_installer)
To test this echo websocket server, I used simple index.html file, which connects to server and sends messages (everything is logged into the console):
<!DOCTYPE html>
<head>
<script>
var socket = null;
var pingData = 1;
var prefix = "ws://";
function connect(){
socket = new WebSocket(prefix + document.getElementById("websocket_url").value);
socket.onopen = function() {
console.log("Connection established");
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log("Connection closed clean");
} else {
console.log("Connection aborted (e.g. server process killed)");
}
console.log("Code: " + event.code + " reason: " + event.reason);
};
socket.onmessage = function(event) {
console.log("Data received: " + event.data);
};
socket.onerror = function(error) {
console.log("Error: " + error.message);
};
}
function ping(){
if( !socket || (socket.readyState != WebSocket.OPEN)){
console.log("Websocket connection not establihed");
return;
}
socket.send(pingData++);
}
</script>
</head>
<body>
ws://<input id="websocket_url">
<button onclick="connect()">connect</button>
<button onclick="ping()">ping</button>
</body>
</html>
Only thing to do left is to enter server address into the textbox of the Index page (websocket.vcap.me in my case), press Connect button and we have working Websocket connection over TCP which could be tested by sending Ping and receiving echo. That worked well in Chrome, however there were some issues with IE 10 and Firefox.
What about "TRUE" TCP socket, there is no exact info: according to the last paragraph here you cannot use any port except 80 and 443 (HTTP and HTTPS) to communicate with your app from outside of Cloud Foundry, which makes me think TCP socket cannot be implemented. However, according to this answer, you can actually use any other port... It seems that some deep investigation on this question is required...
"Cloud Foundry uses an L7 router (ngnix) between clients and apps. The router needs to parse HTTP before it can route requests to apps. This approach does not work for non-HTTP protocols like WebSockets. Folks running node.js are going to run into this issue but there are no easy fixes in the current architecture of Cloud Foundry."
- http://www.subbu.org/blog/2012/03/my-gripes-with-cloud-foundry
I decided to go with pubnub for all my messaging needs.
I am trying to enable tcp, http and websocket.io communication on the same port. I started out with the tcp server (part above //// line), it worked. Then I ran the echo server example found on websocket.io (part below //// line), it also worked. But when I try to merge them together, tcp doesn't work anymore.
SO, is it possible to enable tcp, http and websockets all using the same port? Or do I have to listen on another port for tcp connections?
var net = require('net');
var http = require('http');
var wsio = require('websocket.io');
var conn = [];
var server = net.createServer(function(client) {//'connection' listener
var info = {
remote : client.remoteAddress + ':' + client.remotePort
};
var i = conn.push(info) - 1;
console.log('[conn] ' + conn[i].remote);
client.on('end', function() {
console.log('[disc] ' + conn[i].remote);
});
client.on('data', function(msg) {
console.log('[data] ' + conn[i].remote + ' ' + msg.toString());
});
client.write('hello\r\n');
});
server.listen(8080);
///////////////////////////////////////////////////////////
var hs = http.createServer(function(req, res) {
res.writeHead(200, {
'Content-Type' : 'text/html'
});
res.end(['<script>', "var ws = new WebSocket('ws://127.0.0.1:8080');", 'ws.onmessage = function (data) { ws.send(data); };', '</script>'].join(''));
});
hs.listen(server);
var ws = wsio.attach(hs);
var i = 0, last;
ws.on('connection', function(client) {
var id = ++i, last
console.log('Client %d connected', id);
function ping() {
client.send('ping!');
if (last)
console.log('Latency for client %d: %d ', id, Date.now() - last);
last = Date.now();
};
ping();
client.on('message', ping);
});
You can have multiple different protocols handled by the same port but there are some caveats:
There must be some way for the server to detect (or negotiate) the protocol that the client wishes to speak. You can think of separate ports as the normal way of detecting the protocol the client wishes to speak.
Only one server process can be actually listening on the port. This server might only serve the purpose of detecting the type of protocol and then forwarding to multiple other servers, but each port is owned by a single server process.
You can't support multiple protocols where the server speaks first (because there is no way to detect the protocol of the client). You can support a single server-first protocol with multiple client-first protocols (by adding a short delay after accept to see if the client will send data), but that's a bit wonky.
An explicit design goal of the WebSocket protocol was to allow WebSocket and HTTP protocols to share the same server port. The initial WebSocket handshake is an HTTP compatible upgrade request.
The websockify server/bridge is an example of a server that can speak 5 different protocols on the same port: HTTP, HTTPS (encrypted HTTP), WS (WebSockets), WSS (encrypted WebSockets), and Flash policy response. The server peeks at the first character of the incoming request to determine if it is TLS encrypted (HTTPS, or WSS) or whether it begins with "<" (Flash policy request). If it is a Flash policy request, then it reads the request, responds and closes the connection. Otherwise, it reads the HTTP handshake (either encrypted or not) and the Connection and Upgrade headers determine whether it is a WebSocket request or a plain HTTP request.
Disclaimer: I made websockify
Short answer - NO, you can't have different TCP/HTTP/Websocket servers running on the same port.
Longish answer -
Both websockets and HTTP work on top of TCP. So you can think of a http server or websocket server as a custom TCP server (with some state mgmt and protocol specific encoding/decoding). It is not possible to have multiple sockets bind to the same port/protocol pair on a machine and so the first one will win and the following ones will get socket bind exceptions.
nginx allows you to run http and websocket on the same port, and it forwards to the correct appliaction:
https://medium.com/localhost-run/using-nginx-to-host-websockets-and-http-on-the-same-domain-port-d9beefbfa95d