I'm trying to learn websocket. I started sending simple string between peers and everything was fine. Now I'm trying to send Object to my javascript client but the onmessage function never fires. Here is the code:
Java serverside:
#ServerEndpoint(value = "/event/{id}",
encoders={PositionJSONEncoder.class},
decoders={PositionJSONDecoder.class}
)
public class SocketManager {
private static ConcurrentHashMap<String, Session> users = new ConcurrentHashMap<String, Session>();
#OnMessage
public void onMessage(Position position, #PathParam("id") String id, Session session) {
log.info("user "+id+", "+position.toString());
try {
for(Entry<String, Session> entry : users.entrySet()) {
if(!entry.getKey().equals(position.getUserID()) && entry.getValue().isOpen()) {
entry.getValue().getBasicRemote().sendObject(position);
}
}
} catch (EncodeException ee) {
log.error(ee);
} catch (IOException ioe) {
log.error(ioe);
}
}
}
The serverendpoint encoder (I'll omit the decoder, server handle data correctly):
public class PositionJSONEncoder implements Encoder.Text<Position>{
private Gson gson = new Gson();
public void destroy() {}
public void init(EndpointConfig arg0) {}
public String encode(Position arg0) throws EncodeException {
return gson.toJson(arg0);
}
}
The relevant client side (AngularJS):
app.factory('socket', function() {
var service = {};
service.ws = {};
service.connect = function(userID) {
this.ws = new WebSocket("ws://localhost:8080/event/"+userID);
};
service.disconnect = function() {
if(this.ws != undefined && this.ws != null) {
this.ws.onclose();
}
};
service.ws.onopen = function() {
// TODO
};
service.ws.onmessage = function(msg) {
try {
alert('roba: '+JSON.parse(msg.data));
} catch(err) {
alert(err.message);
}
};
service.ws.onclose = function() {
// TODO
};
service.ws.onerror = function(evt) {
alert(evt.data);
};
return service;
});
The model the server send:
public class Position {
private String userID;
private Float lat;
private Float lng;
public Position() {}
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public Float getLat() {
return lat;
}
public void setLat(Float lat) {
this.lat = lat;
}
public Float getLng() {
return lng;
}
public void setLng(Float lng) {
this.lng = lng;
}
#Override
public String toString() {
return userID+"["+"lat: "+lat+", "+"lng: "+lng+"]";
}
}
My pom's dependencies:
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<!-- GSON JSON serializer -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
The server gets the JSON object from the client correctly, but when it sends some Position object back the client, the onmessage function won't fire. I can see the Encoder is working as it returns strings like this:
{"userID":"bob","lat":2.0,"lng":2.0}
I see the websocket carry the messages:
but my javascript onmessage function always stay silent. I also implemented an onerror function but I can't get any feedback from it too. I'm using wildfly-8.0.0.Final.
Update: I implement a java websocket client. This client receive websocket frame sent by the server. Is my AngularJS client wrong?
I found what was wrong. In my javascript client I assigned a function to an undefined object. Here:
service.ws.onmessage = function(msg) {
try {
alert('roba: '+JSON.parse(msg.data));
} catch(err) {
alert(err.message);
}
};
service.ws.onmessage was undefined, that's why onmessage function never fire. I change my angular factory in this way:
app.factory('socket', function() {
var service = {};
service.ws = {};
service.connect = function(userID) {
this.ws = new WebSocket("ws://localhost:8080/event/"+userID);
this.ws.onmessage = function(msg) {
try {
alert('roba: '+JSON.parse(msg.data).lat+' :: '+JSON.parse(msg.data).lng);
} catch(err) {
alert(err.message);
}
};
this.ws.onerror = function(evt) {
alert('error: '+evt.data);
};
};
service.disconnect = function() {
if(this.ws != undefined && this.ws != null) {
this.ws.onclose();
}
};
return service;
});
Related
I'm confused with SignalR connection, the solution working well on my computer but when hosted on the Linux server it returns some errors, SLL installed on the server.
Please anyone can guide me what is missing :(
I'm using .Net core 3.1
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddHttpClient();
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 2;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Password.RequiredUniqueChars = 0;
options.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication();
services.AddAuthorization();
services.AddMvc();
services.AddScoped<ILoginService, LoginService>();
services.AddScoped<IAdminService, AdminService>();
services.AddScoped<ITradingService, TradingService>();
services.AddScoped<UserManager<ApplicationUser>, UserManager<ApplicationUser>>();
services.AddScoped<SignInManager<ApplicationUser>, SignInManager<ApplicationUser>>();
services.AddSignalR();
services.AddSingleton(s =>
{
using var scope = s.CreateScope();
var user = scope.ServiceProvider.GetRequiredService<IUserService>();
var config = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var hub = scope.ServiceProvider.GetRequiredService<IHubContext<MessagesHub>>();
return new RedisClient(hub, config, user);
});
services.AddTransient<IUserService, UserService>();
}
..
..
[System.Obsolete]
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApplicationLifetime applicationLifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
applicationLifetime.ApplicationStarted.Register(OnAppStarted);
applicationLifetime.ApplicationStopping.Register(OnAppStopping);
applicationLifetime.ApplicationStopped.Register(OnAppStopped);
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseLoggedUserMiddleware();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapHub<MessagesHub>("/messagesHub");
});
}
..
..
public class MessagesHub : Hub
{
private readonly Logger _logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
public MessagesHub(RedisClient redisClient)
{
}
public override Task OnConnectedAsync()
{
var username = Context.GetHttpContext().User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name);
if (username != null) {
_logger.Info($"{username?.Value} connected to SignalR");
}
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
var username = Context.GetHttpContext().User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name);
if (username != null)
{
_logger.Info($"{username?.Value} disconnected from SignalR");
}
if(exception != null)
{
_logger.Error(exception);
}
return base.OnDisconnectedAsync(exception);
}
}
and this is the code that connects on messageHub from js
var connection = new signalR.HubConnectionBuilder().withUrl("/messagesHub").build();
connection.on("ReceiveReflectionMessage", function (message) {
newReflection = JSON.parse(message);
handleReflection(newReflection);
});
And this for Layout view:
<script src="~/js/signalr/dist/browser/signalr.js" defer></script>
<script src="~/js/messages.js" defer></script>
At the end, no messages received on the frontend due to failed connection, so what is the missing or must add some configurations ??
Im programming in Visual Code C#, i'm making a chat application in SignalR, i want to store messages in a database in MongoDB. I need a help, that how can I use the 'message' and the 'user' variable from chat.js file in the Pogram.cs?
chat.js
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + ": " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
Program.cs
namespace bcwebchat
{
public class Message{
public DateTime Sent;
public string Msg;
}
public class Program
{
public static void Main(string[] args)
{
var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("DemoInsert");
var collec = db.GetCollection<Message>("DemoInsert");
collec.InsertOne(new Message
{
Sent = DateTime.Now,
Msg = "blaaahahaah"
});
I want to use the user and the message in here:
collec.InsertOne(new Message
{
Sent = DateTime.Now,
Msg = "blaaahahaah"
});
Just following this section Microsoft SignalR
First, you should create a ASP.NET Web Application
Then, create a ChatHub and insert message, it should work
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
// insert your message to database
collec.InsertOne(new Message
{
Sent = DateTime.Now,
Msg = message
});
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//some code here
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chatHub");
});
}
I am able to connect to my Hub and I've hooked up OnConnected and OnDisconnected. They should add/subtract from a integer and call a client callback with the new value.
My angular application is connecting to the server successfully but my registered callback function is not being triggered.
Here is my Serverhub:
[HubName("online")]
public class OnlineHub : Hub
{
private static int userCount = 0;
public override Task OnConnected()
{
userCount++;
Clients.All.listUpdated(userCount);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
userCount--;
Clients.All.listUpdated(userCount);
return base.OnDisconnected(stopCalled);
}
}
And here's my Angular SignalRService:
import { AppSettings } from './../app.settings';
import { EventEmitter, Injectable, OnDestroy } from '#angular/core';
declare const $: any;
#Injectable()
export class SignalRService {
// Declare the variables
private onlineHub: any;
// create the Event Emitter
public messageReceived: EventEmitter<any>;
public connectionEstablished: EventEmitter<Boolean>;
public connectionExists: Boolean;
constructor(private appSettings: AppSettings) {
// Setup
this.connectionEstablished = new EventEmitter<Boolean>();
this.messageReceived = new EventEmitter<any>();
this.connectionExists = false;
}
// This method gets executed from angular controller
public initialize(proxyName: string): void {
this.onlineHub = $.connection.online;
this.onlineHub.client.listUpdated = function(list: any): void {
console.log(list);
this.messageReceived.emit(list);
};
this.startConnection();
}
private startConnection(): void {
$.connection.hub.url = this.appSettings.SIGNALR_BASE_URL + '/signalr';
$.connection.hub.start()
.done((data: any) => {
console.log('SignalR Connected with: ' + data.transport.name);
this.connectionEstablished.emit(true);
this.connectionExists = true;
})
.fail((error: any) => {
console.log('SignalR could not connect: ' + error);
this.connectionEstablished.emit(false);
});
}
private registerOnServerEvents() {
this.onlineHub.client.listUpdated = function(list: any): void {
console.log(list);
this.messageReceived.emit(list);
};
}
}
I am registering my callback "listUpdated" before I run start() as the documentation says and $.connection.hub contains client.listUpdated before start() is called so it should register. But still, the OnConnected method is not called.
I fixed this issue by surrounding the OnConnected() and OnDisconnected() code in try/catch block and created a clientside method called "error" that returns eventual exceptions to the client. That way I found out that I had a Json Serialization issue.
My Hub now looks like this:
[HubName("online")]
public class OnlineHub : Hub
{
private static int userCount = 0;
public override Task OnConnected()
{
try
{
userCount++;
Clients.All.listUpdated(userCount);
}
catch (Exception exc)
{
Clients.All.error(exc);
}
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
try
{
userCount--;
Clients.All.listUpdated(userCount);
}
catch (Exception exc)
{
Clients.All.error(exc);
}
return base.OnDisconnected(stopCalled);
}
}
And I register the error callback on the js client BEFORE calling start():
this.onlineHub.client.error = (exc: any): void => {
console.log('Error occured:', exc);
};
I'm trying to get the webview control into editable mode (for UWP). Here's my code where I am injecting the js to do that:
private const string EditableParameter = "~editable~";
private const string SetBodyEditableScript = #"
try
{
document.body.contentEditable = '" + EditableParameter + #"';
}
catch(e)
{
}";
public MainPage()
{
this.InitializeComponent();
MakeWebviewEditable();
}
private const string EventNotificationFormat = #"window.external.notify('{0}');";
private async void MakeWebviewEditable()
{
await InjectJavaScriptAsync(SetBodyEditableScript.Replace(EditableParameter, "true"));
}
private async Task InjectJavaScriptAsync(string jscript)
{
await WebView.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
try
{
string result = await WebView.InvokeScriptAsync("eval", new string[] { jscript });
}
catch (Exception ex)
{
}
});
}
But it doesn't work and the webview control is still not editable.
It turned out to be pretty simple. Just had to set the designmode to true:
document.designMode='on';
I have problem in my site
I need push notification and I use server sent event javascript and Handler.ashx.
My NotificationHandler.ashx:
public class NotificationHandler : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
public void EndProcessRequest(IAsyncResult result)
{
}
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/event-stream";
context.Response.CacheControl = "no-cache";
var _json = new JsonStringSerializer();
var _chat = new ChatManager();
while (true)
{
var GetStorage = _chat.GetStorage();
var GetJson = _json.SerializeToString<ChatUserStorage>(GetStorage);
context.Response.Write(string.Format("data: {0}\n\n", GetJson));
context.Response.Flush();
if (context.Response.IsClientConnected == false)
{
break;
}
System.Threading.Thread.Sleep(3000);
}
}
public bool IsReusable
{
get
{
return false;
}
}
JavaScript :
function ServerSentNotification() {
sourceEventSource = new EventSource('/services/NotificationHandler.ashx', { retry: 1000 });
sourceEventSource.addEventListener("open", function (event) {
}, false);
sourceEventSource.addEventListener("error", function (event) {
if (event.eventPhase == EventSource.CLOSED) {
}
}, false);
sourceEventSource.addEventListener("message", function (event) {
UpdateProgress(event.data);
}, false);
}
All request stuck my page and another ajax poll not working request waiting.
I use web application framework 4.0