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 ...
}
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");
}
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 wish to update a table in a database using php. I'm developing a game in unity using php to retrieve and update data. Each user logs in with their FB details (the correct way by using the FB API), but for now the username is a unique int ID and a sc column that needs to be updated.
Here is a link example: http://www.mydomain.co.za/php/myphp2.php?id=1&sc="1,2"
PHP code (myphp2.php):
<?php
require_once('/home/########/public_html/php/mysqli_connect.php');
$id = $_GET['id'];
$selected_cards = $_GET['sc'];
$query = "UPDATE PlayerCards SET SelectedCards=$selected_cards WHERE ID=$id";
$response = #mysqli_query($dbc, $query);
if($response){
echo 'Updated the sc of the selected id';
} else {
echo 'could not execute database query 2';
}
?>
This way I can update any user's sc value using a browser. (BIG PROBLEM)
Here is my C# scripts for Unity that retrieves the facebook user's login ID that I will use in my database to store values:
FB_manager.cs: (script that contains data)
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using Facebook.Unity;
public class FB_manager : MonoBehaviour {
private static FB_manager _instance;
public static FB_manager Instance
{
get {
if(_instance == null){
GameObject fbm = new GameObject("FBManager");
fbm.AddComponent<FB_manager>();
}
return _instance;
}
}
public bool IsLoggedIn {get; set;}
public string ProfileName {get; set;}
public Sprite ProfilePic {get; set;}
public string ProfileEmail {get; set;}
void Awake()
{
DontDestroyOnLoad(this.gameObject);
_instance = this;
}
public void InitFB(){
if (!FB.IsInitialized) {
// Initialize the Facebook SDK
FB.Init(InitCallback, OnHideUnity);
} else {
IsLoggedIn = FB.IsLoggedIn;
}
}
private void InitCallback()
{
if (FB.IsInitialized) {
Debug.Log("FB is logged in");
GetProfile();
FB.ActivateApp();
} else {
Debug.Log("FB not logged in");
}
IsLoggedIn = FB.IsLoggedIn;
}
private void OnHideUnity(bool isGameShown)
{
if (!isGameShown) {
// Pause the game - we will need to hide
Time.timeScale = 0;
} else {
// Resume the game - we're getting focus again
Time.timeScale = 1;
}
}
public void GetProfile(){
FB.API("/me?fields=first_name",HttpMethod.GET, DisplayUserName);
FB.API("/me/picture?type=square&height=128&&widht=128",HttpMethod.GET, DisplayProfilePic);
FB.API("/me?fields=email",HttpMethod.GET, DisplayUserEmail);
}
void DisplayUserName(IResult result){
if(result.Error == null){
ProfileName = "" + result.ResultDictionary["first_name"];
} else {
Debug.Log(result.Error);
}
}
void DisplayUserEmail(IResult result){
if(result.Error == null){
Debug.Log(result);
ProfileEmail = "" + result.ResultDictionary["id"];
} else {
Debug.Log(result.Error);
}
}
void DisplayProfilePic(IGraphResult result){
if(result.Texture != null){
ProfilePic = Sprite.Create(result.Texture, new Rect(0,0,128,128), new Vector2());
} else {
Debug.Log(result.Error);
}
}
}
FB_script.cs: (script that contains data)
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using Facebook.Unity;
public class FB_script : MonoBehaviour {
public GameObject DialogLoggedIn;
public GameObject DialogLoggedOut;
public GameObject logInStatusLabel;
public GameObject Name;
public GameObject ProfilePic;
void Awake()
{
FB_manager.Instance.InitFB();
HandleMenu(FB.IsLoggedIn);
}
public void FBLogin() {
var perms = new List<string>() { "public_profile", "email", "user_friends", "publish_actions"};
FB.LogInWithReadPermissions(perms, AuthCallback);
}
private void AuthCallback(ILoginResult result)
{
if (FB.IsLoggedIn) {
HandleMenu(FB.IsLoggedIn);
Debug.Log("User logged in");
FB_manager.Instance.IsLoggedIn = true;
FB_manager.Instance.GetProfile();
// AccessToken class will have session details
var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
// Print current access token's User ID
Debug.Log(aToken.UserId);
// Print current access token's granted permissions
foreach (string perm in aToken.Permissions) {
Debug.Log(perm);
}
} else{
Debug.Log("User cancelled login");
}
HandleMenu(FB.IsLoggedIn);
}
void HandleMenu(bool isLoggedIn) {
if (isLoggedIn) {
DialogLoggedIn.SetActive(true);
DialogLoggedOut.SetActive(false);
logInStatusLabel.GetComponent<Text>().text = "Logged in as: ";
if(FB_manager.Instance.ProfileName!=null){
Text userName = Name.GetComponent<Text>();
userName.text = "" + FB_manager.Instance.ProfileName;
} else {
StartCoroutine("WaitForProfileName");
}
if(FB_manager.Instance.ProfilePic!=null){
Image image = ProfilePic.GetComponent<Image>();
image.sprite = FB_manager.Instance.ProfilePic;
} else {
StartCoroutine("WaitForProfilePic");
}
if(FB_manager.Instance.ProfileEmail!=null){
Text userName = Name.GetComponent<Text>();
userName.text = "" + FB_manager.Instance.ProfileEmail;
} else {
StartCoroutine("WaitForProfileEmail");
}
} else {
DialogLoggedIn.SetActive(false);
DialogLoggedOut.SetActive(true);
logInStatusLabel.GetComponent<Text>().text = "Not logged in";
}
}
IEnumerator WaitForProfileName(){
while(FB_manager.Instance.ProfileName==null){
yield return null;
}
HandleMenu(FB.IsLoggedIn);
}
IEnumerator WaitForProfilePic(){
while(FB_manager.Instance.ProfilePic==null){
yield return null;
}
HandleMenu(FB.IsLoggedIn);
}
IEnumerator WaitForProfileEmail(){
while(FB_manager.Instance.ProfileEmail==null){
yield return null;
}
HandleMenu(FB.IsLoggedIn);
}
}
I can connect to the database within Unity so that it access the database in order to update the table. Giving only update privileges when connecting within unity. The id and sc can then be enclosed by a script (embedding php into the script) to update the table. Will users be able to change the id inside the script? When deploying the game will user be able to edit scripts?
When a user logs in with Facebook credentials, then set their id in a session variable. Use the session variable in your sql query so that only the user can update their cards.
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?
I am using ServiceStack.Client to consume, the data pushed by my server(which is an aspx page).
Below is the code which i use to consume the data using ServiceStack Client:
using System;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;
using System.Threading;
using ServiceStack;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ServerEventConnect connectMsg = null;
var msgs = new List<ServerEventMessage>();
var commands = new List<ServerEventMessage>();
var errors = new List<Exception>();
var client = new ServerEventsClient("https://testing.leadsquared.com/ReferralCampaign/Demo")
{
OnConnect = e => PrintMsg(e),
OnCommand = e => PrintCmdMsg(e),
OnMessage = e => PrintCmMsg(e),
OnException = e => PrintExMsg(e)
}.Start();
Console.Read();
}
private static void PrintCmMsg(ServerEventMessage e)
{
if (e != null)
{
PrintMsg(e.Data);
}
}
private static void PrintExMsg(Exception e)
{
if (e != null)
{
PrintMsg(e.Message);
}
}
private static void PrintCmdMsg(ServerEventMessage e)
{
if (e != null)
{
PrintMsg(e.Data);
}
}
private static void PrintMsg(ServerEventConnect e)
{
if (e!=null)
{
PrintMsg(e.Data);
}
}
private static void PrintMsg(string x)
{
Console.WriteLine(x);
}
}
}
When I run my code , the client does print any message on the console.
The ConnectionDisplayName property is "(not connected)".
If i subscribe to the same URL using javascript EventSource, i get the notifications.
My requirement is that I would want to consume the data by my server in C#.
How can I achieve this?
Firstly the url needs to be the BaseUri where ServiceStack is hosted, i.e. the same url used in JavaScript ServerEvents Client, e.g:
var client = new ServerEventsClient(BaseUrl).Start();
It's not clear if /ReferralCampaign/Demo is the BaseUri or not.
You will also want to call Connect() to wait for the client to make a connection, e.g:
await client.Connect();
Then to see message events you'll need to call a ServiceStack Service that publishes a Notify* Event on IServerEvents API which you can use with a separate JsonServiceClient or the ServiceClient available in ServerEventsClient, e.g:
client.ServiceClient.Post(new PostRawToChannel {
From = client.SubscriptionId,
Message = "Test Message",
Channel = channel ?? "*",
Selector = "cmd.announce",
});
This is an example calling the Chat PostRawToChannel ServiceStack Service:
public class ServerEventsServices : Service
{
public IServerEvents ServerEvents { get; set; }
public void Any(PostRawToChannel request)
{
// Ensure the subscription sending this notification is still active
var sub = ServerEvents.GetSubscriptionInfo(request.From);
if (sub == null)
throw HttpError.NotFound("Subscription {0} does not exist".Fmt(request.From));
// Check to see if this is a private message to a specific user
if (request.ToUserId != null)
{
// Only notify that specific user
ServerEvents.NotifyUserId(request.ToUserId, request.Selector, request.Message);
}
else
{
// Notify everyone in the channel for public messages
ServerEvents.NotifyChannel(request.Channel, request.Selector, request.Message);
}
}
}
I also recommend looking at the C# ServerEventTests for complete stand-alone examples using C# ServerEventClient.