Socket.IO make an infinte loop on connection !
It's weird and never happen until now. I don't know what's wrong. I try to rebuild the package, I copy some code from an older source and the same result...
io.on('connection', function(socket){
console.log('someone connected << infinite loop');
});
It's a basic ExpressJS server with http on top.
const
port = process.env.PORT || 3200,
app = require('express')(),
express = require('express'),
mysql = require('mysql'),
bodyParser = require('body-parser'),
path = require('path'),
methodOverride = require('method-override'),
http = require('http').Server(app),
io = require('socket.io')(http);
Client-Side
var socket = io();
The problem was at the client-side, I don't load the latest version of socket.io like on the server-side. In my package.json I have 2.0.3 and at the client-side was 1.2.0.. For everyone who have this problem
This happened to me when I emitted an array (on connection) and not an object...
I solved it by wrapping the array with {} so I'm now sending an object... i.e {array}...
e.g:
myArray = [{blah:"some value"},{blah2:"some other value"}];
socket.emit('target',myArray); //results in infinite loop
socket.emit('target',{myArray}); //sends ok and not looping
No need for socket version to match on server and client... in my case the client is c# and my server is node.js
Related
I have code to login to my email account to fetch recent emails:
def fetchRecentEmail(emailAddr, emailPassword, timeout=120):
host = fetch_imap_server(emailAddr) # e.g. 'outlook.office365.com'
with IMAP4_SSL(host) as session:
status, _ = session.login(emailAddr, emailPassword)
if status == 'OK':
# fetch most recent message
status, messageData = session.select("Inbox")
:
I'm trying to tweak it to go through a proxy.
ref: How can I fetch emails via POP or IMAP through a proxy?
ref: https://gist.github.com/sstevan/efccf3d5d3e73039c21aa848353ff52f
In each of the above resources, the proxy is of clean form IP:PORT.
However my proxy is of the form USER:PASS:HOST:PORT.
The proxy works:
USER = 'Pp7fwti5n-res-any-sid-' + random8Digits()
PASS = 'abEDxts7v'
HOST = 'gw.proxy.rainproxy.io'
PORT = 5959
proxy = f'{USER}:{PASS}#{HOST}:{PORT}'
proxies = {
'http': 'http://' + proxy,
'https': 'http://' + proxy
}
response = requests.get(
'https://ip.nf/me.json',
proxies=proxies, timeout=15
)
The following code looks like it should work, but errors:
HOST = 'outlook.office365.com'
IMAP_PORT = 963
PROXY_TYPE = 'http' # rainproxies are HTTP
mailbox = SocksIMAP4SSL(
host=HOST,
port=IMAP_PORT,
proxy_type=PROXY_TYPE,
proxy_addr=URL,
proxy_port=PORT,
username=USER,
password=PASS
)
emailAddress, emailPassword = EMAIL.split(',')
mailbox.login(emailAddress, emailPassword)
typ, data = mailbox.list()
print(typ)
print(data)
I needed to add a timeout arg/param in 2 places to get the code to run:
def _create_socket(self, timeout=None):
sock = SocksIMAP4._create_socket(self, timeout)
server_hostname = self.host if ssl.HAS_SNI else None
return self.ssl_context.wrap_socket(
sock, server_hostname=server_hostname
)
def open(self, host='', port=IMAP4_PORT, timeout=None):
SocksIMAP4.open(self, host, port, timeout)
Rather confusing that nobody else seems to have flagged that in the gist.
But it still won't work.
If I use any number other than 443 for IMAP_PORT I get this error:
GeneralProxyError: Socket error: 403: Forbidden
[*] Note: The HTTP proxy server may not be supported by PySocks (must be a CONNECT tunnel proxy)
And if I use 443, while I now get no error, mailbox = SocksIMAP4SSL( never completes.
So I am still far from a working solution.
I am hoping to run this code simultaneously on 2 CPU cores, so I don't understand the implications of using port 443. Is that going to mean that no other process on my system can use that port? And if this code is using this port simultaneously in two processes, does this mean that there will be a conflict?
Maybe you can try monkeypatching socket.socket with PySocket.
import socket
import socks
socks.set_default_proxy(socks.SOCKS5, HOST, PORT, True, USER, PASS)
socket.socket = socks.socksocket
Then check if your IMAP traffic is going through a given proxy.
The following code is part of some automated tests that I have written in python 3.6:
connected = False
def aiohttp_server(loop):
async def handler(msg, session):
global connected
if msg.type == sockjs.MSG_OPEN:
connected = True
if msg.type == sockjs.MSG_CLOSE:
connected = False
app = web.Application(loop=loop)
sockjs.add_endpoint(app, handler)
runner = web.AppRunner(app)
return runner
def run_server(runner, loop):
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s')
asyncio.set_event_loop(loop)
loop.run_until_complete(runner.setup())
site = web.TCPSite(runner, 'localhost', 8080)
loop.run_until_complete(site.start())
loop.run_forever()
def start_server():
loop = asyncio.new_event_loop()
t = threading.Thread(target=run_server, args=(aiohttp_server(loop),loop,), daemon=True)
t.start()
time.sleep(0.01)
Basically, calling start_server should initiate a simple web server with a sockjs endpoint named /sockjs
I am not yet a master of python's async keyword. There are two issues, that I suspect are related:
Firstly, I am getting a deprecation warning on the app = web.Application(loop=loop) statement:
/home/peter/incubator/sockjs_client/tests/test_sockjs_client.py:25: DeprecationWarning: loop argument is deprecated
app = web.Application(loop=loop)
/home/peter/.local/lib/python3.6/site-packages/sockjs/route.py:54: DeprecationWarning: loop property is deprecated
manager = SessionManager(name, app, handler, app.loop)
And secondly, the tests fail occasionally. I believe that, depending on machine load, sometimes the server hasn't had enough time to start before the test code actually starts executing.
Basically, what I need is for the start_server function to initialise a web application with a websocket endpoint, and not return until the application is prepared to accept websocket connections.
Firstly, I am getting a deprecation warning on the app = web.Application(loop=loop) statement:
The recommended way to avoid passing around the loop everywhere is to switch to asyncio.run. Instead of managing the loop manually, let asyncio.run create (and close) the loop for you. If all your work is done in coroutines, you can access the loop with get_event_loop() or get_running_loop().
Basically, what I need is for the start_server function to initialise a web application with a websocket endpoint, and not return until the application is prepared to accept websocket connections.
You can pass a threading.Event to the thread that gets set when the site is set up, and wait for it in the main thread.
Here is an (untested) example that implements both suggestions:
connected = False
def aiohttp_server():
async def handler(msg, session):
global connected
if msg.type == sockjs.MSG_OPEN:
connected = True
if msg.type == sockjs.MSG_CLOSE:
connected = False
app = web.Application()
sockjs.add_endpoint(app, handler)
return web.AppRunner(app)
async def run_server(ready):
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s')
runner = aiohttp_server()
await runner.setup()
site = web.TCPSite(runner, 'localhost', 8080)
await site.start()
ready.set()
# emulates loop.run_forever()
await asyncio.get_running_loop().create_future()
def start_server():
ready = threading.Event()
threading.Thread(target=asyncio.run, args=(aiohttp_server(ready),),
daemon=True).start()
ready.wait()
Please upgrade sockjs to the newest version.
It doesn't require passing the loop anymore.
I want to use a NodeMCU device (Lua based top level) to act as a websocket server to 1 or more browser clients.
Luckily, there is code to do this here: NodeMCU Websocket Server
(courtesy of #creationix and/or #moononournation)
This works as described and I am able to send a message from the client to the NodeMCU server, which then responds based on the received message. Great.
My questions are:
How can I send messages to the client without it having to be sent as a response to a client request (standalone sending of data)? When I try to call socket.send() socket is not found as a variable, which I understand, but cannot work out how to do it! :(
Why does the decode() function output the extra variable? What is this for? I'm assuming it will be for packet overflow, but I can never seem to make it return anything, regardless of my message length.
In the listen method, why has the author added a queuing system? is this essential or for applications that perhaps may receive multiple simultaneous messages? Ideally, I'd like to remove it.
I have simplified the code as below:
(excluding the decode() and encode() functions - please see the link above for the full script)
net.createServer(net.TCP):listen(80, function(conn)
local buffer = false
local socket = {}
local queue = {}
local waiting = false
local function onSend()
if queue[1] then
local data = table.remove(queue, 1)
return conn:send(data, onSend)
end
waiting = false
end
function socket.send(...)
local data = encode(...)
if not waiting then
waiting = true
conn:send(data, onSend)
else
queue[#queue + 1] = data
end
end
conn:on("receive", function(_, chunk)
if buffer then
buffer = buffer .. chunk
while true do
local extra, payload, opcode = decode(buffer)
if opcode==8 then
print("Websocket client disconnected")
end
--print(type(extra), payload, opcode)
if not extra then return end
buffer = extra
socket.onmessage(payload, opcode)
end
end
local _, e, method = string.find(chunk, "([A-Z]+) /[^\r]* HTTP/%d%.%d\r\n")
local key, name, value
for name, value in string.gmatch(chunk, "([^ ]+): *([^\r]+)\r\n") do
if string.lower(name) == "sec-websocket-key" then
key = value
break
end
end
if method == "GET" and key then
acceptkey=crypto.toBase64(crypto.hash("sha1", key.."258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
conn:send(
"HTTP/1.1 101 Switching Protocols\r\n"..
"Upgrade: websocket\r\nConnection: Upgrade\r\n"..
"Sec-WebSocket-Accept: "..acceptkey.."\r\n\r\n",
function ()
print("New websocket client connected")
function socket.onmessage(payload,opcode)
socket.send("GOT YOUR DATA", 1)
print("PAYLOAD = "..payload)
--print("OPCODE = "..opcode)
end
end)
buffer = ""
else
conn:send(
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nHello World!",
conn.close)
end
end)
end)
I can only answer 1 question, the others may be better suited for the library author. Besides, SO is a format where you ask 1 question normally.
How can I send messages to the client without it having to be sent as a response to a client request (standalone sending of data)?
You can't. Without the client contacting the server first and establishing a socket connection the server wouldn't know where to send the messages to. Even with SSE (server-sent events) it's the client that first initiates a connection to the server.
I've been working on a web application which is essentially a web messenger using sinatra. My goal is to have all messages encrypted using pgp and to have full duplex communication between clients using faye websocket.
My main problem is being able to send messages to a specific client using faye. To add to this all my messages in a single chatroom are saved twice for each person since it is pgp encrypted.
So far I've thought of starting up a new socket object for every client and storing them in a hash. I do not know if this approach is the most efficient one. I have seen that socket.io for example allows you to emit to a specific client but not with faye websockets it seems ? I am also considering maybe using a pub sub model but once again I am not sure.
Any advice is appreciated thanks !
I am iodine's author, so I might be biased in my approach.
I would consider naming a channel by the used ID (i.e. user1...user201983 and sending the message to the user's channel.
I think Faye will support this. I know that when using the iodine native websockets and builtin pub/sub, this is quite effective.
So far I've thought of starting up a new socket object for every client and storing them in a hash...
This is a very common mistake, often seen in simple examples.
It works only in single process environments and than you will have to recode the whole logic in order to scale your application.
The channel approach allows you to scale using Redis or any other Pub/Sub service without recoding your application's logic.
Here's a quick example you can run from the Ruby terminal (irb). I'm using plezi.io just to make it a bit shorter to code:
require 'plezi'
class Example
def index
"Use Websockets to connect."
end
def pre_connect
if(!params[:id])
puts "an attempt to connect without credentials was made."
return false
end
return true
end
def on_open
subscribe channel: params[:id]
end
def on_message data
begin
msg = JSON.parse(data)
if(!msg["to"] || !msg["data"])
puts "JSON message error", data
return
end
msg["from"] = params[:id]
publish channel: msg["to"].to_s, message: msg.to_json
rescue => e
puts "JSON parsing failed!", e.message
end
end
end
Plezi.route "/" ,Example
Iodine.threads = 1
exit
To test this example, use a Javascript client, maybe something like this:
// in browser tab 1
var id = 1
ws = new WebSocket("ws://localhost:3000/" + id)
ws.onopen = function(e) {console.log("opened connection");}
ws.onclose = function(e) {console.log("closed connection");}
ws.onmessage = function(e) {console.log(e.data);}
ws.send_to = function(to, data) {
this.send(JSON.stringify({to: to, data: data}));
}.bind(ws);
// in browser tab 2
var id = 2
ws = new WebSocket("ws://localhost:3000/" + id)
ws.onopen = function(e) {console.log("opened connection");}
ws.onclose = function(e) {console.log("closed connection");}
ws.onmessage = function(e) {console.log(e.data);}
ws.send_to = function(to, data) {
this.send(JSON.stringify({to: to, data: data}));
}.bind(ws);
ws.send_to(1, "hello!")
Is there a way to send events between two Meteor servers? I know I can connect Server1 to Server2 (and vice versa) using DDP.connect and just call methods between the two servers. This will not work for me, because one of my servers (Server1) will be at my house behind a dynamic IP and firewall. DDP.connect requires a url. What is the best way, if any, to communicate between the two servers? I am thinking the only way to do this would be something like Socket.io where Server1 connects to Server2. I'm not sure if this can be done in Meteor though. Thanks.
You can do this by using DDP.connect to connect server 1 to server 2 on startup, then subscribing to a collection that server 2 publishes, for example:
On Server 2 (known URL):
var Events = new Meteor.Collection("events");
Meteor.publish("events", function () {
return Events.find({});
}
On Server 1 (at your house):
var EventConnection = DDP.connect("<server 2 URL>");
var Events = new Meteor.Collection("events", {connection: EventConnection});
EventConnection.subscribe("events");
Events.find({}).observe({
added: function (newEvent) {
// do something with newEvent
}
});
Then, whenever server 2 adds an object to the Events collection, you will get it on server 1 via the connection. Watch out, though - every time server 1 connects to server 2 it will get all previous events as well. If you don't want that to happen, you need to use the ready callback on subscribe:
Revised code for Server 1:
var EventConnection = DDP.connect("<server 2 URL>");
var Events = new Meteor.Collection("events", {connection: EventConnection});
EventConnection.subscribe("events", function () {
Events.find({}).observe({
added: function (newEvent) {
// do something with newEvent
}
});
});