I am trying to build a chat application using channel API in Google App Engine.
When going through http://developers.google.com/appengine/docs/python/channel/overview
I could not understand the opening the socket part using the XMLHttpRequest();
Any help will be appreciated!!
Thanks
Basically, when the socket is opened, the code below updates the user interface of an example Tic Tac Toe game, and sends a POST message to the server asking for the latest game state.
The code is not Python, it's client-side Javascript. I have commented it below:
sendMessage = function(path, opt_param) {
/* path variable is part of a URL being maintained */
path += '?g=' + state.game_key; /* Saving game key in URL */
if (opt_param) {
path += '&' + opt_param; /* Adding optional parameters to the path */
}
var xhr = new XMLHttpRequest(); /* Used for Ajax in Javascript */
xhr.open('POST', path, true); /* Asynchronously POST, via HTTP, the path */
xhr.send(); /* Start the POST above */
};
onOpened = function() {
connected = true; /* Set boolean value, which lets us know we're connected */
sendMessage('opened'); /* We can now send messages to the server */
updateBoard(); /* Update user interface to reflect that socket is open */
};
Note that the application defines sendMessage() as a wrapper around XmlHttpRequest, which the client uses to send messages to the server.
The socket is "opened" when the channel between client and server is established. At that point the OnOpened callback is called. The callback does a POST request back to the server to get the current state of the game. So the XMLHttpRequest is not related to the opening of the socket but rather is just a common coding pattern to have a two way communication since the channel is just one way (server to client). The other route (client to server) is done through these HTTP requests. Almost every time you get a message in the channel from the server you will want to send something back to the server (a response, an update, etc.).
Hope this helps.
Related
I've never used before websockets neither web workers and everything around that. Obviously, I'm lost and I don't know how to make it properly.
I've learned how to make a websocket and leave it working in a server port successfully. As well, load a web worker (but this one can't refresh the website straight).
When a user is connected, everything is working fine. Websocket is sending messages to the user and the user is refreshing their website.
The problem is when I want to use it with many other users (different tabs or browsers to simulate different users). Only works with next user is connected with websocket, is not working anymore with the other users.
I share a diagram just to make it simple to understand what I want to do.
Another point is. Which language do I have to use to get this, both for the server and for the users? Python, NodeJs, Php (those are the only I know how to use).
SOLVED!
Just I generate a number assigned to each client (can be different device between each other) and I send the random number generated by server to each connection!
Before "connection" you shoul add:
const WS = require('ws');
const WS_PORT = 8081
const express = require('express');
const app = express();
const PORT = 3000;
app.listen(PORT, () => console.log(`Server listening , go to http://localhost:${PORT}`));
app.use(express.static('public'));
const wss = new WS.Server({ port: WS_PORT })
const wsSelected = new Set();
// Creating connection using websocket
const interval = setInterval(() => {
const randomNumber = Math.floor(Math.random() * 100);
//Sending same number to each client
wsSelected.forEach(ws =>
ws.send(randomNumber)
)
}, 2000);
After "connection" you should add:
wss.on("connection", ws => {
console.log("New client!");
//This line you should add
wsSelected.add(ws);
...
I'm assuming this isn't possible, but wanted to ask in case it is. If I want to provide a status information web page, I want to use WebSockets to push the data from the server to the browser. But my concerns are the effect a large number of browsers will have on the server. Can I broadcast to all clients rather than send discrete messages to each client?
WebSockets uses TCP, which is point to point, and provides no broadcast support.
Not sure how is your client/server setup, but you can always just keep in the server a collection of all connected clients - and then iterate over each one and send the message.
A simple example using Node's Websocket library:
Server code
var WebSocketServer = require('websocket').server;
var clients = [];
var socket = new WebSocketServer({
httpServer: server,
autoAcceptConnections: false
});
socket.on('request', function(request) {
var connection = request.accept('any-protocol', request.origin);
clients.push(connection);
connection.on('message', function(message) {
//broadcast the message to all the clients
clients.forEach(function(client) {
client.send(message.utf8Data);
});
});
});
As noted in other answers, WebSockets don't support multicast, but it looks like the 'ws' module maintains a list of connected clients for you, so it's pretty easy to iterate through them. From the docs:
const WebSocketServer = require('ws').Server;
const wss = new WebSocketServer({ port: 8080 });
wss.broadcast = function(data) {
wss.clients.forEach(client => client.send(data));
};
Yes, it is possible to broadcast messages to multiple clients.
In Java,
#OnMessage
public void onMessage(String m, Session s) throws IOException {
for (Session session : s.getOpenSessions()) {
session.getBasicRemote().sendText(m);
}
}
and here it is explained.
https://blogs.oracle.com/PavelBucek/entry/optimized_websocket_broadcast.
It depends on the server-side really. Here's an example of how it's done using Tomcat7:
Tomcat 7 Chat Websockets Servlet Example
and an explanation of the how it's constructed here.
Yes you can and there are many socket servers out there written in various scripting languages that are doing it.
The Microsoft.Web.WebSockets namespace has a WebSocketCollection with Broadcast capability. Look for the assembly in Nuget. The name is Microsoft.WebSockets.
I am currently involved in an Arduino and Google App Engine (GAE) project. I am designing an ambient sensor system, consisting of a temperature sensor, light sensor and motion sensor. I am also developing a webapp using Google App Engine. Users can use this webapp to view their ambient status through the cloud, in an IoT fashion.
I have an Arduino Uno and an Arduino Ethernet Shield. I have been successfully able to send sensor data to GAE datastore, but only when login is DISABLED in the webapp.
When I enable user login for the webapp, the Arduino data DOES NOT get sent to the google datastore.
My question: How do I get the Arduino to send data to the Google Datastore, with a username identity appended to it?
Ideal scenario: users can append their google account email address to the arduino code, and data that is sent from the arduino to the datastore is visible by this Google account only, when the user logs into the app with their Google account.
Right now, only the URL of my webapp is provided in the arduino code. How and where do I indicate a particular Google account identity? In my arduino code, this is the only line which informs the arduino of my webapp. What other lines of code should I include to achieve my desired results?
//Server to connect to
char serverName[] = "http://ambienators-nus.appspot.com";
Here is the partial arduino code.
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address for your controller below
byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
//Server to connect to
char serverName[] = "http://ambienators-nus.appspot.com";
// Initialize the Ethernet client library
EthernetClient client;
void setup()
{
analogReference(INTERNAL);
Serial.begin(9600);
// start the Ethernet connection:
if (Ethernet.begin(mac) == 0)
{
while(true);
}
// give the Ethernet shield a second to initialize:
delay(1000);
}
void postData()
{
String temperature = String((int)(temp+0.5));
String lightStr = String((int)(light));
String move = String(movement);
String moves1 = String(moves);
String data = String("temp="+ temperature+"&movement="+move+"&moves="+moves1+"&light="+lightStr);
while(!client.connected())
{
Serial.println("Connecting to client: ");
client.connect(serverName, 80);
}
client.println("POST /arduino_post HTTP/1.1");
client.println("Host: ambienators-nus.appspot.com");
client.println("Connection: keep-alive");
client.print("Content-Length: ");
client.println(data.length());
client.println("Cache-Control: max-age=0");
client.println("Content-Type: application/x-www-form-urlencoded");
client.println("Accept-Language: en-US,en;q=0.8");
client.println();
client.print(data);
Serial.print(data);
client.println();
Serial.println();
char c;
while(client.available())
{
c = client.read();
}
client.stop();
}
You may view the webapp here: http://ambienators-nus.appspot.com
You can find the rest of the webapp code in this github repo: https://github.com/taniach/ambienators-nus
Didn't go through all the code, and personally I think that a good practice is to post just significant parts of it for letting people get more easily where the problem could be.
As for your question, why together with sensor data not sending also a string that identify that specific arduino board (this could be maybe the username of the person associated with the board? This way is the webapp could know what board/user is sending each chunck of data and store the values in the correspondent database entity.
I have a nodejs app which listen for messages from clients (python app).
the pattern i used for communication over zmq is REQ/REP pattern.
the Main app should get messages from many clients. it will not reply to them, just get messages.
the problem is the main app will only get the first message and the next messages are not shown in nodejs app console.
in other words every time i start nodejs app i only get one message.
here is my code:
Nodejs app
var responder = zmq.socket('rep');
responder.on('message', function(request) {
console.log(request);
//here, it seems this function will be called just once!
});
responder.bind('tcp://127.0.0.1:8000', function(err) {
if (err) {
console.log(err);
} else {
console.log('Listening on 8000...');
}
});
python (client) part:
socket = context.socket(zmq.REQ)
socket.connect("tcp://127.0.0.1:8000")
socket.send('blaaaa')
print 'message sent!'
python part is inside a function. i could see the output of "message sent!" in python console(i mean many 'message sent!').
but i could not see the messages in nodejs app.just the first message is seen in the console of nodejs.
When using the REQ/REP-pattern you actually need to respond to a request before you are given the next request - you will only handle one request at the time.
var responder = zmq.socket('rep');
responder.on('message', function(request) {
console.log(request);
responder.send('Here comes the reply!');
});
Respond, and you will receive the next one. If you do not wish to respond, then you need to choose some other socket pair than req/rep - ex: push/pull or maybe look at xreq/xrep (router/dealer) if you wish to handle multiple requests at the same time.
If in doubt, look up the send/receive pattern for each socket type at http://api.zeromq.org/2-1:zmq-socket
I want to build an upload-centric app using Django. One way to do this is with nginx's upload module (nonblocking) but it has its problems. Node.js is supposed to be a good candidate for this type of application. But how can I make node.js act as an upload_handler() for Django? I'm not sure where to look for examples?
Okay I'm not like an expert on the subject or anything, but the way I think I understand it, nginx is a proxy that runs in front of the webserver that serves your django app, right?
You could build a simple node.js server that does the same - listen on port 80, wait until the request is completely sent to the server, and then forward it to the webserver that serves the Django app. If your problem is that the webservers threads are being used up by long running uploads, then I think this would solve that problem.
Here's some code - just off the top of my head
var http = require('http');
var server = http.createServer(function (req, res) {
var headers = req.headers;
var url = req.url;
var method = req.method;
var body = '';
req.addListener('data', function(chunk) {
body += chunk;
});
req.addListener('end', function() {
// at this point the request is completely uploaded (including files)
// so it can be forwarded to the django webserver
var dj_client = http.createClient(8000, 'localhost');
var dj_req = dj_client.request(method, url, headers);
dj_req.addListener('response', function (dj_res) {
// here the response have been received from the django server
// so we can return that to the waiting client
res.writeHead(dj_res.status, dj_res.headers);
dj_res.addListener('data', res.write);
dj_res.addListener('end', res.close);
});
// send the request to the django webserver
dj_req.write(body);
dj_req.close();
});
});
server.listen(80);
Make node.js write the file to disk and proxy the upload POST to Django along with a '_node_file' value so your Django view knows where to get the file.