I am quite new to SignalR.
My first assignment is to make simple chat app.
I have been browsing and reading and finally made a page where you come and chat and it works fine.
Now I need to show a list of connected clients. To achieve this I have wrote the following code.
This is my HUB.
public class ChatHub: Hub
{
chatEntities dc = new chatEntities();
public void Send(string message,string clientName)
{
Clients.addMessage(message,clientName);
}
// I want to save the user into my database, when they join
public void Joined(string userId,string userName)
{
CurrentChattingUsers cu = new CurrentChattingUsers();
cu.ConnectionId = userId;
cu.UserName = userName;
dc.CurrentChattingUsers.AddObject(cu);
dc.SaveChanges();
Clients.joins(userId, userName);
}
// This will return a list of connected user from my db table.
public List<ClientModel> GetConnectedUsers()
{
var query = (from k in dc.CurrentChattingUsers
select new ClientModel()
{
FullName = k.UserName,
UserId = k.ConnectionId
}).ToList();
return query;
}
}
And thats it...Now what??
Am I going to the right direction? If, I am then how to call this methods from the view?
Some good suggestions will really help me out.
cheers
EDIT:
I have added the following script when the hub start
$.connection.hub.start(function () {
chat.getConnectedUsers();
});
This is the method that returns the client names in my Hub
public List<ClientModel> GetConnectedUsers()
{
var data = (from k in dc.Users
select new ClientModel()
{
FullName = k.UserName
}).ToList();
Clients.loadUsers(data);
return data;
}
in firebug i can see it returns something as follows;
{"State":{},"Result":[{"FullName":"mokarom","UserId":null}, {"FullName":"aka8000","UserId":null},{"FullName":"johnnyno5","UserId":null},{"FullName":"reza","UserId":null},{"FullName":"amyo","UserId":null},{"FullName":"rezatech","UserId":null}],"Id":"0","Error":null,"StackTrace":null}
But, how would I display that in my view??
EDIT:
this the complete view so far
<script type="text/javascript">
var chat;
var myClientName
$(document).ready(function(){
myClientName = '#Request.Cookies["ChatterName"].Value';
// Created proxy
chat = $.connection.chatHub;
// Assign a function to be called by the server
chat.addMessage = onAddMessage;
// Register a function with the button click
$("#broadcast").click(onBroadcast);
$('#message').keydown(function (e) {
if (e.which == 13) { //Enter
e.preventDefault();
onBroadcast();
}
});
// Start the connection
$.connection.hub.start(function () {
chat.getConnectedUsers();
});
chat.loadUsers = function (data) {
loadUsers(data);
};
});
function onAddMessage(message,clientName) {
// Add the message to the list
$('#messages').append('<div class="chatterName">' + clientName + ' </div><div class="chatterMessage"> ' + message + '</div><div class="clear">');
}
function onBroadcast() {
// Call the chat method on the server
chat.send($('#message').val(), myClientName);
$('#message').val('');
}
function loadUsers(data) {
$('#clientList').html(data.Result[0].FullName);
}
</script>
Problem: don't see anything here: $('#clientList').html(data.Result[0].FullName);
firebug says 'data is not defined'
JavaScript
var chats = $.connection.chatHub;
chats.loadUsers = function (data) { loadUsers(data); };
var connectedUserCount = 0;
$.connection.hub.start(function ()
{ chats.getConnectedUsers(); });
function loadUsers = = function (data) {
console.log(data); //so you can see your data in firebug.etc
//which signal r will have converted to json so you could try
var numberOfUsers = data.length;
}
Once hub is started chats would have all the public functions of your hub available as javascript functions. This is what the signalr/hubs creates using the best available connection method between client and server.
In reverse your C# hub will have access to any javascripts functions you setup, e.g.
Clients.loadUsers(query);
//add this to you server side just before you return query
ps - you might also consider using OnConnectedAsync, though of course you might still persist these. I'm also waiting for full support for web farm support using sql, which is in the pipeline.
Related
I have created my module modulemy with the following code :
var request = require('request');
var EventEmitter = require('events').EventEmitter;
var User = new EventEmitter();
var userFbInfos = new EventEmitter();
//database and calls the get started conversation.
User['checkUserRegistered'] = function (userID) {
//if registered and have phone number then nothing to do otherwise, get user infos return it and last ask his phonenumber
//Now we consider that the user is not registered. So we get is infos
userFbInfos.getInfos(userID);
userFbInfos.on('gotten', function (err, userInfos) {
User.emit('checked',err, userInfos);
});
}
//This gets user info from Facebook and notifies when the information is gotten via event gotten
//The thing is as node is event driven if you request the user info and run a code before getting that info, the userinfos will be null.
userFbInfos['getInfos'] = function (userID) {
request.get("https://graph.facebook.com/v2.6/" + userID + "?fields=first_name,last_name,locale,gender,timezone,profile_pic&access_token=" + process.env.page_token,
function (err, response, body) {
userFbInfos.emit('gotten',err,JSON.parse(body));
});
}
exports.userFbInfos = userFbInfos;
exports.User = User;
and then in my app.js, I am using it as follow :
modulemy = require('./modulemy.js');
var user = modulemy.User;
//Checking if user is registered.
user.checkUserRegistered('123');
//This variable checks if the event was trigger before already to not execute the code twice.
var called = false;
user.on('checked', function (err, userInfos) {
if (!called){
console.log("Call Checked");
var name = "";
if (typeof (userInfos) != 'undefined') {
name = userInfos['first_name'];
}
console.log("Yes you are there");
called = true;
}
});
The problem is the checked event is being triggered multiple times... I don't understand where the problem is ... I need help.
there might be an issue when receiving the packages from fb. Try to count some variable up if the input data coming from the query is the same as the one from the query before.
i am testing this signal with a very basic string. But the client side is not firing the server code and there is no error. i added the [HubName("MyHub1")] and the [HubMethodName("GetValueString")] in because if not the javascript will complaint client undefine and methodname not found.
after i added this 2 meta in. there is no error but server code was not fire. Anyone help please.
Client Script
(function () {
// Defining a connection to the server hub.
debugger
var myHub = $.connection.MyHub1;
// Setting logging to true so that we can see whats happening in the browser console log. [OPTIONAL]
$.connection.hub.logging = true;
// Start the hub
$.connection.hub.start();
// This is the client method which is being called inside the MyHub constructor method every 3 seconds
myHub.client.GetValueString = function (serverTime) {
// Set the received serverTime in the span to show in browser
$("#newTime").html(serverTime);
};
}());
Server script
[HubName("MyHub1")]
public class MyHub1 : Hub
{
public void Hello()
{
Clients.All.hello();
}
[HubMethodName("GetValueString")]
public void Getstring() {
var taskTimer = Task.Factory.StartNew(async () =>
{
while (true)
{
string timeNow = DateTime.Now.ToString();
//Sending the server time to all the connected clients on the client method ()
Clients.All.GetValueString("test");
//Delaying by 3 seconds.
await Task.Delay(3000);
}
}, TaskCreationOptions.LongRunning
);
}
}
Update 1
so i change my javascript to this and the output was "undefine" but no error also
var haha = myHub.client.GetValueString;
// Set the received serverTime in the span to show in browser
Try to give this ago. Also, use this for reference
I've added a console.log try and see if you see it when you run this code.
(function () {
var myHub = $.connection.MyHub1;
myHub.client.GetValueString = function (serverTime) {
$("#newTime").html(serverTime);
};
$.connection.hub.logging = true;
$.connection.hub.start().done(function() {
console.log("hub is ready"); // tell me if you see the message in your console log
myHub.server.getstring() // note i wrote getstring with a small g, has to be.
});
}());
[HubName("MyHub1")]
public class MyHub1 : Hub
{
public void Hello()
{
Clients.All.hello();
}
public void Getstring() {
var taskTimer = Task.Factory.StartNew(async () =>
{
while (true)
{
string timeNow = DateTime.Now.ToString();
//Sending the server time to all the connected clients on the client method ()
Clients.All.GetValueString("test");
//Delaying by 3 seconds.
await Task.Delay(3000);
}
}, TaskCreationOptions.LongRunning
);
}
}
I have a simple application using SignalR, where I wan't to display different data for different machines, depending on which machine has been chosen by the user
My Hub class looks like this:
readonly ISprayBroadcaster _sprayBroadcaster;
readonly IWorkRecordRepository _workRecordRepository;
public SprayHub(ISprayBroadcaster sprayBroadcaster, IWorkRecordRepository workRecordRepository)
{
_sprayBroadcaster = sprayBroadcaster;
_workRecordRepository = workRecordRepository;
}
public void Broadcast(string name)
{
Process.DataProcess(_workRecordRepository, Clients, name).Wait();
}
public void SwapGroup(string previousGroup, string newGroup)
{
Groups.Remove(Context.ConnectionId, previousGroup);
Groups.Add(Context.ConnectionId, newGroup);
}
public void JoinGroup(string groupName)
{
Groups.Add(Context.ConnectionId, groupName);
}
This is how I initialize the hub on the client side and call Broadcast method on it:
$(function () {
hub = $.connection.sprayHub;
function init() {
hub.server.joinGroup("machine1");
hub.server.broadcast("machine1");
};
// Client-side hub method that the server will call
hub.client.updateData = function (shifts) {
ViewModel.Measurements(recreateArray(shifts));
}
$.connection.hub.start().done(init);
});
Broadcast method goes to the DataProcess method which populates data to the clients from the assigned group:
public static async Task DataProcess(IWorkRecordRepository workRecordRepository, IHubConnectionContext hubClients, string machine)
{
var shiftRecords = await workRecordRepository.Records(machine, DateTime.Now).ToList();
var result = SLGT.Sentinel.Core.Calculations.Shifts(shiftRecords);
hubClients.Group(machine).updateData(result);
}
At the same time I setup a broadcaster which runs in the loop and feeds clients with appropriate data. This is a broadcast method from the broadcaster which calls the same DataProcess method to populate data for each machine found in the system:
void Broadcast(object state)
{
lock (_updateLock)
{
if (_updating)
return;
_updating = true;
var machines = _workRecordRepository.Machines();
machines.Subscribe(async machine =>
{
await Process.DataProcess(_workRecordRepository, Clients, machine);
});
_updating = false;
}
}
Finally when user clicks a button for different machine on the client side I swap the groups for the appropriate data to be displayed for this client:
$(".machineButton").click(function () {
var name = $(this).attr("id");
hub.server.swapGroup(previousGroup, name);
previousGroup = name;
}
Now, when I run this application in my test environment, everything works fine. When I run it on the server, swap between the groups doesn't work correctly, and the client is constantly fed with the same set of data. Why might it be happening? As I said local version works fine so I do not know how to debug it?
The group management methods (add and remove) are async. If you don't await the returned task then send to the group immediately after you have a race condition such that the client you just added might not receive the message. Also, you should never call .Wait() from in a hub method. Make the hub method async and await it instead.
readonly ISprayBroadcaster _sprayBroadcaster;
readonly IWorkRecordRepository _workRecordRepository;
public SprayHub(ISprayBroadcaster sprayBroadcaster, IWorkRecordRepository workRecordRepository)
{
_sprayBroadcaster = sprayBroadcaster;
_workRecordRepository = workRecordRepository;
}
public async Task Broadcast(string name)
{
await Process.DataProcess(_workRecordRepository, Clients, name);
}
public async Task SwapGroup(string previousGroup, string newGroup)
{
await Groups.Remove(Context.ConnectionId, previousGroup);
await Groups.Add(Context.ConnectionId, newGroup);
}
public async Task JoinGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
}
Also, is your production environment a single web server or is it a load-balanced farm? If it's a farm you'll need to configure SignalR scale-out.
In my chat application project, I am trying to broadcast usernames to all the users whenever a new user is connected to a server. and remove a username whenever the user leaves the server. The below is the code which I have tried by going through tutorials. (please check the file.js file which is not showing the desired output)
Chat.cs (Working) --> Implements "Hub" class of SignalR
public class Chat : Hub
{
/* showing only related content */
static ConcurrentDictionary<string, User> _users = new ConcurrentDictionary<string, User>();
public override Task OnDisconnected()
{
var user = _users[Context.ConnectionId]; //user as ConnectionId
User removedUser; //new class object
_users.TryRemove(Context.ConnectionId, out removedUser);
return Clients.All.leave(user, DateTime.Now.ToString()); //dynamic expression
}
public void Joined()
{
User user = new User(Context.ConnectionId, Clients.Caller.username);
_users.TryAdd(user.ConnectionID, user);
Clients.All.joins(user.ConnectionID, user.Name, DateTime.Now); //dynamic expression
}
}
User.cs (Working)
public class User
{
public User(string ConnID, string Username)
{
Name = Username;
ConnectionID = ConnID;
}
public string Name { get; set; }
public string ConnectionID { get; set; }
}
file.js (not working)
var chatUsername = window.prompt("Enter Username:", ""); //username
var chat = $.connection.chat; //connection
//
chat.client.joins = function (ConnectionId, name, Date) {
ConnectionId = 1; /* given value to just test */
name = chatUsername;
};
chat.client.leave = function (user, date) {
user = ""; //making the string empty so that the disconnected user value will be lost.
};
//Here is the connection which calls the "Joined" function of the server (Chat.cs)
What should I write in file.js functions (joins and leave) so that I will get the desired result as I mentioned above. Before asking here, I have gone through this site which is doing the same but including additional javascript files(knockout.js and json) which I dont want to include.(bcos I am new to jquery).
In order to pass UserNames to the client you can take your dictionary and in your joined server side method you could change the SignalR line to be:
Clients.All.joins(_users.Values); //dynamic expression
Then the client version of joins would be:
chat.client.joins = function (users) {
for(var i = users.length - 1; i >= 0; i--) {
alert("User Name: " + users[i].Name + "\nUser Connection ID: " + users[i].ConnectionID);
}
};
Of course you can handle the user information differently than alerting it, but that's the gist of how to handle the data. Lastly, I'd recommend against passing down the connection ID to everyone because a third party could then easily hijack it.
I know there are lots of examples out there to do with SignalR but I can't seem to get it working, I was hoping that one of you may be able to show (in full) how a WebPage (threaded loop so we can see it happening over and over) could call a JS method on a Page and change a text label or create a popup or, just something so that we an see the method execute?
I'll give you my code and maybe you can point out the error, but any basic example of Server->Client invocation without a Client first making a request would be amazing!
Hub:
[HubName("chat")]
public class Chat : Hub
{
public void Send(string message)
{
// Call the addMessage method on all clients?
Clients.addMessage(message);
}
}
Calling (Threaded) method:
private void DoIt()
{
int i = 0;
while (true)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<Chat>();
hubContext.Clients.addMessage("Doing it... " + i);
i++;
Thread.Sleep(500);
}
}
JS:
$(function () {
// Proxy created on the fly
var chat = $.connection.chat;
// Declare a function on the chat hub so the server can invoke it
chat.addMessage = function (message) {
confirm("Are you having fun?");
confirm(message);
};
// Start the connection
$.connection.hub.start();
});
The issue I had was a self closing JS import tag which stopped all JS on the page being run...
For others who have the same issue, here is my working example on a Server pushing data out to all clients without any prompting from a client:
Javascript:
$(function () {
// Proxy created on the fly
var chat = $.connection.chat;
// Declare a function so the hub can invoke it
chat.addMessage = function (message) {
document.getElementById('lblQuestion').innerHTML = message;
};
// Start the connection
$.connection.hub.start();
});
HTML:
<h2 id="lblQuestion" runat="server">Please wait for a question...</h2>
Hub:
[HubName("chat")]
public class Chat : Hub
{
public void Send(string message)
{
// Call the addMessage method on all clients
Clients.addMessage(message);
}
public void Broadcast(string message)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<Chat>();
context.Clients.addMessage(message);
}
}
Call to clients:
private void DoIt()
{
int i = 0;
while (true)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<Chat>();
hubContext.Clients.addMessage("Doing it... " + i);
i++;
Thread.Sleep(500);
}
}
Threaded call to DoIt():
var thread = new Thread(new ThreadStart(DoIt));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();