I'm currently trying to develop a simple Flash game which talks to a node.js server.
My question is this:
How might I go about making a server which differentiates web requests from game requests?
Here are the details of what I've done:
Previously, I used the net and static modules to handle requests from the game client and the browser, respectively.
TwoServers.js
// Web server
var file = new staticModule.Server('./public');
http.createServer(function(req, res){
req.addListener('end', function(){
file.serve(req, res, function(err, result){
// do something
});
});
}).listen(port1, "127.0.0.1");
// Game Server
var server = net.createServer(function(socket)
{
// handle messages to/from Flash client
socket.setEncoding('utf8');
socket.write('foo');
socket.on('data', onMessageReceived);
});
server.listen(port2, "127.0.0.1");
I'd like to do the above with just an Express server listening in on a single port, but I'm not sure how to go about doing that.
Here's what I'm thinking it might look like (doesn't actually work):
OneServer.js
var app = express();
app.configure(function()
{
// ...
app.use('/',express.static(path.join(__dirname, 'public'))); // The static server
});
app.get('/', function(req, res) // This is incorrect (expects http requests)
{
// Handle messages to/from Flash client
var socket = req.connection;
socket.setEncoding('utf8');
socket.write('foo');
socket.on('data', onMessageReceived);
});
app.listen(app.get('port')); // Listen in on a single port
But I'd like to be able to differentiate from web page requests and requests from the game.
Note: Actionscript's XMLSocket makes TCP requests, so using app.get('/') is incorrect for two reasons:
When Flash writes to the socket, it isn't using the http protocol, so app.get('/') will not be fired when the game tries to connect.
Since I don't have access to correct the net.Socket object, I cannot expect to be reading or writing from/to the correct socket. Instead, I'll be reading/writing from/to the socket associated with the web page requests.
Any help on this would be much appreciated (especially if I'm reasoning about this the wrong way).
When a TCP connection is opened to a given port, the server (Node + Express) has no way of telling who made that connection (whether it's a browser or your custom client).
Therefore, your custom client must speak HTTP if it wishes to communicate with the Express server sitting on port 80. Otherwise, the data you send over a freshly opened socket (in your custom protocol) will just look like garbage to Express, and it will close the connection.
However, this doesn't mean you can't get a TCP stream to speak a custom protocol over – you just have to speak HTTP first and ask to switch protocols. HTTP provides a mechanism exactly to accomplish this (the Upgrade header), and in fact it is how WebSockets are implemented.
When your Flash client first opens a TCP connection to your server, it should send: (note line breaks MUST be sent as CRLF characters, aka \r\n)
GET /gamesocket HTTP/1.1
Upgrade: x-my-custom-protocol/1.0
Host: example.com
Cache-Control: no-cache
The value of Upgrade is your choice, Host MUST be sent for all HTTP requests, and the Cache-Control header ensures no intermediate proxies service this request. Notice the blank line, which indicates the request is complete.
The server responds:
HTTP/1.1 101 Switching Protocols
Upgrade: x-my-custom-protocol/1.0
Connection: Upgrade
Again, a blank line indicates the headers are complete, and after that final CRLF, you are now free to send any data you like in any format over the TCP connection.
To implement the server side of this:
app.get('/gamesocket', function(req, res) {
if (req.get('Upgrade') == 'x-my-custom-protocol/1.0') {
res.writeHead(101, { Upgrade: req.get('Upgrade'), Connection: 'Upgrade' });
// `req.connection` is the raw net.Socket object
req.connection.removeAllListeners(); // make sure Express doesn't listen to the data anymore... we've got it from here!
// now you can do whatever with the socket
req.connection.setEncoding('utf8');
req.connection.write('foo');
req.connection.on('data', onMessageReceived);
} else res.send(400); // bad request
});
Of course, remember that TCP is not a message-based protocol, it only provides a stream, and thus the data events of a Socket can either fragment a single logical message into multiple events or even include several logical messages in a single event. Be prepared to manually buffer data.
Your other option here is to use socket.io, which implements a WebSockets server plus its own protocol on top of the WebSockets protocol. The WebSockets protocol is message-based. It mostly works just like I've outlined here, and then after HTTP negotiation adds a message framing layer on top of the TCP connection so that the application doesn't have to worry about the data stream. (Using WebSockets also opens the possibility of connecting to your server from a HTML page if necessary.)
There is a Flash socket.io client available.
Related
I would like to build a web page with interactive content.
I would like to use socket IO, but I've got a problem when I send two number from client to the server.
The server adds the two numbers and sends for every user (but I want just send back to the one user). I wouldn't like to store the users. So I would like to ask how can I build this example using NodeJS and Socket IO?
if you look into socket.io docs, you will find out that you can specify the key for each message you send, for instance:
io.on('connection', function (socket) {
// specify a clientId for each client you have, you may define it at the moment the connection starts.
socket.emit(`news:${clientId}`, { hello: 'world' });
});
And, on the client side, you have:
socket.on(`news:${myClientId}`, function (data) {
console.log(data);
});
You may generate this id randomly by using many libraries, for instance Node-Forge.
Hope it helps! Feel free to ask further.
I have a nodeJS server, that takes JSON from three websites and sends it to be displayed on my website(in JSON). The JSON on the websites that I'm taking from is constantly updated, every 10 seconds. How can I make my NodeJS server constantly update so it has the most up to date data?
I'm assuming this isn't possible without refreshing the page, but it would be optimal if the page wasn't refreshed.
If this is impossible to do with NodeJS and there is a different method of accomplishing this, I would be extremely appreciative if you told me.
Code:
router.get("/", function(req, res){
var request = require('request-promise');
var data1;
var data2;
var data3;
request("website1.json").then(function(body){
data1 = JSON.parse(body);
return request("website2.json");
})
.then(function(body) {
data2 = JSON.parse(body);
return request("website3.json");
})
.then(function(body){
data3 = JSON.parse(body);
res.render("app.ejs", {data1: data1, data2: data2, data3: data3});
})
});
Here's some general guidelines:
Examine the APIs you have available from your external service. Find out if there is anything it offers that lets you make a webSocket connection or some other continuous TCP connection so you can get realtime (or close to realtime) notifications when things change. If so, have your server use that.
If there is no realtime notification API from the external server, then you are just going to have to poll it every xx seconds. To decide how often you should poll it, you need to consider: a) How often you really need new data in your web pages (for example, maybe data that is current within 5 minutes is OK), b) What the terms of service and rate limiting are for the 3rd party service (e.g. how often will they let you poll it) and c) how much your server can afford to poll it (from a server load point of view).
Once you figure out how often you're going to poll the external service, then you build yourself a recurring polling mechanism. The simplest way would be using setInterval() that is set for your polling interval time. I have a raspberry pi node.js server that uses a setInterval() to repeatedly check several temperature sensors. That mechanism works fine as long as you pick an appropriate interval time for your situation.
Then for communication of new information back to a connected web page, the best way to get near "real time" updates form the server is for the web page to make a webSocket or socket.io connection to your server. This is a continuously connected socket over which messages can be sent either way. So, using this mechanism, the client makes a socket.io connection to your server. The server receives that connection and the connection stays open for the lifetime of that web page. Then, anytime your server has new data that needs to be sent to that web page, it can just send a message over that socket.io connection. The web page will receive that message and can then update the contents of the web page accordingly based on the data in the message. No page refresh is needed.
Here's an outline of the server code:
// start up socket.io listener using your existing web server
var io = require('socket.io')(app);
// recurring interval to poll several external web sites.
setInterval(function () {
var results = {};
request("website1.json").then(function (body) {
results.data1 = JSON.parse(body);
return request("website2.json");
}).then(function (body) {
results.data2 = JSON.parse(body);
return request("website3.json");
}).then(function (body) {
results.data3 = JSON.parse(body);
// decide if anything has actually changed on external service data
// and whether anything needs to be sent to connected clients
io.emit("newData", results);
}).catch(function(err) {
// decide what to do if external service causes an error
});
}, 10000);
The client code would then be generally like this:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
socket.on("newData", function(data) {
// process new data here and update the web page
});
</script>
I have this problem with compression, and I am not sure if it is a bug. My WebSocket server does not support context takeover, and I am having problems sending messages, but not receiving.
The browser issues a request like this:
GET /socket HTTP/1.1
Host: thirdparty.com
Origin: http://example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame
If the server does not specify any option about context takeover:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Access-Control-Allow-Origin: http://example.com
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Extensions: permessage-deflate
I can read and write the first message, but cannot do subsequent reads or writes, because Chrome expects the server is keeping the context.
So my server provides this answer:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Access-Control-Allow-Origin: http://example.com
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; server_no_context_takeover
And now I can receive messages without problems, but again, I can only send the first message, the second message fails, and I see an error in Chrome saying that it failed at inflating the frame. I tried to send two identical strings, and I can see how the server is sending twice the same data, but the client fails to decompress it the second time.
So it seems that Chrome accepts the client_no_context_takeover parameter that specify that the client won't use the same compression context for all messages when compressing, but ignores server_no_context_takeover indicates the server won't use the same context.
Is this a bug in Chrome? I am not clear about if I can send options back that have not been offered/requested by the client.
Is there any other option I can use to disable the client context takeover?
UPDATE:
In WebSocketPerMessageDeflate.cpp in the Chromium source code, I can see:
if (clientNoContextTakeover != parameters.end()) {
if (!clientNoContextTakeover->value.isNull()) {
m_failureReason = "Received invalid client_no_context_takeover parameter";
return false;
}
mode = WebSocketDeflater::DoNotTakeOverContext;
++numProcessedParameters;
}
But also:
if (serverNoContextTakeover != parameters.end()) {
if (!serverNoContextTakeover->value.isNull()) {
m_failureReason = "Received invalid server_no_context_takeover parameter";
return false;
}
++numProcessedParameters;
}
In the first snippet, it is setting the "mode" variable, but in the second one is not doing nothing, so it seems it is basically ignoring the parameter.
Cheers.
A server must send a server_no_context_takeover parameter in a response only if the client requested the "no context takeover". In essence, the server acknowledges the client's request.
If a server decides to do "no context takeover" for sending on it's own (without the client having requested it), that's fine. In this case, no parameter is sent by the server.
A deflate sender can always on it's own drop compression context and/or reduce compression window size. There is no need to tell the receiver. The deflate wire format has enough information for the receiver to cope with that.
Here is how configuration and handshake looks with Crossbar.io.
I finally found the problem.
https://datatracker.ietf.org/doc/html/draft-ietf-hybi-permessage-compression-17#section-8.2.3
8.2.3.4. Using a DEFLATE Block with BFINAL Set to 1
Going through the examples in the draft I found my server was sending slightly different payloads. Turned out that the problem was the BFINAL, I need to set it to 0 by adding a 0 byte at the end.
Now it works.
Here is a simplified version of some Flash code I have been using to get username of the person playing the game:
var req:URLLoader = new URLLoader();
req.addEventListener(Event.COMPLETE, onLoaded);
req.load(new URLRequest(baseURL + "php/account.php?getUsername"));
function onLoaded(e:Event):void {
if(e.target.data == "Guest") {
LoadingText.text = "Welcome, Guest!"
}
else {
LoadingText.text = "Welcome, " + e.target.data + "!";
}
}
php/account.php?getUsername just gets the username from a PHP session variable and outputs it.
Using something like Tamper Data for Firefox, I can see the request being sent from Flash and can even edit it if I wanted.
This approach feels very slow compared to other online games, and when trying to reverse engineer other games to see how they manage something like this, for some games, requests wont show up in Tamper Data.
So I wonder, are there any alternative ways to send requests in Flash which are faster and more efficient? I don't need anything fancy such as sending post data or editing the referral URL.
Thanks.
Most probably other games use socket connections. This is reason why you don't see request in Tamper Data and why they have better performance.
In case of socket connection you open connection to server once and you can make as many requests as you wish.
With HTTP, TCP connection is opened and closed for each request this affects performance.
With socket connections you can avoid HTTP protocol completely, you can design your protocol much lighter than HTTP. In fact no one uses HTTP when you need high performance.
See flash socket documentation here: Socket
P.S.
For high performance it is better to use UDP instead of TCP but flash sockets are limited to TCP so you don't need to take care of this.
I have been trying to implement HTML5 socket server to broadcast whatever it receives to all its connected clients but have no success.
I am new to sockets, can someone pelase suggest me if there is anything already available opensource or what really is it one has to check for doing so. All i could see is client to server communication but there is no way i can send data from one client to server to the other client or simply put, the server just broadcast all messages to all its connected client??
It sounds like you're trying to achieve peer-to-peer communication, which isn't possible over websockets.
It wouldn't be very difficult to set up a fast broadcast server using Node.js and CoffeeScript, that just echoes everything it receives from one socket to all of the others connected:
net = require 'net'
Array::remove = (e) -> #[t..t] = [] if (t = #indexOf(e)) > -1
class Client
constructor: (#socket) ->
clients = []
server = net.createServer (socket) ->
client = new Client(socket)
clients.push client
socket.addListener 'connect', ->
socket.write "Welcome\r\n"
socket.addListener 'data', (data) ->
for c in clients when c isnt client
c.socket.write data
socket.addListener 'end', ->
clients.remove client
socket.end
.listen 4000
console.log "Chat server is running at localhost:4000"