SocketIO message originated from the server is not working - javascript

I am developing an app and using the websockets for the server-client communication. The concept is to have the client requesting from the server for messages and a few times the server needs to push some messages to the client (without the latter has requested for them). (I'll use front-end and back-end to describe my app)
The app works when the front-end requests from the back-end but it doesn't for the case where the back-end needs to be the originator of the message (i.e. emit data without the front-end has requested for that). In that case the websocket seems to stall and blocked for a few seconds, until the client disconnects (reason timeout) and connects again. Of course, the topic sent by the back-end/server is never received by the client, i.e. the /non_requested_topic as seen below.
For the BACK-END I am using flask-socketio in PY2.7 and
events_handling.py
#!/usr/bin/env python
from flask import request
from emit_topics import emit_topic
def on_connect():
print("Client {} connected".format(request.sid))
# set the client ID to unrequested.py blah blah
def on_disconnect():
print("Client {} disconnected".format(request.sid))
def on_topic_request(data):
data_rx = "blah blah"
to = request.sid # client
namespace = "my_namespace"
emit_topic(topic_name="/topic_name", data_rx, to, namespace)
emit_topics.py
def emit_topic(topic_name, data, to, namespace):
socket_io.emit(topic_name, data=data, to=to, namespace=namespace)
And then there is a function that calls emit_topic and pushes some data to the client (without the client has requested for them):
unrequested.py
#!/usr/bin/env python
from emit_topics import emit_topic
def function_a(self):
to = self.client_id # client id that is set every time the client connects
namespace = "my_namespace"
data_rx="Msg from the server"
print("Check the client id {}".format(to)) # this matches with the one observed for the ws
emit_topic (topic_name="/non_requested_topic", data_rx, to, namespace)
For the FRONT-END:
There is an HTML file where:
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script>
var socket = io("my_namespace", {'forceNew': true});
var interval_timer;
socket.on('connect', function() {
console.log('Connected to the server');
interval_timer = setInterval(topics_request, 5000);
});
socket.on('disconnect', function() {
console.log('Disconnected from the server');
clearInterval(interval_timer);
});
socket.on("/topic_name", (msg) => {
console.log("/topic_name rx ", msg);
});
socket.on("/non_requested_topic", (msg) => {
console.log("Non requested topic rx ", msg);
});
function topics_request(){
socket.emit("topic_request", {"topic": "/topic_name"}) // some topic name
}
Is a request from the client required first to trigger the /non_requested_topic or is there a way for the client to keep listening to that?

You have several issues with your code.
First of all, Python 2.7 has not been a supported version of Python for Flask-SocketIO for a while. I strong advise you to start using Python 3.
Your use of namespaces in the server is very inconsistent. Your connect and disconnect handlers do not use a namespace. Your on_topic_request function doesn't either. But then the emit_topic function accepts a namespace as an argument, which you set to my_namespace. Why do you use a namespace only when emitting but not when receiving events? Also, namespaces are supposed to start with a slash.

Related

Python: Send data every 10 seconds via socket.io

I have a question very similar to this one: Send data every 10 seconds via socket.io
But: My server is written in Python, the client is in JavaScript
The goal:
Clients connect to server via socket.io
Clients receive push messages ping from server every n seconds
When a client sends a ping message, the server broadcasts a pong message
What works:
Socket.io connection works fine
Client ping is received by server, answered with pong, which is again received by client
Server executes ping_in_intervals every 5 seconds
What doesn't work:
When server executes ping_in_intervals (which triggers sending a ping), that ping is not received by any client
When ping_in_intervals loop is active, socket connections crash every minute or so. If the method is commented out, then socket connection stays stable.
Observations:
The thread, that ping_in_intervals is running in doesn't seem to properly work together with the wsgi server thread.
The ping_in_intervals thread destabilizes the server thred, causes it to loose connections (which are reestablished right away, but they do drop every minute or so)
I think, that I'm doing something terribly wrong with threading. I have very little experience with threading in Python and don't know, where to look for the problem
Server:
import eventlet
import socketio
import threading
sio = socketio.Server(cors_allowed_origins="*", async_mode='eventlet')
app = socketio.WSGIApp(sio)
def ping_in_intervals():
threading.Timer(5.0, ping_in_intervals).start()
print("send ping")
sio.emit('ping')
#sio.on('ping')
def ping(*args):
print("received ping - send pong")
sio.emit('pong')
ping_in_intervals()
eventlet.wsgi.server(eventlet.listen(('', 8080)), app)
Client:
const socket = io.connect('localhost:8080', {secure: true, transports: ['websocket']});
socket.on('pong', () => {
console.log('received pong');
});
socket.on('ping', () => {
console.log('received ping');
});
socket.on('connect', () => {
socket.emit('ping')
});
Found the solution at https://github.com/miguelgrinberg/python-socketio/blob/main/examples/server/wsgi/app.py#L16-L22
The thread, which pushes server messages every n seconds, shouldn't be started using threading, but instead using the start_background_task function of socketio.
Here's the working code:
import eventlet
import socketio
sio = socketio.Server(cors_allowed_origins="*", async_mode='eventlet')
app = socketio.WSGIApp(sio)
def ping_in_intervals():
while True:
sio.sleep(10)
sio.emit('ping')
#sio.on('ping')
def ping(*args):
sio.emit('pong')
thread = sio.start_background_task(ping_in_intervals)
eventlet.wsgi.server(eventlet.listen(('', 8080)), app)

Connecting client to socket in Angularjs

I'm very new to Socket protocol and I'm sure the problem comes from me knowing almost nothing about this. But basically I have a socket on port 5000 on my server and I need to have an angularjs code to listen to this socket. The socket on the server can read whatever I send from another computer (client). But for some reason the angular code can't listen/connect to the socket. Here's what I have right now:
index.html
<html ng-app="MyAwesomeApp">
<head>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/ng-websocket/ng-websocket.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="cnt">
</body>
</html>
and here's the angularjs code:
app.js
var app = angular.module('MyAwesomeApp', ['ngWebsocket']);
app.controller('cnt', function ($websocket) {
var ws = $websocket.$new('ws://localhost:5000');
ws.$on('$open', function () {
ws.$emit('hello', 'world'); // it sends the event 'hello' with data 'world'
})
.$on('test', function (message) { // it listents for 'incoming event'
console.log('something incoming from the server: ' + message);
});
});
and here's the python code that I have for server socket:
#server example
import socket
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind(('localhost', 5000))
serversocket.listen(1) # become a server socket, maximum 5 connections
# print "hello"
while True:
connection, address = serversocket.accept()
print address
while True:
buf = connection.recv(16)
if len(buf) > 0:
connection.sendall(buf)
print buf
# break
Most of the angularjs code comes from https://coderwall.com/p/uhqeqg/html5-websocket-with-angularjs
These are the errors that I get in Chrome
ng-websocket.js:122 WebSocket connection to 'ws://localhost:5000/'
failed: Error during WebSocket handshake: net::ERR_CONNECTION_RESET
and in Firefox:
Firefox can’t establish a connection to the server at
ws://localhost:5000/.
What you are trying to do is not possible. At least not in a way you want to do it.
WebSockets is an application layer protocol, much like HTTP protocol is. Pay attention at ws part of ws://localhost:5000.
On the other side you are using plain BSD sockets. This is just a raw socket for communication between two parties. It needs an to 'have an idea' about what the other side (AngularJS) is 'speaking', i.e. needs to communicate using same protocol. In BSD sockets case it inherently doesn't.
That is why you get:
ng-websocket.js:122 WebSocket connection to 'ws://localhost:5000/'
failed: Error during WebSocket handshake: net::ERR_CONNECTION_RESET
To be able to do this, you will need some asynchronous programming framework with WebSockets protocol built on top of it. One suggestion is Autobahn.

Java Socket and JS WebSocket

So I am trying to make some sort of connection between my Java app and my Web app, I looked up websockets and they look really simple and easy to use :). And I created myself a Java Server, which uses the ServerSocket class.
Now the problem is I am able to connect to the server from the web, with the websocket, but I am unable to send data to the server... but when I tried to send data from a Java Client it worked fine... what might be the problem?
My Java/Scala (I followed this tutorial: https://www.tutorialspoint.com/java/java_networking.htm) server:
class Server(val port: Int) extends Thread {
private val serverSocket = new ServerSocket(port)
override def run(): Unit = {
try {
while(true) {
println("Waiting for client on port: " + serverSocket.getLocalPort)
val server = serverSocket.accept()
println(server.getRemoteSocketAddress)
val in = new DataInputStream(server.getInputStream())
println(in.readUTF())
val out = new DataOutputStream(server.getOutputStream())
out.writeUTF("Hello world!")
server.close()
}
} catch {
case s: SocketTimeoutException => println("Connection timed out!");
case e: Exception => e.printStackTrace()
}
}
}
My web js (I followed https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications ):
/**
* Created by samuelkodytek on 20/12/2016.
*/
var conn = new WebSocket('ws://127.0.0.1:8080');
conn.onopen = function(e) {
console.log("Connection established!");
conn.send("Hello!");
};
conn.onmessage = function(e) {
console.log(e.data);
};
A web socket server is not the same thing as a simple socket server. A server that offers web sockets must first offer HTTP or HTTPS services because the web socket is established when a web client sends an HTTP request with an Upgrade option and special fields for establishing the web socket. Even after the web socket is established, the connection still does not behave exactly like a regular socket. The Web Socket protocol uses frames to send or receive data. This is all considerably different from what you seem to expect.
One other thing that you should be aware of is that the browser will enforce the rule that the web socket must come from the same host as the page that is attempting to establish the web socket (the same protocol, address, and TCP port).

Using an IP address parameter in the creation of a Socket.io socket

In the following snippet, a tutorial author shows how to alter the original tutorial to include an http server. Here's the snippet.
var http = require(‘http’),
fs = require(‘fs’),
io = require(‘socket.io’),
index;
fs.readFile(‘./chat.html’, function (err, data) {
if (err) {
throw err;
}
index = data;
});
var server = http.createServer(function(request, response) {
response.writeHeader(200, {“Content-Type”: “text/html”});
response.write(index);
response.end();
}).listen(1223);
//and replace var socket = io.listen(1223, "1.2.3.4"); with:
var socket = io.listen(server);
The code in the original tutorial didn't include the http server, and socket was defined as simply:
var socket = io.listen(1223, "1.2.3.4");
I noticed that he replaces the variable's content io.listen(1223, "1.2.3.4"); with server which doesn't include the ip (1.2.3.4) anywhere.
My Question:
What is the purpose/effect of the referenced IP address?
Why is it excluded when passing an http server to create the socket?
When you are listening on a port, you can optionally include the IP address of a specific interface to listen on. For example, you might have several network interfaces with several IP addresses, and only want your service running on one of them. A more common use case is that you only want your server accessible on localhost, so you might have it listen only on 127.0.0.1.
Now, when you call io.listen(server) where server is an existing Node.js HTTP server, Socket.IO isn't actually opening a new listening connection at all. This is a shortcut for Socket.IO to wrap its methods on the existing HTTP server. If you wanted to specify a specific interface address to listen on, you would need to do it where .listen() is called on the HTTP server, above where you call io.listen(server).
More info in the documentation for raw network sockets in Node.js: http://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback

Call function in nodejs from angular application

I'm having an angular app(angular-seed app) which should call a function in nodejs(web-server.js).
The function in nodejs is just calls a batch file.
If I understood this correctly you want a click on the client-side (angular app) to call a batch file on the server side. You can do this in several ways depending on your requirements, but basically you want the client-side to send a http-request to the server (either with ajax call or form submit) and process this on the server that will call the batch file.
Client-side
On the client-side you need to have a button that uses the angular ng-click directive:
<button ng-click="batchfile()">Click me!</button>
In your angular controller you'll need to use the $http service to make a HTTP GET request to your server on some particular url. What that url is depends how you've set up your express app. Something like this:
function MyCtrl($scope, $http) {
// $http is injected by angular's IOC implementation
// other functions and controller stuff is here...
// this is called when button is clicked
$scope.batchfile = function() {
$http.get('/performbatch').success(function() {
// url was called successfully, do something
// maybe indicate in the UI that the batch file is
// executed...
});
}
}
You can validate that this HTTP GET request is made by using e.g. your browser's developer tools such as Google Chrome's network tab or a http packet sniffer such as fiddler.
Server-side
EDIT: I incorrectly assumed that angular-seed was using expressjs, which it doesn't. See basti1302's answer on how to set it up server-side "vanilla style" node.js. If you're using express you can continue below.
On the server side you need to set up the url in your express app that will perform the batch file call. Since we let the client-side above make a simple HTTP GET request to /performbatch we'll set it up that way:
app.get('/performbatch', function(req, res){
// is called when /performbatch is requested from any client
// ... call the function that executes the batch file from your node app
});
Calling the batch file is done in some ways but you can read the stackoverflow answer here for a solution:
node.js shell command execution
Hope this helps
The OP didn't mention express so I'll provide an alternative for the server side (Node.js part) without using any additional frameworks (which would require installing it via npm). This solution uses just node core:
web-server.js:
'use strict';
var http = require('http')
var spawn = require('child_process').spawn
var url = require('url')
function onRequest(request, response) {
console.log('received request')
var path = url.parse(request.url).pathname
console.log('requested path: ' + path)
if (path === '/performbatch') {
// call your already existing function here or start the batch file like this:
response.statusCode = 200
response.write('Starting batch file...\n')
spawn('whatever.bat')
response.write('Batch file started.')
} else {
response.statusCode = 400
response.write('Could not process your request, sorry.')
}
response.end()
}
http.createServer(onRequest).listen(8888)
Assuming you are on Windows, I would at first use a batch file like this to test it:
whatever.bat:
REM Append a timestamp to out.txt
time /t >> out.txt
For the client side, there is nothing to add to Spoike's solution.

Categories