I'm new to node and to feathersjs, and for my first app, I'm trying to have different parts of it communicate using channels. I understand the operations and how they're used, but I don't understand how to establish a connection to a channel in the first place.
For example, here's some code from the official documentation:
app.on('login', (payload, { connection }) => {
if(connection && connection.user.isAdmin) {
// Join the admins channel
app.channel('admins').join(connection);
// Calling a second time will do nothing
app.channel('admins').join(connection);
}
});
Where does "connection" come from? There is no built-in function (unless I'm missing something obvious) in feathersjs to do this.
Thanks!
Channel is used in feathers to achieve real time.
In the server you need to configure socketio. Then it also requires the client to be connected to the server via socketio.
Where does "connection" come from?
connection is a js object that represents the connection the user has established by logging in.
Try doing a console.log(connection) to see what it contains.
connection is in this case passed by the Feathers framework in the function call to the function that you have quoted.
Once you have obtained this connection object then you can use it for adding the user to a channel, and many other things.
Related
The Socket.io documentation seems to specify a few ways to emit an event to all connected clients in a room. They are as follows:
io.to(), as found in the first example here: https://socket.io/docs/server-api/#socket-join-room-callback
io.in(), as found in the emit cheatsheet, found here: https://socket.io/docs/emit-cheatsheet/
socket.to(), as found here: https://socket.io/docs/server-api/#socket-to-room
Other than the examples linked above, both io.to() and io.in() are not listed anywhere else in the documentation. What do these methods do exactly, and where can I find more information on them?
socket.to() can be used inside the io.on('connection', callback) event, like so:
io.on('connection', function(socket){
// to one room
socket.to('others').emit('an event', { some: 'data' });
// to multiple rooms
socket.to('room1').to('room2').emit('hello');
});
However, this does not make sense, as the socket object passed into this callback represents a connected client. How can the incoming socket object be used to broadcast to all other connected sockets, as shown in the above example?
Definitive explanations of the above are appreciated.
However, this does not make sense, as the socket object passed into this callback represents a connected client.
If you trace into those call in a debugger, you can see what is going on.
First off, the socket.to() creates a property on the socket named _rooms that is an array of room names. You can see the whole code in context here in the Github repository, but here's the relevant portion for .to():
Socket.prototype.to =
Socket.prototype.in = function(name){
if (!~this._rooms.indexOf(name)) this._rooms.push(name);
return this;
};
Each successive call to .to() just an addition room to the array.
Then, socket.emit() checks to see if the _rooms property exists and if it does, it calls this.adapter.broadcast(...) which grabs the adapter and tells it to broadcast this message to all sockets on that adapter except the current one. The whole code for socket.emit() is here on Github. The particular broadcast part of the code is this:
if (this._rooms.length || this.flags.broadcast) {
this.adapter.broadcast(packet, {
except: [this.id],
rooms: this._rooms,
flags: this.flags
});
} else {
// dispatch packet
this.packet(packet, this.flags);
}
How can the incoming socket object be used to broadcast to all other connected sockets, as shown in the above example?
Each socket contains a reference to the adapter and the adapter has a list of all sockets on that adapter. So, it's possible to get form the socket to the adapter, to all the other sockets.
I would agree that this is a bit of an odd overloading of functionality, but that's how they do it. I'm guessing they wanted to give people access to broadcast functionality when all you had a reference to was an individual socket.
FYI, the only way to really answer these types of questions yourself that are not documented is by looking at the code and that is certainly one of the huge advantages of using open source libraries. I find that the quickest way to get to the right source is to step into the method of interest in the debugger. Fire up the debugger, set a breakpoint in your code, then step into the function of choice and it will show you the relevant source code immediately. You can then further step through that function if you want to see what path it is taking.
For anyone coming across this question like me, here is the link to the docs for explanation:
https://socket.io/docs/v3/rooms/index.html
Is it possible to broadcast or emit data over to browser and catch it with javascript to treat it accordingly, using ReactPHP without Ratchet?
I have a periodicTimer on event-loop and sometimes it needs to broadcast or emit to users connected by a socket. I saw there was a class socket and in the examples on the React github project page it was instantiated but not used in web-flow, only on netcat. Is it possible to make it work natively on browsers like socket.IO or Ratchet?
The factory and connection works fine, I just don't know how to bind socket to send/receive data to the server with event-loop.
Broadcasting this is not super-special function.
First of all, you need save all user connection in global scope.
For example you have a class TestServer, it can contains attribute connections (type array). In method run you write loop.
For example this your connection event
$server->on('connection', function(ConnectionInterface $conn) {
$this->connections[] = $conn;
});
And write method
public function broadcast($message) {
foreach($this->connections as $conn) {
$conn->write($message);
}
}
Attention!
You must write code to delete disconnecting if user disconnect, and if you need, you can write logic about topics.
I am experimenting with ZeroMQ. And I found it really interesting that in ZeroMQ, it does not matter whether either connect or bind happens first. I tried looking into the source code of ZeroMQ but it was too big to find anything.
The code is as follows.
# client side
import zmq
ctx = zmq.Context()
socket = ctx.socket(zmq.PAIR)
socket.connect('tcp://*:2345') # line [1]
# make it wait here
# server side
import zmq
ctx = zmq.Context()
socket = ctx.socket(zmq.PAIR)
socket.bind('tcp://localhost:2345')
# make it wait here
If I start client side first, the server has not been started yet, but magically the code is not blocked at line [1]. At this point, I checked with ss and made sure that the client is not listening on any port. Nor does it have any open connection. Then I start the server. Now the server is listening on port 2345, and magically the client is connected to it. My question is how does the client know the server is now online?
The best place to ask your question is the ZMQ mailing list, as many of the developers (and founders!) of the library are active there and can answer your question directly, but I'll give it a try. I'll admit that I'm not a C developer so my understanding of the source is limited, but here's what I gather, mostly from src/tcp_connector.cpp (other transports are covered in their respective files and may behave differently).
Line 214 starts the open() method, and here looks to be the meat of what's going on.
To answer your question about why the code is not blocked at Line [1], see line 258. It's specifically calling a method to make the socket behave asynchronously (for specifics on how unblock_socket() works you'll have to talk to someone more versed in C, it's defined here).
On line 278, it attempts to make the connection to the remote peer. If it's successful immediately, you're good, the bound socket was there and we've connected. If it wasn't, on line 294 it sets the error code to EINPROGRESS and fails.
To see what happens then, we go back to the start_connecting() method on line 161. This is where the open() method is called from, and where the EINPROGRESS error is used. My best understanding of what's happening here is that if at first it does not succeed, it tries again, asynchronously, until it finds its peer.
I think the best answer is in zeromq wiki
When should I use bind and when connect?
As a very general advice: use bind on the most stable points in your architecture and connect from the more volatile endpoints. For request/reply the service provider might be point where you bind and the client uses connect. Like plain old TCP.
If you can't figure out which parts are more stable (i.e. peer-to-peer) think about a stable device in the middle, where boths sides can connect to.
The question of bind or connect is often overemphasized. It's really just a matter of what the endpoints do and if they live long — or not. And this depends on your architecture. So build your architecture to fit your problem, not to fit the tool.
And
Why do I see different behavior when I bind a socket versus connect a socket?
ZeroMQ creates queues per underlying connection, e.g. if your socket is connected to 3 peer sockets there are 3 messages queues.
With bind, you allow peers to connect to you, thus you don't know how many peers there will be in the future and you cannot create the queues in advance. Instead, queues are created as individual peers connect to the bound socket.
With connect, ZeroMQ knows that there's going to be at least a single peer and thus it can create a single queue immediately. This applies to all socket types except ROUTER, where queues are only created after the peer we connect to has acknowledge our connection.
Consequently, when sending a message to bound socket with no peers, or a ROUTER with no live connections, there's no queue to store the message to.
When you call socket.connect('tcp://*:2345') or socket.bind('tcp://localhost:2345') you are not calling these methods directly on an underlying TCP socket. All of ZMQ's IO - including connecting/binding underlying TCP sockets - happens in threads that are abstracted away from the user.
When these methods are called on a ZMQ socket it essentially queues these events within the IO threads. Once the IO threads begin to process them they will not return an error unless the event is truly impossible, otherwise they will continually attempt to connect/reconnect.
This means that a ZMQ socket may return without an error even if socket.connect is not successful. In your example it would likely fail without error but then quickly reattempt and succeeded if you were to run the server side of script.
It may also allow you to send messages while in this state (depending on the state of the queue in this situation, rather than the state of the network) and will then attempt to transmit queued messages once the IO threads are able to successfully connect. This also includes if a working TCP connection is later lost. The queues may continue to accept messages for the unconnected socket while IO attempts to automatically resolve the lost connection in the background. If the endpoint takes a while to come back online it should still receive it's messages.
To better explain here's another example
<?php
$pid = pcntl_fork();
if($pid)
{
$context = new ZMQContext();
$client = new ZMQSocket($context, ZMQ::SOCKET_REQ);
try
{
$client->connect("tcp://0.0.0.0:9000");
}catch (ZMQSocketException $e)
{
var_dump($e);
}
$client->send("request");
$msg = $client->recv();
var_dump($msg);
}else
{
// in spawned process
echo "waiting 2 seconds\n";
sleep(2);
$context = new ZMQContext();
$server = new ZMQSocket($context, ZMQ::SOCKET_REP);
try
{
$server->bind("tcp://0.0.0.0:9000");
}catch (ZMQSocketException $e)
{
var_dump($e);
}
$msg = $server->recv();
$server->send("response");
var_dump($msg);
}
The binding process will not begin until 2 seconds later than the connecting process. But once the child process wakes and successfully binds the req/rep transaction will successfully take place without error.
jason#jason-VirtualBox:~/php-dev$ php play.php
waiting 2 seconds
string(7) "request"
string(8) "response"
If I was to replace tcp://0.0.0.0:9000 on the binding socket with tcp://0.0.0.0:2345 it will hang because the client is trying to connect to tcp://0.0.0.0:9000, yet still without error.
But if I replace both with tcp://localhost:2345 I get an error on my system because it can't bind on localhost making the call truly impossible.
object(ZMQSocketException)#3 (7) {
["message":protected]=>
string(38) "Failed to bind the ZMQ: No such device"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(19)
["file":protected]=>
string(28) "/home/jason/php-dev/play.php"
["line":protected]=>
int(40)
["trace":"Exception":private]=>
array(1) {
[0]=>
array(6) {
["file"]=>
string(28) "/home/jason/php-dev/play.php"
["line"]=>
int(40)
["function"]=>
string(4) "bind"
["class"]=>
string(9) "ZMQSocket"
["type"]=>
string(2) "->"
["args"]=>
array(1) {
[0]=>
string(20) "tcp://localhost:2345"
}
}
}
["previous":"Exception":private]=>
NULL
}
If your needing real-time information for the state of underlying sockets you should look into socket monitors. Using socket monitors along with the ZMQ poll allows you to poll for both socket events and queue events.
Keep in mind that polling a monitor socket using ZMQ poll is not similar to polling a ZMQ_FD resource via select, epoll, etc. The ZMQ_FD is edge triggered and therefor doesn't behave the way you would expect when polling network resources, where a monitor socket within ZMQ poll is level triggered. Also, monitor sockets are very light weight and latency between the system event and the resulting monitor event is typically sub microsecond.
Using shelf_auth I can extract current user information from request this way:
getAuthenticatedContext(request)
.map((ac) => ac.principal.name)
.getOrElse(() => 'guest')
but, obviously, I need a request for that to work :)
On the other hand, using shelf_web_socket, establishing of websocket connection executes handler like that:
handleWS(CompatibleWebSocket ws){
// Here I should get user from getAuthenticatedContext()
// but I cannot due to absence of Request here.
ws.messages.listen(...);
};
rootRouter.get('/ws', webSocketHandler(handleWS), middleWare: authMiddleware);
But I have no idea how to forward original socket connection request to my handleWS, to be able to know which user just connected to server.
Another question is what is best practice to store these open sockets, to being able to send broadcast messages to all connected clients, and delete corresponding socket when it's closed.
First what is coming to my mind is to store sockets in a map like Map<int,CompatibleWebSocket>, where int is CompatibleWebSocket.hash().
Then I can iterate over Map.values() to do broadcast, and delete by key when connection is closed.
I can't get if that technique overcomplicated for such task and maybe exist more convenient way doing that, like storing them in a list? Or can I join Streams somehow for broadcasting?
I read netty proxy example, (https://github.com/netty/netty/tree/master/example/src/main/java/io/netty/example/proxy )
and I have two requirement.
I want to use fixed-count connection on proxy->server.
On proxy example, proxy->server conn. count equals client->proxy conn. count.
It may be too many.
When client->proxy connection ends, proxy->server connection has to be keep alived
And when new client->proxy connection established, reuse proxy->server connections.
How can it be implemented?
The first requirement can be realized rather easily by using a DefaultChannelGroup to store your channels. Assuming that the ChannelHandler which is accepting incoming connections is a singleton, then you can use the following code.
// initialize channelgroup in your singleton handler
ChannelGroup ALL_CONNECTIONS = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
...
#Override
public synchronized void channelActive(ChannelHandlerContext ctx) throws Exception
{
if(ALL_CONNECTIONS.size() > 100){
ctx.channel().close();// dont accept further connections
}else{
ALL_CONNECTIONS.add(ctx.channel());
// do whatever logic.
}
}
I think you are thinking of "connection pooling" for the second requirement. If so, its not a great idea I think. Since, when a new client "connects" to your server, it is always a new connection since it is coming from outside of your network. However I am not sure of this and someone with more knowledge can answer.
Both what your need, i think, is a client with connection pool.
Both HttpComponents and AsyncHttpClient support pooling, You could have a look at the codes in AsyncHttpClient which also have a netty based implementation.