I've got some code that looks like this (this is an excerpt of a much larger project):
this.oldMessagesSubject = new Subject<Message[]>();
this.oldMessages = this.oldMessagesSubject.asObservable();
getOldMessages(): Observable<Message[]> {
this.chatHub.server.getMessages();
return this.oldMessages;
}
I then subscribe to it like this:
this.chatService.getOldMessages()
.subscribe(res => this.messages = res);
I'm not very experienced with ReactiveX, but when I subscribe to the observable returned by getOldMessages(), it iterates through each of the values that it has received since the app started. So if I get the old messages for 'chat 1' that works fine. But if I then navigate to a different page and get the old messages for 'chat 2' the observable emits the messages for 'chat 1' and then the messages for 'chat 2'.
I have a feeling I'm using the Observables in the wrong way and I'd really appreciate any help.
EDIT:
This is where next is called:
this.chatHub.client.showMessages = (messages: Message[]) => this.oldMessagesSubject.next(messages);
getMessages is an RPC to the server. Here is the relevant method on the server (C#):
public Task GetMessages()
{
try
{
// Get the current user
var user = repo.GetUser(Context.User.Identity.Name);
// Associate the current user with a connection
var connection = chatContext.Find(x => x.UserId == user.UserId);
if (connection != null)
{
// Get all the messages in the user's chat (encrypted)
List<EncryptedMessage> encryptedMessages = repo.GetMessages(connection.ChatId);
List<Message> messages = new List<Message>();
// Get the decrpytion key
byte[] key = Encoding.Default.GetBytes(ConfigurationManager.AppSettings["secret"]).Take(16).ToArray();
// Decrypt the messages
foreach (var encryptedMessage in encryptedMessages)
{
Message message = new Message();
message.Id = encryptedMessage.Id;
message.GroupId = encryptedMessage.GroupId;
message.Owner = encryptedMessage.Owner;
message.Sent = encryptedMessage.Sent;
message.Body = cryptoProvider.DecryptMessage(encryptedMessage.Body, key);
messages.Add(message);
}
// Return the messages to the client
return Clients.Caller.ShowMessages(messages);
}
return Clients.Caller.LoginError();
}
catch (Exception ex)
{
return Clients.Caller.Exception(ex);
}
}
When I'm back at my workstation, I'll debug and check that the problem isn't server side. I should be able to tell if next is being called multiple times on each subscription.
Related
I have almost completed pub/sub fake-server, that requests user password and email (from the client), compares this info with database and returns data back. It has 'api_in' and 'api_out' frames and then the JSON.
The publisher takes and processes all the info without a hitch, but it doesn't seem to send anything back to the client (subscriber) and I don't know why, cause it is connected to the subsequent port.
And I know that this implementation is not a classic PUB/SUB pattern, but that was prerequisite, to do it like that.
I tried different pub/sub options, but nothing has changed.
Server
let zmq = require('zeromq');
const sqlite3 = require('sqlite3').verbose();
const DBSOURCE = "./db.sqlite";
let db = new sqlite3.Database(DBSOURCE, (err) => {
if(err) {
console.error(err.message);
throw err;
} else {
console.log('Connected to SQLite database');
db.run(`CREATE TABLE users (
user_id INTEGER,
email TEXT,
passw TEXT)`,
(err) => {
if (err) {
// Table already created
} else {
// Creating rows
let insert = 'INSERT INTO users (user_id, email, passw) VALUES (?,?,?)';
db.run(insert, [123098, 'phillCollins#gmail.com','5502013']);
db.run(insert, [42424242,'dukenukem3d#mustdie.com','RodriguesShallLiveLong']);
db.run(insert, [5,'yourchick#yandex.ru','semolinaAndPain666']);
}
})
}
});
const args = require('minimist')(process.argv.slice(2));
const pubSocket = zmq.socket('pub', null);
pubSocket.bindSync(`tcp://127.0.0.1:${args['pub']}`);
const subSocket = zmq.socket('sub', null);
subSocket.subscribe('api_in');
subSocket.on('message', function(data) {
let message = data.toString().replace(/api_in/g, '');
let mes = JSON.parse(message);
let api_out = 'api_out';
let errorWrongPWD = 'WRONG_PWD';
let errorWrongFormat = 'WRONG_FORMAT';
if(mes.type = 'login') {
db.get(`SELECT user_id from users WHERE email = ? and passw = ?`, [mes.email, mes.pwd], function(err, row) {
if(err) {
console.log(err);
} else {
if(row) {
let msg = {
msg_id: mes.msg_id,
user_id: row.user_id,
status: 'ok'
}
let outMessage = api_out + JSON.stringify(msg);
console.log(outMessage);
subSocket.send(outMessage);
} else {
let msg = {
msg_id: mes.msg_id,
status: 'error',
error: mes.email == '' || mes.pwd == '' ? errorWrongFormat : errorWrongPWD
}
console.log(msg);
let outMessage = api_out + JSON.stringify(msg);
subSocket.send(outMessage);
}
}
});
}
});
subSocket.bindSync(`tcp://127.0.0.1:${args['sub']}`);
client
let zmq = require('zeromq');
let uniqid = require('uniqid');
let readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
const args = require('minimist')(process.argv.slice(2));
const pubSocket = zmq.socket('pub', null);
let pubSocketTCP = `tcp://127.0.0.1:${args['sub']}`;
pubSocket.connect(pubSocketTCP);
const subSocket = zmq.socket('sub', null);
let subSocketTCP = `tcp://127.0.0.1:${args['pub']}`;
subSocket.connect(subSocketTCP);
let api_in = 'api_in';
let secondFrame = {
type: 'login',
email: '',
pwd: '',
msg_id: uniqid()
}
readline.question('What is your email? \n', (email) => {
secondFrame.email = email;
readline.question('What is your password \n', (pwd) => {
secondFrame.pwd = pwd;
let msg = api_in + JSON.stringify(secondFrame);
console.log(msg);
pubSocket.send(msg);
});
});
subSocket.subscribe('api_out');
subSocket.on('message', (response) => {
/* let res = response.toString().replace('api_out');
let responseParsed = JSON.parse(res);
console.log(responseParsed.status);
if(response.status == 'error') console.log(response.error); */
console.log(response);
});
I want the server side to send info back.
Well, first of all, welcome to the Zen-of-Zero domain. ZeroMQ is a powerful tool for smart signaling / messaging, so if you pay attention to all its internal beauties, there will be no such thing you will not be able to do with it ( nightmares still not avoided on this way forward ). If feeling to be new to this domain, one may enjoy a short read into "ZeroMQ Principles in less than Five Seconds" before diving into further details on subject, or re-use some of tricks posted here
Q : it doesn't seem to send anything back to the client (subscriber) and I don't know why
There are two pieces of code, that seem to use both PUB and SUB Scalable Formal Communication Pattern Archetypes, yet have some glitches on how these get configured.
The server-code :
seems to try to instantiate PUB-archetype and equips that instance with a single AccessPoint, using .bindSync()-method and cli-parameter args['pub'] for accepting connections over a plain and commontcp://-transport-class.
After defining the event-handler .on( 'message', ... ) for the second instance, being the SUB-archetype, this one becomes .bindSync()-equipped with a single AccessPoint, using tcp://-transport-class, for receiving connections using tcp://-transport-class.
If you indeed have to make a .send() over a SUB-alike archetype, you have to use XSUB alternative, where you can send data and perform some tricks with the actual payload on the PUB-side or XPUB-side ( ref. API documentation for details about ZMQ_XPUB_MANUAL mode capabilities and limits for some wilder data-mangling on the XPUB-side )
ZMQ_XSUB
Same as ZMQ_SUB except that you subscribe by sending subscription messages to the socket. Subscription message is a byte 1 (for subscriptions) or byte 0 (for unsubscriptions) followed by the subscription body. Messages without a sub/unsub prefix may also be sent, but have no effect on subscription status.
The client-code :
Seems to instantiate and .connect() both the client-local PUB and SUB Archetypes over tcp://-transport-class to the server-side AccessPoints ( which both ought to set ZMQ_LINGER to 0, so as to avoid infinitely hanging orphans ( version dependent defaults do not have other solution but an explicit setting on this ) ).
Possible improvements :
XPUB/XSUB with ZMQ_XPUB_MANUAL may solve the sending via SUB-archetype
XPUB/SUB with ZMQ_XPUB_MANUAL may solve the sending via SUB-archetype with less comfort of masquerading the messages to be sent via the .subscribe()-method
PUB/SUB with making all .send()-s strictly via a local PUB-archetype instance.
be explicit with ZMQ_SNDHWM and ZMQ_RCVHWM parameters if still loosing messages
be explicit on setting .subscribe() only after a successfully completed { .bind() + .connect() }-methods ( systematically using original zmq_errno() and zmq_strerror() functions for actively detecting and repairing the potential colliding states )
may request to work with only completed connections ( distributed systems have zero-warranty of an order of operations autonomous distributed (many) agents perform ) using .setsockopt( ZMQ_IMMEDIATE, 1 )
Your server is trying to send on the sub socket
subSocket.send(outMessage);
You can't send on the sub socket. It should be sending on a pub socket.
This is a long post, so I appreciate those who answer it. I am trying to understand the websocket communication in the blockchain example below.
Here is the source code for a node in a blockchain:
const BrewChain = require('./brewChain');
const WebSocket = require('ws');
const BrewNode = function(port){
let brewSockets = [];
let brewServer;
let _port = port
let chain = new BrewChain();
const REQUEST_CHAIN = "REQUEST_CHAIN";
const REQUEST_BLOCK = "REQUEST_BLOCK";
const BLOCK = "BLOCK";
const CHAIN = "CHAIN";
function init(){
chain.init();
brewServer = new WebSocket.Server({ port: _port });
brewServer.on('connection', (connection) => {
console.log('connection in');
initConnection(connection);
});
}
const messageHandler = (connection) =>{
connection.on('message', (data) => {
const msg = JSON.parse(data);
switch(msg.event){
case REQUEST_CHAIN:
connection.send(JSON.stringify({ event: CHAIN, message: chain.getChain()}))
break;
case REQUEST_BLOCK:
requestLatestBlock(connection);
break;
case BLOCK:
processedRecievedBlock(msg.message);
break;
case CHAIN:
processedRecievedChain(msg.message);
break;
default:
console.log('Unknown message ');
}
});
}
const processedRecievedChain = (blocks) => {
let newChain = blocks.sort((block1, block2) => (block1.index - block2.index))
if(newChain.length > chain.getTotalBlocks() && chain.checkNewChainIsValid(newChain)){
chain.replaceChain(newChain);
console.log('chain replaced');
}
}
const processedRecievedBlock = (block) => {
let currentTopBlock = chain.getLatestBlock();
// Is the same or older?
if(block.index <= currentTopBlock.index){
console.log('No update needed');
return;
}
//Is claiming to be the next in the chain
if(block.previousHash == currentTopBlock.hash){
//Attempt the top block to our chain
chain.addToChain(block);
console.log('New block added');
console.log(chain.getLatestBlock());
}else{
// It is ahead.. we are therefore a few behind, request the whole chain
console.log('requesting chain');
broadcastMessage(REQUEST_CHAIN,"");
}
}
const requestLatestBlock = (connection) => {
connection.send(JSON.stringify({ event: BLOCK, message: chain.getLatestBlock()}))
}
const broadcastMessage = (event, message) => {
brewSockets.forEach(node => node.send(JSON.stringify({ event, message})))
}
const closeConnection = (connection) => {
console.log('closing connection');
brewSockets.splice(brewSockets.indexOf(connection),1);
}
const initConnection = (connection) => {
console.log('init connection');
messageHandler(connection);
requestLatestBlock(connection);
brewSockets.push(connection);
connection.on('error', () => closeConnection(connection));
connection.on('close', () => closeConnection(connection));
}
const createBlock = (teammember) => {
let newBlock = chain.createBlock(teammember)
chain.addToChain(newBlock);
broadcastMessage(BLOCK, newBlock);
}
const getStats = () => {
return {
blocks: chain.getTotalBlocks()
}
}
const addPeer = (host, port) => {
let connection = new WebSocket(`ws://${host}:${port}`);
connection.on('error', (error) =>{
console.log(error);
});
connection.on('open', (msg) =>{
initConnection(connection);
});
}
return {
init,
broadcastMessage,
addPeer,
createBlock,
getStats
}
}
module.exports = BrewNode;
When a new block is created by the node with the createBlock() function, a message is broadcast from the node to all connected sockets with the broadcastMessage() function to tell them a new block has been created. The connected sockets will receive the message, and in messageHandler() it will hit the BLOCK option in the switch statement for each of them. I have a grasp of this process, and have drawn up a graph to show my understanding.
FIGURE 1
As stated earlier, when A creates a new block it will send the new block to it's connected nodes, where each node will verify it and possibly add it to it's chain. This processing is done by the processedRecievedBlock() function. Let's say B and C decide to add the block to their chain, but D is several blocks behind so it must request the whole chain from A. This is where I am confused. I expected that D would send a message back to A requesting the whole chain, like this:
FIGURE 2
However, according to the processReceivedBlock() function, in this situation D will broadcast a REQUEST_CHAIN message to all it's connected sockets, when this line is run:
broadcastMessage(REQUEST_CHAIN,"");
Let's say D is connected to E and F. Instead of requesting the chain from A like in FIGURE 2, it seems as though it will send the REQUEST_CHAIN message to it's connected sockets, like this:
FIGURE 3
In the messageHandler() function, the REQUEST_CHAIN option in the switch statement will be run for E and F, and they will hit this line of code:
connection.send(JSON.stringify({ event: CHAIN, message: chain.getChain()}));
It is my understanding that this will cause E and F to send their own chain back to themselves, like this:
FIGURE 4
I want to know why FIGURE 2 does not occur when D needs to request the whole chain from A. Tracing the code has had me believe that FIGURE 3 and FIGURE 4 occur instead, neither of which seem to be useful.
I am trying to find an understanding of what exactly happens in this code when a node must request the whole chain from another node. I must be misunderstanding what these sockets are doing.
Complete source code: https://github.com/dbjsdev/BrewChain/blob/master/brewNode.js
Thanks for a descriptive question. :)
You are right for the most part and Figure 3 is the correct description of that part of the process. But Figure 4 is wrong.
Note that every socket connection between peers leads to a different instance of connection which are collectively maintained in brewSockets.
So, when A/E/F receive a request on connection from D, they respond with the whole chain, as in the code below:
connection.send(JSON.stringify({ event: CHAIN, message: chain.getChain()}));
D then processes the CHAIN message:
const processedRecievedChain = (blocks) => {
let newChain = blocks.sort((block1, block2) => (block1.index - block2.index))
if(newChain.length > chain.getTotalBlocks() && chain.checkNewChainIsValid(newChain)){
chain.replaceChain(newChain);
console.log('chain replaced');
}
}
Now, onto the 'why'!
Firstly, the underlying principle is that we trust in the network, not just one node. So, you want to verify the authenticity of the chain from as many sources as possible.
Secondly, you want the latest chain from your peers not just any random chain.
By doing so, we ensured that any node is as latest as its peers. So, D node fetched chain from multiple sources and stored the latest verified chain.
Hope that helps!
I've got an app I'm writing in React Native. It's socketed and I have a file that controls all socket information.
import {Alert, AppState} from 'react-native';
import store from '../store/store';
import {updateNotifications} from '../reducers/notifications';
import {setError, clearError} from '../reducers/error';
import {updateCurrentEvent, updateEventStatus, setCurrentEvent} from '../reducers/event_details';
import {setAlert} from '../reducers/alert';
import {ws_url} from '../api/urls'
let conn = new WebSocket(ws_url);
/*
handleSocketConnections handles any actions that require rerouting. The rest are passed off to handleOnMessage
This is being called from authLogin on componentDidMount. It would be ideal to only initialize a socket conn
when a user logs in somehow, but this package gets ran when a user opens the app, meaning there are socket
connections that don't need to exist yet.
*/
function setAppStateHandler() {
AppState.addEventListener('change', cstate => {
if(cstate === 'active') {
reconnect()
}
})
}
export const handleSocketConnections = (navigator, route) => {
setAppStateHandler();
conn.onmessage = e => {
const state = store.getState();
const msg = JSON.parse(e.data);
const { type, payload, event_id } = msg;
const { event } = state.event_details.event_details;
if (type == "SET_EVENT_STATUS" && payload == "CLOSED" && event_id == event.event_id) {
navigator.push(route)
// store.dispatch(setAlert({
// message:"Event is closed, click to navigate to checkout."
// , scene: null
// }))
store.dispatch(updateEventStatus(payload));
} else {
handleOnMessage(msg, state)
}
}
}
export function reconnect() {
//TODO: Fatal errors should redirect the mainNav to a fatal error screen. Not dismount the nav entirely, as it does now
//and this should pop the error screen when it's fixed.
let state = store.getState();
conn = new WebSocket(ws_url);
setTimeout(function () {
if (conn.readyState == 1) {
if (typeof state.event_details.event_details != 'undefined') {
setSocketedEventInfo(state.event_details.event_details.event.event_id);
}
store.dispatch(clearError());
} else {
store.dispatch(setError('fatal',`Socket readyState should be 1 but it's ${conn.readyState}`))
}
}, 1000);
}
//Preform function on ES close.
conn.onclose = e => {
console.log("Closing wsbidder, ", `${e.code} -- ${e.reason}`);
//TODO: Set error here saying they need to restart the app. Maybe a 'reconnect' somehow?
//Maybe set a store variable to socketErr and if null, all is good. Else, panic the app?
//Use Case: Server is not started and user tries to connect to the app. String of e.message contains "Connection refused"
store.dispatch(setError("fatal", `Socket onclose: ${e.code} -- ${e.reason}`))
};
conn.onerror = e => {
console.log("Error at socket, ", e);
store.dispatch(setError("fatal", `Socket onerror: ${e.message}`))
};
//Initialization function for websocket.
// conn.onopen = e => console.log("Opening wsbidder, ", e)
function handleOnMessage(msg, state) {
switch (msg.type) {
//These types come from the SocketWrappers on the server.
//updateCurrentEvent should be filtering the event by event_id.
case "EVENT_ITEMS":
store.dispatch(updateCurrentEvent(
msg.payload
, state.user_info.uid
, state.event_details.event_details.event.event_id));
break;
case "NOTIFICATIONS":
//bug: this needs to filter notifications per event on the client-side.
store.dispatch(updateNotifications(
msg.payload
, state.event_details.event_details.event.event_id
, state.user_info.uid)
);
break;
case "NOT_BIDDABLE":
if (msg.event_id == state.event_details.event_details.event.event_id) {
store.dispatch(updateEventStatus("CLOSED"));
}
break;
case "PUSH_NOTIFICATION":
const {title, message} = msg.payload;
Alert.alert(title, message);
break;
default:
console.warn(`Unrecognized socket action type: ${msg.type}`);
}
}
//closes the socket connection and sends a reason to the server.
export const closeConn = reason => conn.close(null, reason);
export const setSocketedEventInfo = event_id => {
//Gives the event ID to the socketed connection, which pulls end dates.
const msg = {
type: "UPDATE_EVENT_DETAILS"
, payload: { event_id }
}
conn.send(JSON.stringify(msg));
}
export const createBid = (bid, cb) => {
/*
Expects:
const new_bid = {
item_id: item.item_id,
bid: amount, //Storage keeps storing it as a string
uid: 0, //Not needed here, but can't be null since the server wants an int.
event_id, key, bidder
};
*/
const new_bid = {
type: 'BID'
, payload: bid
};
// Send this to the server socket
conn.send(JSON.stringify(new_bid));
//Returning the callback so the front-end knows to flip the card back over.
return cb()
};
Some of the code is crap, I know. Unless you're giving true advice, which I'm always glad to follow, no need to bash it :-)
The issue I'm having is that when the socket dies (the conn variable), I can't re-initialize the socket and assign it to that conn variable. What I think is happening is all functions using the conn variable aren't using the 'new' one, still stuck to the 'old' one.
Line 9 -- Creating the original one.
Line 28 -- Creating an onMessage function for the conn object, within the handleSocketConnections function that gets called elsewhere at the start of the program
Line 57 -- Trying to re-assign a new connection to the conn variable in the reconnect function, that gets run whenever the app goes on standby (killing the socket connections).
Line 131 -- This gets called correctly from the reconnect function, connecting the socket to the server again
The reconnect() function runs correctly - the server registers the new connection with all the right info, but the app seems to still be in a weird state where there's no conn error (possibly looking at the new one??) but no actions are formed on the conn (possibly looking at the old one?).
Any ideas?
If you have to start a replacement webSocket connection, then you will need to rerun all the code that hooks up to the webSocket (installs event handlers, etc...). Because it's a new object, the old event listeners aren't associated with the new webSocket object.
The simplest way to do that is usually to create a single webSocketInit() function that you call both when you first create your webSocket connection and then call again any time you have to replace it with a new one. You can pass the latest webSocket object to webSocketInit() so any other code can see the new object. Individual blocks of code can register for onclose themselves if they want to know when the old one closes.
There are also more event-driven ways to do this by creating an EventEmitter that gets notified whenever the webSocket has been replaced and individual blocks of code can subscribe to that event if they want to get notified of that occurrence.
I am trying to send a Push Notification through Parse Cloud Code when a certain object has been modified - "dirty"
I think I am almost there, but received an error because I believe am creating a new user instead of querying for one.
Parse.Cloud.beforeSave("Fact", function(request, response) {
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "isValid") {
//send push
// Creates a pointer to _User with object id of userId
var targetUser = new Parse.User();
// targetUser.id = userId;
targetUser.id = request.object.userID;
var query = new Parse.Query(Parse.Installation);
query.equalTo('user', targetUser);
Parse.Push.send({
where: query,
data: {
alert: "Your Fact was approved :)"
}
});
return;
}
}
response.success();
});
I found this post related to my problem. My question now is how to integrate the user query in my beforeSave block. Ideally I would create another function for the user query and place that in my beforeSave block.
**5/14 Update
I took #toddg's advice and fixed the before save. Here is a clearer picture of what I am trying to do and the new error.
A couple points (as #Subash noted in the comments) before I get into the code:
Parse.Push.send is an async operation, so you'll want to ensure you call response.success() after your push send completes. I'm going to deal with this using Promises, as I think they are more flexible than callbacks. If you're not familiar, read about them here
The return in your if statement will likely prevent the response.success() from being called.
Here's my recommended way of doing it:
Parse.Cloud.beforeSave("Fact", function(request, response) {
// Keep track of whether we need to send the push notification
var shouldPushBeSent = false;
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "isValid") {
shouldPushBeSent = true;
}
}
if (shouldPushBeSent) {
//send push
// Creates a pointer to _User with object id of userId
var targetUser = new Parse.User();
// targetUser.id = userId;
targetUser.id = request.object.userId;
var query = new Parse.Query(Parse.Installation);
// We want to pass the User object to the query rather than the UserId
query.equalTo('user', targetUser);
Parse.Push.send({
where: query, // Set our Installation query
data: {
alert: "Your fact was approved"
}
}).then(function(){
// Now we know the push notification was successfully sent
response.success();
}, function(error){
// There was an error sending the push notification
response.error("We had an error sending push: " + error);
});
} else {
// We don't need to send the push notification.
response.success();
}
});
By the way, I'm assuming that you have a column on your Installation class that tracks which user is associated with each Installation.
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.