I just got handed a new project where most of the job was already done but I needed to change some things for my project. I have a self hosted server setup like this in a consoleapp:
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
and a route configured like this:
private static HttpSelfHostConfiguration CreateWebServerConfiguration()
{
var config = new HttpSelfHostConfiguration(string.Format("http://{0}:{1}", Environment.MachineName, 80));
config.Routes.MapHttpRoute("Api", "api/{controller}/{id}/{value}", new {id = RouteParameter.Optional, value = RouteParameter.Optional });
config.Routes.MapHttpRoute("Defect", "defect/{action}/{id}", new { controller = "Defect", action = RouteParameter.Optional, id = RouteParameter.Optional });
config.Routes.MapHttpRoute("Content", "content/{action}/{file}", new { controller = "Content"});
config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}/{date}", new { controller = "Home", action = "Index", id = RouteParameter.Optional, date = RouteParameter.Optional });
var templateConfig = new TemplateServiceConfiguration { Resolver = new DelegateTemplateResolver(name => ReadFileContent("Views", name))};
Razor.SetTemplateService(new TemplateService(templateConfig));
return config;
}
This works perfectly, but I ran into a situation, where I didn't want the front end to pull with a timer on the server(currently auto refresh every 5 min). I want the server to update the frontend, when there is something new to update. I found the solution to this which would be websockets, but I have a lot of problems using those.
my .js file:
(function() {
if ("WebSocket" in window) {
alert("WebSocket is supported by your Browser!");
} ws://' + window.location.hostname + window.location.pathname.replace('index.htm', 'ws.ashx') + '?name='
var socket = new WebSocket("ws://localhost:80/WebsocketServer.cs");
socket.onmessage = function(event) {
alert("message recv.");
alert(event.data);
};
socket.onopen = function() {
alert("open");
}
socket.onerror = function(errorEvent) {
alert("Error");
alert(errorEvent);
};
socket.onclose = function(closeEvent) {
alert(closeEvent.code);
}
})();
I found multiple examples like this one: https://blog.simpleisbest.co.uk/2012/05/01/websockets-with-asp-net-4-5-and-visual-studio-11/ but it doesn't seem to work for me, this is my files:
WebsocketServer.cs
public class WebsocketServer : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(new WebSocketManager());
}
}
public bool IsReusable
{
get { return false; }
}
}
WebSocketManager.cs
public class WebSocketManager : WebSocketHandler
{
public static WebSocketCollection clients = new WebSocketCollection();
public override void OnClose()
{
clients.Remove(this);
}
public override void OnError()
{
}
public override void OnMessage(string message)
{
}
public override void OnOpen()
{
clients.Add(this);
clients.Broadcast("Connected");
}
}
The frontend returns closing code 1006 or CLOSE_ABNORMAL. My guess is the link to the backend isn't created?
Related
I use signalR in my project. It works fine. In a part on my project, I need to use instant notification when a delete button is clicked. The button is in jQuery datatable. I used the following code:
var connection = new signalR.HubConnectionBuilder().withUrl("/signalRServer").withAutomaticReconnect().build();
connection.start();
function DeleteData(pmId, mainFileName, fileName) {
if (confirm("Are you sure")) {
connection.invoke("PmPageUpdate", pmId).catch(function(err){
return console.error(err);
});
Delete(pmId, mainFileName, fileName);
} else {
return false;
}
}
When I debug, the hub method is not called. Console shows the following error:
PmManagement:692 Error: Failed to invoke 'PmPageUpdate' due to an
error on the server.
at H. (signalr.js:1:13973)
at L.I (signalr.js:1:14804)
at X.L.connection.onreceive (signalr.js:1:10649)
at WebSocket.r.onmessage (signalr.js:1:27565)
How can I fix this?
Update:
The followings are codes I've written for the hub. Some methods that are not related to this question has been ignored.
Hub:
private readonly IUserRepository _userRepository;
private readonly ICostCenterRepository _costcenterRepository;
private readonly IHttpContextAccessor _httpContext;
private readonly INotificationRepository _notificationRepository;
private readonly IMessageRepository _messageRepository;
private readonly IPmRepository _pmRepository;
public MessageHub(IUserRepository userRepository, ICostCenterRepository costcenterRepository, IHttpContextAccessor httpContext,
INotificationRepository notificationRepository, IMessageRepository messageRepository, IPmRepository pmRepository)
{
_userRepository = userRepository;
_costcenterRepository = costcenterRepository;
_httpContext = httpContext;
_notificationRepository = notificationRepository;
_messageRepository = messageRepository;
_pmRepository = pmRepository;
}
//define a dictionary to store the userid.
private static Dictionary<string, List<string>> NtoIdMappingTable = new Dictionary<string, List<string>>();
public Task JoinNotificationGroup(string groupName)
{
return Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
public Task LeaveNotificationGroup(string groupName)
{
return Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
public override async Task OnConnectedAsync()
{
var username = Context.User.Identity.Name;
var userId = Context.UserIdentifier;
List<string> userIds;
//store the userid to the list.
if (!NtoIdMappingTable.TryGetValue(username, out userIds))
{
userIds = new List<string>();
userIds.Add(userId);
NtoIdMappingTable.Add(username, userIds);
}
else
{
userIds.Add(userId);
}
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
var username = Context.User.Identity.Name;
//remove userid from the List
if (NtoIdMappingTable.ContainsKey(username))
{
NtoIdMappingTable.Remove(username);
}
await base.OnDisconnectedAsync(exception);
}
string username = _userRepository.GetUsernameByCostCenter(pmId).ToString();
var userId = NtoIdMappingTable.GetValueOrDefault(username);
await Clients.User(userId.ToString()).SendAsync("SignalReceiver");
}
Question:
Why am I being able to send messages to all users but not to a specific user? Am I missing something obvious here?
Problem:
My POC uses the following resources:
The official Spring Boot tutorial for sending messages to all users
This Baeldung tutorial for sending messages to specific users
I am currently using two endpoints:
The first one sends messages to all users and works fine.
Send to all users - working
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public StompResponse greeting(StompRequest message) throws Exception {
Thread.sleep(1000); // simulated delay
return new StompResponse("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
The second one doesn't work. The method sendSpecific never gets called and I'm stuck trying to understand why.
Send to specific user - NOT working
/**
* Example of sending message to specific user using 'convertAndSendToUser()' and '/queue'
*/
#MessageMapping(Constants.SECURED_CHAT_ROOM)
public void sendSpecific(#Payload StompRequest message, Principal user, #Header("simpSessionId") String sessionId) throws Exception {
System.out.println("METHOD CALLED"); // This method never gets called
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
StompResponse out = new StompResponse(HtmlUtils.htmlEscape(message.getName()));
simpMessagingTemplate.convertAndSendToUser("hardcoded_username", Constants.SECURED_CHAT_SPECIFIC_USER, out, headerAccessor.getMessageHeaders());
}
Socket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", Constants.SECURED_CHAT_SPECIFIC_USER);
config.setUserDestinationPrefix("/secured/user");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/connection-uri").withSockJS();
registry.addEndpoint(SECURED_CHAT_ROOM).withSockJS();
}
}
Client code:
const SocketConnection = function(l) {
const listener = l;
let sessionId;
const parseSessionId = (url) => {
console.log(stompClient.ws._transport.url);
url = url.replace("ws://localhost:8080" + Uri.CONNECTION_URI + "/", "");
url = url.replace("/websocket", "");
url = url.replace(/^[0-9]+\//, "");
console.log("Your current session is: " + url);
return url;
};
this.connect = () => {
const socket = new SockJS(Uri.CONNECTION_URI);
stompClient = Stomp.over(socket);
stompClient.connect({}, (frame) => {
listener.onSocketConnect(true);
console.log('Connected: ' + frame);
stompClient.subscribe(Uri.ALL_USERS, function (greeting) {
listener.onResponseReceived(JSON.parse(greeting.body).content);
});
// WIP
const url = stompClient.ws._transport.url
sessionId = parseSessionId(url);
console.log('SUBSCRIBING FOR SPECIFIC USER: ', Uri.SECURED_CHAT_SPECIFIC_USER)
// stompClient.subscribe(Uri.SECURED_CHAT_SPECIFIC_USER + sessionId, function (msgOut) {
stompClient.subscribe(Uri.SECURED_CHAT_SPECIFIC_USER + "-user" + sessionId, function (msgOut) {
// that.messageOut(JSON.parse(msgOut.body), opts);
console.log('SINGLE USER WORKS!!!!!!!',JSON.parse(msgOut.body))
});
});
}
this.disconnect = () => {
if (stompClient !== null) {
stompClient.disconnect();
}
listener.onSocketConnect(false);
console.log("Disconnected");
}
this.sendRequest = (uri) => {
stompClient.send(uri, {}, JSON.stringify({'name': $("#name").val()}));
}
};
Client initialization:
$(function () {
const socketClient = new SocketConnection(new FormUI());
$("form").on('submit', function (e) {
e.preventDefault();
});
$( "#connect" ).click(function() { socketClient.connect(); });
$( "#disconnect" ).click(function() { socketClient.disconnect(); });
$( "#send" ).click(function() {
socketClient.sendRequest(Uri.ALL_USERS);
socketClient.sendRequest(Uri.SECURED_CHAT_ROOM);
});
});
I'm trying to make a web socket connection between js and java, but I get this answer:
Uncaught DOMException: An attempt was made to use an object that is not, or is no longer, usable
I did it based in some samples on internet. Someone have some idea what can it be?
Project Name is Teste-1
JS Code
var socket = null;
function init(){
socket = new WebSocket("wss://localhost:8080/Teste-1/task");
socket.onmessage = onMessage;
}
function onMessage(event){
var task = JSON.parse(event.data);
if(task.action === "add"){
document.getElementById("point").innerHTML += event.data;
}
}
function teste() {
var action = {
action: "add",
name: "Test",
description: "This is just a test"
};
socket.send(JSON.stringify(action));
}
window.onload = init;
HTML Code
<html>
<head>
<title>Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<button onclick="teste()">Teste</button>
<div id="point"></div>
<script src="websocket.js"></script>
</body>
</html>
JAVA Codes
public class Task implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private String description;
public Task(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
#ServerEndpoint(value="/task")
public class TaskSocket {
#Inject
private TaskSessionHandler handler;
#OnOpen
public void open(Session session){
handler.addSession(session);
}
#OnClose
public void close(Session session){
handler.removeSession(session);
}
#OnError
public void onError(Throwable error){
Logger.getLogger(TaskSocket.class.getName()).log(Level.SEVERE, null, error);
}
#OnMessage
public void handleMessage(String message, Session session) {
try (JsonReader reader = Json.createReader(new StringReader(message))) {
JsonObject jsonMessage = reader.readObject();
if ("add".equals(jsonMessage.getString("action"))) {
Task task = new Task();
task.setName(jsonMessage.getString("name"));
task.setDescription(jsonMessage.getString("description"));
handler.addTask(task);
}
}
}
}
#ApplicationScoped
public class TaskSessionHandler {
//Each client connected to the application has its own session.
private final Set<Session> sessions = new HashSet<>();
private final Set<Task> tasks = new HashSet<>();
public void addSession(Session session) {
sessions.add(session);
for(Task task : tasks){
JsonObject addMessage = createJSON(task);
sendToSession(session, addMessage);
}
}
public void removeSession(Session session) {
sessions.remove(session);
}
public List<Task> getTasks(){
List<Task> list = new ArrayList<Task>(tasks);
return list;
}
public void addTask(Task e){
tasks.add(e);
JsonObject message = this.createJSON(e);
sendToAllConnectedSessions(message);
}
private JsonObject createJSON(Task task){
JsonProvider provider = JsonProvider.provider();
JsonObject message = provider.createObjectBuilder()
.add("action", "add")
.add("name",task.getName())
.add("description",task.getDescription()).build();
return message;
}
private void sendToAllConnectedSessions(JsonObject message) {
for (Session session : sessions) {
sendToSession(session, message);
}
}
private void sendToSession(Session session, JsonObject message) {
try {
session.getBasicRemote().sendText(message.toString());
} catch (IOException ex) {
sessions.remove(session);
Logger.getLogger(TaskSessionHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
I got the same error message due to the websocket.send() being executed before the connection to the webSocket server was established and open.
My contribution would be the advice to make sure messages aren't sent until the connection is open, so that the server actually can receive them.
I cannot tell whether that was the problem in the question asked though. However I faced a very similar problem and ended up here, and wanted to share what worked for me.
In my case the code that didn't work looked like this:
const exampleSocket = new WebSocket('wws://example')
exampleSocket.onopen = function (event) {
console.log('connection is open!')
}
exampleSocket.send(JSON.stringify(msg))
exampleSocket.onmessage = function (event) {
console.log('message event data looks like this: ', event.data)
}
What worked was moving the code that sent a message into the onopen callback function.
const exampleSocket = new WebSocket('wws://example')
exampleSocket.onopen = function (event) {
console.log('connection is open!')
exampleSocket.send(JSON.stringify(msg)) // <- this is the change
}
exampleSocket.onmessage = function (event) {
console.log('message event data looks like this: ', event.data)
}
Sidenote: the readyState property of websocket could be useful when dealing with making sure the server is ready to receive messages.
If it is 1, it means connection is open, so it could be used like this:
if (exampleSocket.readyState === 1) {
console.log("It is safe to send messages now")
}
I've created a plugin which connects and subscribes to pusher channel successfully via NativeScript using this Java plugin,
now I'm trying to create an eventListener to get events in Nativescript,
this is my Java plugin:
public class PusherAndroid {
public void connectToPusher(String app_key, String channel_name, String event_name) {
PusherOptions options = new PusherOptions().setCluster("eu");
Pusher pusher = new Pusher(app_key, options);
pusher.connect(new ConnectionEventListener() {
#Override
public void onConnectionStateChange(ConnectionStateChange change) {
System.out.println("State changed to " + change.getCurrentState() +
" from " + change.getPreviousState());
}
#Override
public void onError(String message, String code, Exception e) {
System.out.println("There was a problem connecting!");
}
}, ConnectionState.ALL);
Channel channel = pusher.subscribe(channel_name);
channel.bind(event_name, new SubscriptionEventListener() {
#Override
public void onEvent(PusherEvent event) {
System.out.println("Received event with data: " + event.toString());
}
});
}
}
and this is my module:
module.exports = {
connect:function(app_key, channel_name, event_name) {
var psh = new com.pxb.pusherandroid.PusherAndroid();
psh.connectToPusher(app_key, channel_name, event_name);
var EventListener;
function initializeEventListener() {
if (EventListener) {
return;
}
EventListener = com.pxb.pusherandroid.PusherAndroid.extend({
interfaces: [com.pusher.client.channel.SubscriptionEventListener],
onEvent: event => {
console.log(event);
}
});
}
initializeEventListener();
<HERE I NEED MY CHANNEL>.bind(event_name, new EventListener());
}
};
Now, how can I get this channel in Javascript, to use it as my defined connected channel and bind eventListener to it?
Channel channel = pusher.subscribe(channel_name);
thank you
I dont really know how NativeScript works, but couldn't you just searialize your Channel to a json string, store it in a global variable on your PusherAndroid class and then access and desearialize it on your module?
thanks to #Manoj, there's no need to code in Java and try to use them in Javascript,
we can directly use Java classes and methods with Javascript,
there's really not enough reference for this.
here is my module after deleting all java code and just calling the classes and methods from pusher-java-library directly:
module.exports = {
connect:function(app_key, channel_name, event_name) {
PusherOptions = com.pusher.client.PusherOptions;
Pusher = com.pusher.client.Pusher;
Channel = com.pusher.client.channel.Channel;
SubscriptionEventListener = com.pusher.client.channel.SubscriptionEventListener;
PusherEvent = com.pusher.client.channel.PusherEvent;
var options = new PusherOptions().setCluster("eu");
var pusher = new Pusher(app_key, options);
pusher.connect();
var channel = new Channel(pusher.subscribe(channel_name));
}
};
now going to add my eventlistener with Javascript <3
I have made SignalR custom methods to redirect users if they want to join group that already has 2 members. Everything seems to be working fine except the redirect method.
ChatHub.cs:
namespace SignalRChat
{
public class ChatHub : Hub
{
static string test2 = "";
public static Dictionary<string, int> rooms = new Dictionary<string, int>();
public void Test(string groupName)
{
if (!rooms.ContainsKey(groupName))
{
rooms.Add(groupName, 1);
}
else if(rooms[groupName] != 2)
{
rooms[groupName] = 2;
}
else
{
test2 = "testing";
Redirect();
}
}
public Task Redirect()
{
return Clients.Caller.redirectTo();
}
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
}
}
Scripts:
var chat2 = $.connection.chatHub;
$.connection.hub.start().done(function () {
chat2.server.test(roomId);
chat2.client.redirectTo = function () {
window.location.replace("http://stackoverflow.com");
}
chat2.server.joinGroup(roomId);
});
When there are already 2 clients in a group, test2 is set to "testing" but client does not get redirected.
Change your scripts to:
var chat2 = $.connection.chatHub;
// var roomId = "R1" <-- I added this for testing
chat2.client.redirectTo = function () {
window.location.replace("http://stackoverflow.com/questions/35848709/signalr-custom-method-to-redirect#35857376");
}
$.connection.hub.start().done(function () {
chat2.server.joinGroup(roomId);
chat2.server.test(roomId);
});
Note: in your Test method the logic says that the redirect will only run if rooms dictionary contains the given roomname and the int value corresponding to that roomname is '2'. Probably not your real planned logic.
For testing I added to the backed code:
public static Dictionary<string, int> rooms = new Dictionary<string, int>();
public void Test(string groupName) // <-- I sent "groupName: R1" from js
{
rooms["R1"] = 2;
if ...
}