Server Sent Events using Spring WebFlux and EventSource - server-sent-events

I am working on Server Sent Events.
Reference Link: http://sinhamohit.com/writing/spring-boot-reactive-sse
The above example includes SSE with Spring Boot and WebFlux.
Is there any example available with Spring WebFlux and HTML5 EventSource?

Create simple project with WebFlux.
Below is controller method for server sent event:
#GetMapping(value = "/notifyonEvent", produces =
MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getData() {
Random r = new Random();
int low = 0;
int high = 50;
return Flux.fromStream(Stream.generate(() -> r.nextInt(high - low) + low)
.map(s -> String.valueOf(s))
.peek((msg) -> {
LOGGER.info(msg);
}))
.map(s -> s)
.delayElements(Duration.ofSeconds(1));
}
Client side
var source = new EventSource("YOURAPP_URL/notifyonEvent");
source.onmessage = function(event) {
console.log(event.data);
};

Related

Method in event handlers are not called in SignalR after some time

On starting, the application with SignalR 2 implementation works as expected. But after sometime (it can be after 5 minutes or 30 minutes or 10 minutes), the events are not called anymore. But the Server and Client are still connected. I know it is connected because it the trace level of the client for SignalR, it can still get the commands coming from the Server.
But my messages from my client is still being received by the server and its action executed so I know that the Server - Client connection is still good.
Also, I know that it is still connected because I always check the status of the connection.
This is how I implement the SignalR
public void ConnectAsync()
{
WriteToLogs("Connecting to Server...");
var dic = new Dictionary<string, string> { { "UserName", _signalrSettings.ApplicationName } };
_hubConnection?.Dispose();
_hubConnection = new HubConnection(_signalrSettings.ServerUri, dic);
_hubConnection.StateChanged += Connection_StateChanged;
_hubProxy = _hubConnection.CreateHubProxy(_signalrSettings.HubProxy);
try
{
_hubProxy.On<string, string>("Initial", (a, b) => Initial());
_hubConnection.Start().ContinueWith((s) =>
{
if (s.IsCompleted)
{
_isCompleted = true;
}
else if (s.IsCanceled)
{
_isCompleted = false;
}
else if (s.IsFaulted)
{
_isCompleted = false;
}
_hubProxy.On<string, string>("NewMessage", (userName, message) => NewMessage(userName, message)); // THE "NewMessage" is not fired after some time.
WriteToLogs( $#"Connection status is {_hubConnection.State}");
}).Wait();
}
catch (Exception e)
{
var trace = new StackTrace(e, true);
var reflectedType = trace.GetFrame(0).GetMethod().ReflectedType;
if (reflectedType != null)
{
_messageOperation.WriteErrors($#"Error {e.StackTrace} at {trace}");
}
}
}

How can I read byte array coming from server in UWP app?

I have a client-server app. The client is HoloLens 2 and the server is unity running on PC. I have created a socket using HoloLens but I can't read the byte array on HoloLens. In the connect method _inputStream and _outputStream are System.IO.Streams.
PC client uses System.Net.Sockets.TcpClient, System.Net.Sockets.NetworkStream but UWP (HoloLens) uses Windows.Networking.Sockets.StreamSocket.
In the ReceiveCallback I have an issue with int byteLenght = _inputStream.EndRead(result); When I tried this code on HoloLens I got the exception: "System.ArgumentException: The specified AsyncResult does not correspond to any outstanding IO operation". But I tested it with PC client (System.Net.Socket) and everything works fine.
Btw, I gave all related permissions.
How can I read byte array in UWP app?
Update:
#if UNITY_EDITOR
private void ConnectUWP(string host, string port)
#else
private async void ConnectUWP(string host, string port)
#endif
{
#if UNITY_EDITOR
errorStatus = "UWP TCP client used in Unity!";
#else
try
{
socket = new Windows.Networking.Sockets.StreamSocket();
Windows.Networking.HostName serverHost = new Windows.Networking.HostName(host);
_receivedData = new Packet();
_receiveBuffer = new byte[DataBufferSize];
await socket.ConnectAsync(serverHost, port);
successStatus = "Connected!";
_outputStream = socket.OutputStream.AsStreamForWrite();
//_streamWriter = new StreamWriter(_outputStream) { AutoFlush = true };
_inputStream = socket.InputStream.AsStreamForRead();
Task.Run(() => StreamToBytes(socket.InputStream));
}
catch (Exception e)
{
errorStatus = e.ToString();
Debugger.GetComponent<TextMeshPro>().text += "\n Exception: " + errorStatus;
}
#endif
#if !UNITY_EDITOR
public async Task StreamToBytes(Windows.Storage.Streams.IInputStream stream)
{
using (var reader1 = new Windows.Storage.Streams.DataReader(stream))
{
reader1.InputStreamOptions = Windows.Storage.Streams.InputStreamOptions.ReadAhead;
reader1.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
reader1.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;
//InBuffer is always 256,
//even if there is more data waiting. If I put a task.delay in it will always return 25
try
{
await reader1.LoadAsync(256);
}
catch (Exception ex)
{
//..
}
while (reader1.UnconsumedBufferLength > 0)
{
var bytes1 = new byte[reader1.UnconsumedBufferLength];
reader1.ReadBytes(bytes1);
_receivedData.Reset(await HandleData(bytes1));
await reader1.LoadAsync(256);
}
reader1.DetachStream();
}
}
private async Task<bool> HandleData(byte[] data)
{
int packetLength = 0;
_receivedData.SetBytes(data);
if (_receivedData.UnreadLength() >= 4)
{
// If client's received data contains a packet
packetLength = _receivedData.ReadInt();
if (packetLength <= 0)
{
// If packet contains no data
return true; // Reset receivedData instance to allow it to be reused
}
}
while (packetLength > 0 && packetLength <= _receivedData.UnreadLength())
{
// While packet contains data AND packet data length doesn't exceed the length of the packet we're reading
byte[] packetBytes = _receivedData.ReadBytes(packetLength);
ThreadManager.ExecuteOnMainThread(() =>
{
using (Packet packet = new Packet(packetBytes))
{
int packetId = packet.ReadInt();
_packetHandlers[packetId](packet); // Call appropriate method to handle the packet
}
});
packetLength = 0; // Reset packet length
if (_receivedData.UnreadLength() >= 4)
{
// If client's received data contains another packet
packetLength = _receivedData.ReadInt();
if (packetLength <= 0)
{
// If packet contains no data
return true; // Reset receivedData instance to allow it to be reused
}
}
}
if (packetLength <= 1)
{
return true; // Reset receivedData instance to allow it to be reused
}
return false;
}
#endif
When I tried this code on HoloLens I got the exception: "System.ArgumentException: The specified AsyncResult does not correspond to any outstanding IO operation".
Please refer to BeginRead method document, In the .NET Framework 4 and earlier versions, you have to use methods such as BeginRead and EndRead to implement asynchronous I/O operations. These methods are still available in the .NET Framework 4.5 to support legacy code; however, the new async methods, such as ReadAsync, WriteAsync, CopyToAsync, and FlushAsync, help you implement asynchronous I/O operations more easily. I suppose this old api could not compatible with HoloLens device, so please try to use ReadAsync to replace like the following
byte[] bytes = await StreamToBytes(_inputStream);
public async Task<byte[]> StreamToBytes(Stream stream)
{
byte[] bytes = new byte[stream.Length];
await stream.ReadAsync(bytes, 0, bytes.Length);
stream.Seek(0, SeekOrigin.Begin);
return bytes;
}
Or follow the official's processing that use DataReader to read ReadBytes, for more code please refer streamsocket.
Update
using (var reader1 = new DataReader(_inputStream)
{
reader1.InputStreamOptions = Windows.Storage.Streams.InputStreamOptions.Partial;
reader1.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
reader1.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;
await reader1.LoadAsync(256); //InBuffer is always 256,
//even if there is more data waiting. If I put a task.delay in it will always return 25
while (reader1.UnconsumedBufferLength > 0)
{
var bytes1 = new byte[reader1.UnconsumedBufferLength];
reader1.ReadBytes(bytes1);
await reader1.LoadAsync(256);
}
reader1.DetachStream();
}
I tried everything for TCP but TCP is nearly impossible for HoloLens with UWP app. So I tried UDP and it works perfectly (https://github.com/mbaytas/HoloLensUDP). I hope Microsoft put a TCP example for HoloLens 1 and 2 in near future. Thanks Nico for your help.

set alarm in Xamarin forms and set the next time for it from sql lite table using Broadcast receiver without using AlarmManager.SetRepeating

I want to set the alarm on different times but want to set the next by getting the time from sqllite table by using Broadcast receiver.
I've also created dependency service
Here's what I have tried
public void LocalNotification(string title, string body, int id, DateTime notifyTime)
{
var intent = CreateIntent(id);
Random generator = new Random();
_randomNumber = generator.Next(100000, 999999).ToString("D6");
var pendingIntent = PendingIntent.GetBroadcast(Application.Context, Convert.ToInt32(_randomNumber), intent, PendingIntentFlags.Immutable);
var alarmManager = GetAlarmManager();
var todaysdate = notifyTime;
var _UpcomingSalah = new NextSalahTimeRemaining().getNextSalahTime(todaysdate);
var ms = (long)(_UpcomingSalah.SalahTime).TotalMilliseconds;
alarmManager.Set(AlarmType.ElapsedRealtimeWakeup, Android.OS.SystemClock.ElapsedRealtime() +ms, pendingIntent);
}
BroadcastReciever's 'On recieve func'
public override async void OnReceive(Context context, Intent intent)
{
var sound = Android.Net.Uri.Parse(ContentResolver.SchemeAndroidResource + "://" + context.PackageName + "/" + Resource.Raw.azan);
Ringtone r = RingtoneManager.GetRingtone(context, sound);
r.Play();
await TextToSpeech.SpeakAsync("It's Time for salah");
Xamarin.Forms.DependencyService.Get<ILocalNotificationService>().LocalNotification("Local Notification", "sdfasdf", 0, System.DateTime.Now.AddMinutes(1));
}
I'm calling the dependecy service for next time form sqllite table, the alarm works once but stops instantly after that.

SignalR .NET client: subsequent method Invoke with same connection opened

I have a .NET client app, connected to a SignalR Hub hosted with a .net mvc5 webapp. The main routine of the client app does:
int num1 = _myService.GetSomeHubMethod();
int num2 = _myService.GetSomeOtherHubMethod();
...
where _myService is an instance of the class:
public class MyService
{
...
private HubConnection connection;
private IHubProxy hubProxy;
...
public MyService()
{
...
if (BASE_SITE_URL == null)
BASE_SITE_URL = ConfigurationManager.AppSettings["BASE_SITE_URL"].ToString();
if (connection == null)
connection = new HubConnection(BASE_SITE_URL);
if (hubProxy == null)
hubProxy = connection.CreateHubProxy("MyHub");
connection.Start().Wait();
}
...
public int GetSomeHubMethod()
{
//connection.Start().Wait();
var t = hubProxy.Invoke<int>("SomeHubMethod");
int result = t.Result;
//connection.Stop();
return result;
}
public int GetSomeOtherHubMethod()
{
//connection.Start().Wait();
var t = hubProxy.Invoke<int>("SomeOtherHubMethod");
int result = t.Result;
//connection.Stop();
return result;
}
}
and the two hub methods, in the SignalR hub (server side) are:
public class MyHub : Hub
{
...
public int SomeHubMethod()
{ return 1; }
public int SomeOtherHubMethod()
{ return 2; }
}
The problem is: the first call is correctly evaluated, but the second call "hangs" in the
int result = t.Result;
line. In particular, t.Status is "WaitingForActivation".
If I switch the order of the two calls in the main routine, the first is executed and the seconds "hangs".
Note: if I start and stop the connection inside the two methods (see commented lines) instead of calling connection.Start().Wait() in the constructor of MyService, it works perfectly, but it needs too much time to stop and start.
Thanks everybody for help!
Ok, it seems it's a deadlock. I had to modify methods this way:
public async Task<int> SomeHubMethod()
{
return await Task.FromResult<int>(1);
}
...
public async Task<int> GetSomeHubMethod()
{
return await chatHubProxy.Invoke<int>("SomeHubMethod");
}
...
int num1 = await _myService.GetSomeHubMethod();
Ok, I confess I still have much work to do with async stuff...

Older asynchronous messages overwriting newer ones

We are developing a document collaboration tool in SignalR where multiple users can update one single WYSIWYG form.
We are struggling getting the app to work using the KeyUp method to send the changes back to the server. This causes the system to overwrite what the user wrote after his first key stroke when it sends the message back.
Is there anyway to work around this problem?
For the moment I tried to set up a 2 seconds timeout but this delays all updates not only the "writer" page.
public class ChatHub : Hub
{
public ChatHub()
{
}
public void Send(int id,string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(id,message); //id is for the document id where to update the content
}
}
and the client:
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
//console.log("Declare a proxy to reference the hub.");
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (id, message) {
var encodedValue = $('<div />').text(id).html();
// Add the message to the page.
if (encodedValue == $('#hdnDocId').val()) {
$('#DiaplayMsg').text(message);
tinyMCE.activeEditor.setContent("");
tinyMCE.get('txtContent').execCommand('insertHTML', false, message); //!!!
}
};
// Start the connection.
$.connection.hub.start().done(function (e) {
//console.log("Start the connection.");
if ($('#hdnDocId').val() != '') {
tinyMCE.activeEditor.onKeyUp.add(function (ed, e) {
var elelist = $(tinyMCE.activeEditor.getBody()).text();
var content = tinyMCE.get('txtContent').getContent();
function Chat() {
var content = tinyMCE.get('txtContent').getContent();
chat.server.send($('#hdnDocId').val(), content); //send a push to server
}
typewatch(Chat, 2000);
});
}
});
});
var typewatch = function () {
var timer = 0;
return function (Chat, ms) {
clearTimeout(timer);
timer = setTimeout(Chat, ms);
}
} ();
</script>
Hello, here is an update of the client KeyUp code. It seems to be working but I would like your opinion. I've used a global variable to store the timeout, see below:
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
//console.log("Declare a proxy to reference the hub.");
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (id, message) {
var encodedValue = $('<div />').text(id).html();
var currenttime = new Date().getTime() / 1000 - 2
if (typeof window.istyping == 'undefined') {
window.istyping = 0;
}
if (encodedValue == $('#hdnDocId').val() && window.istyping == 0 && window.istyping < currenttime) {
function Update() {
$('#DiaplayMsg').text(message);
tinyMCE.activeEditor.setContent("");
tinyMCE.get('txtContent').execCommand('insertHTML', false, message); //!!!
// tinyMCE.get('txtContent').setContent(message);
window.istyping = 0
}
Update();
}
};
// Start the connection.
$.connection.hub.start().done(function (e) {
//console.log("Start the connection.");
if ($('#hdnDocId').val() != '') {
tinyMCE.activeEditor.onKeyUp.add(function (ed, e) {
var elelist = $(tinyMCE.activeEditor.getBody()).text();
var content = tinyMCE.get('txtContent').getContent();
function Chat() {
//alert("Call");
var content = tinyMCE.get('txtContent').getContent();
chat.server.send($('#hdnDocId').val(), content);
window.istyping = new Date().getTime() / 1000;
}
Chat();
});
}
});
});
var typewatch = function () {
var timer = 0;
return function (Chat, ms) {
clearTimeout(timer);
timer = setTimeout(Chat, ms);
}
} ();
Thanks,
Roberto.
Is there anyway to work around this problem?
Yes, by not sending the entire document to the server, but document elements like paragraphs, table cells, and so on. You can synchronize these after the user has stopped typing for a period, or when focus is lost for example.
Otherwise add some incrementing counter to the messages, so older return values don't overwrite newer ones arriving earlier.
But you're basically asking us to solve a non-trivial problem regarding collaborated document editing. What have you tried?
"This causes the system to overwrite what the user wrote"
that's because this code isn't making any effort to merge changes. it is just blindly overwriting whatever is there.
tinyMCE.activeEditor.setContent("");
tinyMCE.get('txtContent').execCommand('insertHTML', false, message);
as #CodeCaster hinted, you need to be more precise in the messages you send - pass specific changes back and forth rather re-sending the entire document - so that changes can be carefully merged on the receiving side

Resources