Possible to broadcast using ReactPHP? - websocket

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.

Related

How do establish a connection to a channel in feathersjs?

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.

Spring Boot Webflux/Netty - Detect closed connection

I've been working with spring-boot 2.0.0.RC1 using the webflux starter (spring-boot-starter-webflux). I created a simple controller that returns a infinite flux. I would like that the Publisher only does its work if there is a client (Subscriber). Let's say I have a controller like this one:
#RestController
public class Demo {
#GetMapping(value = "/")
public Flux<String> getEvents(){
return Flux.create((FluxSink<String> sink) -> {
while(!sink.isCancelled()){
// TODO e.g. fetch data from somewhere
sink.next("DATA");
}
sink.complete();
}).doFinally(signal -> System.out.println("END"));
}
}
Now, when I try to run that code and access the endpoint http://localhost:8080/ with Chrome, then I can see the data. However, once I close the browser the while-loop continues since no cancel event has been fired. How can I terminate/cancel the streaming as soon as I close the browser?
From this answer I quote that:
Currently with HTTP, the exact backpressure information is not
transmitted over the network, since the HTTP protocol doesn't support
this. This can change if we use a different wire protocol.
I assume that, since backpressure is not supported by the HTTP protocol, it means that no cancel request will be made either.
Investigating a little bit further, by analyzing the network traffic, showed that the browser sends a TCP FIN as soon as I close the browser. Is there a way to configure Netty (or something else) so that a half-closed connection will trigger a cancel event on the publisher, making the while-loop stop?
Or do I have to write my own adapter similar to org.springframework.http.server.reactive.ServletHttpHandlerAdapter where I implement my own Subscriber?
Thanks for any help.
EDIT:
An IOException will be raised on the attempt to write data to the socket if there is no client. As you can see in the stack trace.
But that's not good enough, since it might take a while before the next chunk of data will be ready to send and therefore it takes the same amount of time to detect the gone client. As pointed out in Brian Clozel's answer it is a known issue in Reactor Netty. I tried to use Tomcat instead by adding the dependency to the POM.xml. Like this:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
Although it replaces Netty and uses Tomcat instead, it does not seem reactive due to the fact that the browser does not show any data. However, there is no warning/info/exception in the console. Is spring-boot-starter-webflux as of this version (2.0.0.RC1) supposed to work together with Tomcat?
Since this is a known issue (see Brian Clozel's answer), I ended up using one Flux to fetch my real data and having another one in order to implement some sort of ping/heartbeat mechanism. As a result, I merge both together with Flux.merge().
Here you can see a simplified version of my solution:
#RestController
public class Demo {
public interface Notification{}
public static class MyData implements Notification{
…
public boolean isEmpty(){…}
}
#GetMapping(value = "/", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<? extends Notification>> getNotificationStream() {
return Flux.merge(getEventMessageStream(), getHeartbeatStream());
}
private Flux<ServerSentEvent<Notification>> getHeartbeatStream() {
return Flux.interval(Duration.ofSeconds(2))
.map(i -> ServerSentEvent.<Notification>builder().event("ping").build())
.doFinally(signalType ->System.out.println("END"));
}
private Flux<ServerSentEvent<MyData>> getEventMessageStream() {
return Flux.interval(Duration.ofSeconds(30))
.map(i -> {
// TODO e.g. fetch data from somewhere,
// if there is no data return an empty object
return data;
})
.filter(data -> !data.isEmpty())
.map(data -> ServerSentEvent
.builder(data)
.event("message").build());
}
}
I wrap everything up as ServerSentEvent<? extends Notification>. Notification is just a marker interface. I use the event field from the ServerSentEvent class in order to separate between data and ping events. Since the heartbeat Flux sends events constantly and in short intervals, the time it takes to detect that the client is gone is at most the length of that interval. Remember, I need that because it might take a while before I get some real data that can be sent and, as a result, it might also take a while before it detects that the client is gone. Like this, it will detect that the client is gone as soon as it can’t sent the ping (or possibly the message event).
One last note on the marker interface, which I called Notification. This is not really necessary, but it gives some type safety. Without that, we could write Flux<ServerSentEvent<?>> instead of Flux<ServerSentEvent<? extends Notification>> as return type for the getNotificationStream() method. Or also possible, make getHeartbeatStream() return Flux<ServerSentEvent<MyData>>. However, like this it would allow that any object could be sent, which I don’t want. As a consequence, I added the interface.
I'm not sure why this behaves like this, but I suspect it is because of the choice of generation operator. I think using the following would work:
return Flux.interval(Duration.ofMillis(500))
.map(input -> {
return "DATA";
});
According to Reactor's reference documentation, you're probably hitting the key difference between generate and push (I believe a quite similar approach using generate would probably work as well).
My comment was referring to the backpressure information (how many elements a Subscriber is willing to accept), but the success/error information is communicated over the network.
Depending on your choice of web server (Reactor Netty, Tomcat, Jetty, etc), closing the client connection might result in:
a cancel signal being received on the server side (I think this is supported by Netty)
an error signal being received by the server when it's trying to write on a connection that's been closed (I believe the Servlet spec does not provide that that callback and we're missing the cancel information).
In short: you don't need to do anything special, it should be supported already, but your Flux implementation might be the actual problem here.
Update: this is a known issue in Reactor Netty

Differences between io.to(), io.in(), and socket.to() for emitting to all clients in a room

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

Authenticate websocket connection in Dart using shelf_auth and shelf_web_socket

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?

How to find the connection for a particular distributed objects method invocation?

I have a Cocoa client and server application that communicate using distributed objects implemented via NSSocketPorts and NSConnections in the standard way. The server vends a single object to the client applications, of which there may be several. Each client application can gain access to the same distributed object getting its own proxy.
The vended object supports a particular protocol, which includes a method similar to the following:
#protocol VendedObjectProtocol
- (void) acquireServerResource:(ServerResourceID)rsc;
#end
When a client application invokes this method, the server is supposed to allocate the requested resource to that client application. But there could be several clients that request the same resource, and the server needs to track which clients have requested it.
What I'd like to be able to do on the server-side is determine the NSConnection used by the client's method invocation. How can I do that?
One way I have thought about is this (server-side):
- (void) acquireServerResource:(ServerResourceID)rsc withDummyObject:(id)dummy {
NSConnection* conn = [dummy connectionForProxy];
// Map the connection to a particular client
// ...
}
However, I don't really want the client to have to pass through a dummy object for no real purpose (from the client's perspective). I could make the ServerResourceID be a class so that it gets passed through as a proxy, but I don't want to really do that either.
It seems to me that if I were doing the communications with raw sockets, I would be able to figure out which socket the message came in on and therefore be able to work out which client sent it without the client needing to send anything special as part of the message. I am needing a way to do this with a distributed objects method invocation.
Can anyone suggest a mechanism for doing this?
What you are looking for are NSConnection's delegate methods. For example:
- (BOOL)connection:(NSConnection *)parentConnection shouldMakeNewConnection:(NSConnection *)newConnnection {
// setup and map newConnnection to a particular client
id<VendedObjectProtocol> obj = //...
obj.connection = newConnection;
return YES;
}
You could design an object for each individual connection (like VendedObjectProtocol) and get the connection with self.connection.
- (void) acquireServerResource:(ServerResourceID)rsc {
NSConnection* conn = self.connection;
// Map the connection to a particular client
// ...
}
Or
You can make use of conversation tokens using +createConversationForConnection: and +currentConversation

Resources