I am using Julia's SimpleWebSockets to open a websocket connection. I want it to reconnect after any disconnects. Something is wrong with the notify commented below, it doesnt run after disconnect, thereby preventing the rest of the code from continuing.
using SimpleWebsockets
function connect_loop(C::Coinbase, msg_channel::Channel)
while true
client = WebsocketClient()
ended = Condition()
listen(client, :connect) do ws
listen(ws, :message) do message
put!(msg_channel, message)
end
listen(ws, :error) do reason
#warn "Websocket connection error" reason...
notify(ended)
end
listen(ws, :close) do reason
#warn "Websocket connection closed" reason...
notify(ended)
end
# for msg in C.command_channel
# send(ws, JSON.json(msg))
# end
end
listen(C.client, :connectError) do err
println("Websocket Connect Error.")
notify(ended) # This is not working !!!!!!!!!
end
println("connecting")
#async open(C.client, "wss://ws-feed.pro.coinbase.com")
println("wait for ended")
wait(ended)
println("ended")
sleep(2)
end
end
Procedure is to connect on Wifi, then after successful connection disable the wifi and we should see it go into a reconnect loop, but instead it just stalls waiting on that notify call.
connecting
wait for ended
┌ Warning: Websocket connection closed
│ code = 1006
│ description = "could not ping the server."
└ # Main ~/src/tibra/Tibra.jl/src/MarketDataIO/Coinbase.jl:60
ended
connecting
wait for ended
Websocket Connect Error.
Related
I am new to lua programming and trying to implement Lua-websocket in openwrt as a client. here is the library.
Was trying to use client copas library but the issue is the script is getting stopped listening server after executing once (i.e. connecting to server, receiving message, sending message). I want the script to always listen the server without any timeout or script halt.
Below is the script
local copas = require'copas'
local websocket = require'websocket'
local json = require('json')
local client = require 'websocket.client'.new()
local ok,err = client:connect('ws://192.168.1.250:8080')
if not ok then
print('could not connect',err)
end
local ap_mac = { command = 'subscribe', channel = 'test' }
local ok = client:send(json.encode(ap_mac))
if ok then
print('msg sent')
else
print('connection closed')
end
local message,opcode = client:receive()
if message then
print('msg',message,opcode)
else
print('connection closed')
end
local replymessage = { command = 'message', message = 'TEST' }
local ok = client:send(json.encode(replymessage))
if ok then
print('msg sent')
else
print('connection closed')
end
copas.loop()
Here copas.loop() is not working.
On openWrt I had installed lua 5.1
Short answer: you do not use Copas correctly.
In detail: copas.loop does nothing, because you have neither created a Copas server, nor a Copas thread. Check the Copas documentation.
The send and receive actions in your script are performed outside Copas, because they are not within a Copas.addthread (function () ... end). You also create a websocket client that is not a copas one, but a synchronous one (the default). Check the lua-websocket documentation and its examples.
The solution:
local copas = require'copas'
local websocket = require'websocket'
local json = require'cjson'
local function loop (client)
while client.state == "OPEN" do
local message, opcode = client:receive()
... -- handle message
local replymessage = { command = 'message', message = 'TEST' }
local ok, err = client:send(json.encode(replymessage))
... -- check ok, err
end
end
local function init ()
local client = websocket.client.copas ()
local ok,err = client:connect('ws://192.168.1.250:8080')
... -- check ok, err
local ap_mac = { command = 'subscribe', channel = 'test' }
ok, err = client:send(json.encode(ap_mac))
... -- check ok, err
copas.addthread (function ()
loop (client)
end)
end
copas.addthread (init)
copas.loop()
The init function instantiates a client for Copas. It also starts the main loop in a Copas thread, that waits for incoming messages as long as the connection is open.
Before starting the Copas loop, do not forget to add a Copas thread for the init function.
I have 2 very simple Vertx verticles written in Ruby:
### sender.rb
require 'vertx/vertx'
require 'date'
vertx = Vertx::Vertx.vertx()
event_bus = vertx.event_bus()
vertx.set_periodic(2000) { |v|
msg = "Ruby NOW " + DateTime.now.strftime("%H:%M:%S")
puts(msg)
event_bus.publish("news.uk.sport", msg)
}
and
### listener.rb
require 'vertx/vertx'
vertx = Vertx::Vertx.vertx()
event_bus = vertx.event_bus()
consumer = event_bus.consumer("news.uk.sport")
consumer.handler() { |message|
puts "I have received a message: #{message.body()}"
}
consumer.completion_handler() { |res_err,res|
if (res_err == nil)
puts "The handler registration has reached all nodes"
else
puts "Registration failed!"
end
}
I have constructed these two verticles exactly from the code examples from vertx.io documentation.
I open 2 terminal sessions.
In 1st terminal I deploy the sender.rb verticle:
$ vertx run sender.rb
in 2nd terminal i deploy listener.rb verticle:
$ vertx run listener.rb
The sender sends and prints messages, but listener, doesn't receive anything (no print output from listener.rb)
I have tried to run vertx with flags
-ha
and
-cluster
but it didn't help. Please help.
UPDATE
Many thanks to Tsegismont
I have re-implemented both the listener.rb and the sender.rb and it worked :)
Now i just have little question purely out of curiosity:
In my sender:
simple_eventbus git:(master) ✗ vertx run sender.rb -cluster
Starting clustering...
No cluster-host specified so using address 192.168.7.179
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 2071 ms, time limit is 2000
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 3073 ms, time limit is 2000
You're already on a Vert.x context, are you sure you want to create a new Vertx instance?
Succeeded in deploying verticle
On Sender site: We now have a clustered event bus: #<Vertx::EventBus:0x61deddf7>
The message has been sent: 13:20:13
The message has been sent: 13:20:15
The message has been sent: 13:20:17
The sender.rb is:
require 'vertx/vertx'
require 'date'
options = {
}
Vertx::Vertx.clustered_vertx(options) { |res_err,res|
if (res_err == nil)
vertx = res
event_bus = vertx.event_bus()
puts "On Sender site: We now have a clustered event bus: #{event_bus}"
vertx.set_periodic(2000) { |v|
msg = "Message from Sender: ruby message NOW " + DateTime.now.strftime("%H:%M:%S")
puts "The message has been sent: " + DateTime.now.strftime("%H:%M:%S")
event_bus.publish("news.uk.sport", msg)
}
else
puts "Failed: #{res_err}"
end
}
and my listener.rb output:
simple_eventbus git:(master) ✗ vertx run listener.rb -cluster
Starting clustering...
No cluster-host specified so using address 192.168.7.179
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 2169 ms, time limit is 2000
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 3172 ms, time limit is 2000
You're already on a Vert.x context, are you sure you want to create a new Vertx instance?
Succeeded in deploying verticle
On listener side: We now have a clustered event bus: #<Vertx::EventBus:0x44af7bbf>
The handler registration has reached all nodes
I have received a message: Message from Sender: ruby message NOW 13:20:13
I have received a message: Message from Sender: ruby message NOW 13:20:15
I have received a message: Message from Sender: ruby message NOW 13:20:17
The listener.rb code:
require 'vertx/vertx'
options = {
}
Vertx::Vertx.clustered_vertx(options) { |res_err,res|
if (res_err == nil)
vertx = res
event_bus = vertx.event_bus()
puts "On listener side: We now have a clustered event bus: #{event_bus}"
consumer = event_bus.consumer("news.uk.sport")
consumer.handler() { |message|
puts "I have received a message: #{message.body()}"
}
consumer.completion_handler() { |res_err,res|
if (res_err == nil)
puts "The handler registration has reached all nodes"
else
puts "Registration failed!"
end
}
else
puts "Failed: #{res_err}"
end
}
In my sender.rb the eventBus object assigned is:
#<Vertx::EventBus:0x61deddf7>
In my listener.rb it is different:
#<Vertx::EventBus:0x44af7bbf>
Is it just the object instance reference?
Doesn't have to be the same object instance to be shared?
And another question is why does it tell me onDeploy this?
Thread Thread[vert.x-eventloop-thread-2,5,main] has been blocked for 3073 ms, time limit is 2000
You're already on a Vert.x context, are you sure you want to create a new Vertx instance?
I have run the code once: vertx run listener.rb -cluster
Why does it tell me that I am already in Vertx context?
The problem is that your code creates a standalone Vert.x instance. To create a clustered Vert.x instance, follow this example:
require 'vertx/vertx'
options = {
}
Vertx::Vertx.clustered_vertx(options) { |res_err,res|
if (res_err == nil)
vertx = res
eventBus = vertx.event_bus()
puts "We now have a clustered event bus: #{eventBus}"
else
puts "Failed: #{res_err}"
end
}
from the Vert.x Event Bus clustering documentation section.
I've this Ruby server that uses a Unix Socket:
require 'socket'
server = UNIXServer.new('/tmp/ciccio.sock')
loop do
sock = server.accept
loop do
begin
data = sock.recv(1024)
data = "DO SOMETHING -> #{data}"
sock.write(data)
sock.flush
rescue Errno::EPIPE, Errno::ENOTCONN
break
end
end
end
And I've this client in JavaScript that uses node.js net api:
Net = require('net');
var client = Net.connect({path: '/tmp/ciccio.sock'}, () => {
console.log('write data');
client.write('hello world!');
});
client.on('data', (data) => {
console.log(data.toString());
client.end();
});
client.on('end', () => {
console.log('end');
});
client.on('error', (error) => {
console.log(error.toString());
});
The problem is that at the first iteration of server loop, recv receives the right string and the server replies to the client with the string data. But at the second iteration recv receives empty data and so a infinite loop begins... server continues to receive empty data and recv does not block...
If I use a ruby client like this all works fine...
require 'socket'
sock = UNIXSocket.new('/tmp/ciccio.sock')
loop do
sock.write('hello world!')
data = sock.recv(1024)
puts data
sleep(10)
end
sock.close
Your client closes the connection upon connecting, writing "hello world", and then receiving a response. Now your server is calling recv on a socket that has been closed, which will return an empty string. Since you're looping to read on this socket indefinitely, it will never return control back to the outer loop to call accept for the second client. The ruby version of your client works because it never closes the connection to the server.
Breaking out of your inner loop after receiving an empty string should get you what you want,
require 'socket'
server = UNIXServer.new('/tmp/ciccio.sock')
loop do
sock = server.accept
loop do
begin
data = sock.recv(1024)
break if data.empty?
data = "DO SOMETHING -> #{data}"
sock.write(data)
sock.flush
rescue Errno::EPIPE, Errno::ENOTCONN
break
end
end
end
Another potential problem is that your server can only service a single connection to a single client, as the inner loop blocks any subsequent calls to accept until the current connection ends.
I am a beginnner in Tcl. I am trying to learn Tcl without getting involved in Tk. I came across commands like vwait and after but I was confused as most explanations involved the notion of event loop and further mostly demonstrated the concept by using Tk. I would like to understand the notion of events and event loop and how the commands I mentiond relate to them, please refere me some reference for this. The explantion should not use Tk as examples, use no Tcl extensions, assume no prior knowledge of events. Some (minimal) toy examples and real-wordl application of tcl event loop except GUI programming/Tk would be appreciated.
I came across this tutorial on Tclers wiki. I am looking for OTHER reference or explanation like this.
If you're not using Tk, the main reasons for using the event loop are for waiting in the background while you do some other I/O-bound task, and for handling server sockets.
Let's look at server sockets.
When you open a server socket with:
socket -server myCallbackProcedure 12345
You're arranging for an event handler to be set on the server socket so that when there is an incoming connection, that connection is converted you a normal socket and your supplied callback procedure (myCallbackProcedure) is called to handle the interaction with the socket. That's often in turn done by setting a fileevent handler so that incoming data is processed when it arrives instead of blocking the process waiting for it, but it doesn't have to be.
The event loop? That's a piece of code that calls into the OS (via select(), poll(), WaitForMultipleObject(), etc., depending on OS and build options) to wait until something happens on any nominated channel or a timeout occurs. It's very efficient, since the thread making the call can be suspended while waiting. If something happens, the OS call returns and Tcl arranges for the relevant callbacks to be called. (There's a queue internally.) It's a loop because once the events are processed, it's normal to go back and wait for some more. (That's what Tk does until there are no more windows for it to control, and what vwait does until the variable it is waiting for is set by some event handler.)
Asynchronous waits are managed using a time-ordered queue, and translate into setting the timeout on the call into the OS.
An example:
socket -server myCallbackProcedure 12345
proc myCallbackProcedure {channel clientHost clientPort} {
puts "Connection from $clientHost"
puts $channel "Hi there!"
flush $channel
close $channel
}
vwait forever
# The “forever” is an idiom; it's just a variable that isn't used elsewhere
# and so is never set, and it indicates that we're going to run the process
# until we kill it manually.
A somewhat more complicated example with asynchronous connection handling so we can serve multiple connections at once (CPU needed: minimal):
socket -server myCallbackProcedure 12345
proc myCallbackProcedure {channel clientHost clientPort} {
puts "Connection from $clientHost"
fileevent $channel readable [list incoming $channel $clientHost]
fconfigure $channel -blocking 0 -buffering line
puts $channel "Hi there!"
}
proc incoming {channel host timeout} {
if {[gets $channel line] >= 0} {
puts $channel "You said '$line'"
} elseif {[eof $channel]} {
puts "$host has gone"
close $channel
}
}
vwait forever
An even more complicated example that will close connections 10 seconds (= 10000ms) after the last message on them:
socket -server myCallbackProcedure 12345
proc myCallbackProcedure {channel clientHost clientPort} {
global timeouts
puts "Connection from $clientHost"
set timeouts($channel) [after 10000 [list timeout $channel $clientHost]]
fileevent $channel readable [list incoming $channel $clientHost]
fconfigure $channel -blocking 0 -buffering line
puts $channel "Hi there!"
}
proc incoming {channel host timeout} {
global timeouts
after cancel $timeouts($channel)
if {[gets $channel line] >= 0} {
puts $channel "You said '$line'"
} elseif {[eof $channel]} {
puts "$host has gone"
close $channel
unset timeouts($channel)
return
}
# Reset the timeout
set timeouts($channel) [after 10000 [list timeout $channel $host]]
}
proc timeout {channel host} {
global timeouts
puts "Timeout for $host, closing anyway..."
close $channel
unset -nocomplain timeouts($channel)
}
vwait forever
I have an asynchronous socket and call to connect() + GetLastError() which returns WSA_WOULD_BLOCK, as expected. So I start "receiving/reading" thread and subscribe Event to FD_READ and FD_CLOSE.
The story is: connect will sequentially fail, since Server is not up and running. My understanding that my receiving thread should get FD_CLOSE soon and I need to follow-up with cleaning.
It does not happen. How soon should I receive FD_CLOSE? Is it proper approach? Is there any other way to understand that connect() failed? Shoul I ever receive FD_CLOSE if socket isn't connected?
I do start my receiving thread and subscribe event after successful call to DoConnect() and I am afraid that racing condition prevents me from getting FD_CLOSE.
Here is some code:
int RecvSocketThread::WaitForData()
{
int retVal = 0
while (!retVal)
{
// sockets to pool can be added on other threads.
// please validate that all of them in the pool are connected
// before doing any reading on them
retVal = DoWaitForData();
}
}
int RecvSocketThread::DoWaitForData()
{
// before waiting for incoming data, check if all sockets are connected
WaitForPendingConnection_DoForAllSocketsInThePool();
// other routine to read (FD_READ) or react to FD_CLOSE
// create array of event (each per socket) and wait
}
void RecvSocketThread::WaitForPendingConnection_DoForAllSocketsInThePool()
{
// create array and set it for events associated with pending connect sockets
HANDLE* EventArray = NULL;
int counter = 0;
EventArray = new HANDLE[m_RecvSocketInfoPool.size()];
// add those event whose associated socket is still not connected
// and wait for FD_WRITE and FD_CLOSE. At the end of this function
// don't forget to switch them to FD_READ and FD_CLOSE
while (it != m_RecvSocketInfoPool.end())
{
RecvSocketInfo* recvSocketInfo = it->second;
if (!IsEventSet(recvSocketInfo->m_Connected, &retVal2))
{
::WSAEventSelect(recvSocketInfo->m_WorkerSocket, recvSocketInfo->m_Event, FD_WRITE | FD_CLOSE);
EventArray[counter++] = recvSocketInfo->m_Event;
}
++it;
}
if (counter)
{
DWORD indexSignaled = WaitForMultipleObjects(counter, EventArray, WaitAtLeastOneEvent, INFINITE);
// no matter what is further Wait doen't return for failed to connect socket
if (WAIT_OBJECT_0 <= indexSignaled &&
indexSignaled < (WAIT_OBJECT_0 + counter))
{
it = m_RecvSocketInfoPool.begin();
while (it != m_RecvSocketInfoPool.end())
{
RecvSocketInfo* recvSocketInfo = it->second;
if (IsEventSet(recvSocketInfo->m_Event, NULL))
{
rc = WSAEnumNetworkEvents(recvSocketInfo->m_WorkerSocket,
recvSocketInfo->m_Event, &networkEvents);
// Check recvSocketInfo->m_Event using WSAEnumnetworkevents
// for FD_CLOSE using FD_CLOSE_BIT
if ((networkEvents.lNetworkEvents & FD_CLOSE))
{
recvSocketInfo->m_FD_CLOSE_Recieved = 1;
*retVal = networkEvents.iErrorCode[FD_CLOSE_BIT];
}
if ((networkEvents.lNetworkEvents & FD_WRITE))
{
WSASetEvent(recvSocketInfo->m_Connected);
*retVal = networkEvents.iErrorCode[FD_WRITE_BIT];
}
}
++it;
}
}
// if error - DoClean, if FD_WRITE (socket is writable) check if m_Connected
// before do any sending
}
}
You will not receive an FD_CLOSE notification if connect() fails. You must subscribe to FD_CONNECT to detect that. This is clearly stated in the connect() documentation:
With a nonblocking socket, the connection attempt cannot be completed
immediately. In this case, connect will return SOCKET_ERROR, and
WSAGetLastError will return WSAEWOULDBLOCK. In this case, there are
three possible scenarios:
•Use the select function to determine the completion of the
connection request by checking to see if the socket is writeable.
•If the application is using WSAAsyncSelect to indicate interest in
connection events, then the application will receive an FD_CONNECT
notification indicating that the connect operation is complete
(successfully or not).
•If the application is using WSAEventSelect to indicate interest in
connection events, then the associated event object will be signaled
indicating that the connect operation is complete (successfully or
not).
The result code of connect() will be in the event's HIWORD(lParam) value when LOWORD(lParam) is FD_CONNECT. If the result code is 0, connect() was successful, otherwise it will be a WinSock error code.
If you call connect() and get a blocking notification you have to write more code to monitor for connect() completion (success or failure) via one of three methods as described here.
With a nonblocking socket, the connection attempt cannot be completed
immediately. In this case, connect will return SOCKET_ERROR, and
WSAGetLastError will return WSAEWOULDBLOCK. In this case, there are
three possible scenarios:
•Use the select function to determine the completion of the connection
request by checking to see if the socket is writeable.
•If the
application is using WSAAsyncSelect to indicate interest in connection
events, then the application will receive an FD_CONNECT notification
indicating that the connect operation is complete (successfully or
not).
•If the application is using WSAEventSelect to indicate interest
in connection events, then the associated event object will be
signaled indicating that the connect operation is complete
(successfully or not).
I think I need to start Receving thread once socket handle is created, but before connect is called. It is too late to create it after connect was called on asynchronous socket.
For synchronous socket those two calls createsocket() and connect() was just two consequitive lines. Does not work for non-blocking.
In this case at the beginning of receiving thread I need to check for FD_CONNECT and/or FD_WRITE in order be informed of connect attempt status.