I have this test code that listens for messages forever. If it gets one it prints it out. What I want to do is avoid having to construct a MQMessage object prior to each get(). How do i reuse a MQMessage for multiple calls to get()?
using System;
using IBM.WMQ;
namespace WMQ {
class Program {
static void Main(string[] args) {
string QueueManagerName = "A1PATA00";
string channelName = "ECACHE";
string connectionName = "A1PATA00.WORLDSPAN.COM(1414)";
var queueManager = new MQQueueManager(QueueManagerName, channelName, connectionName);
MQQueue get = queueManager.AccessQueue("SPLASH.ECAC.2", MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQGMO_FAIL_IF_QUIESCING);
var gmo = new MQGetMessageOptions();
gmo.Options = MQC.MQGMO_FAIL_IF_QUIESCING | MQC.MQGMO_WAIT;
gmo.WaitInterval = 10000000;// wait time
// var queueMessage = new MQMessage(); <--- i want to do this new once!
while (true) {
var queueMessage = new MQMessage(); // <-- only works if I do this every time i do a get
get.Get(queueMessage, gmo);
var strReturn = queueMessage.ReadString(queueMessage.MessageLength);
Console.WriteLine(strReturn);
}
}
}
}
When I see questions like this, I shake my head. You do not
understand object oriented concepts in the .NET VM (framework) and
garbage collection.
Also, why don't you write pure C# code?
Finally, in a world where security is very important, your code does
not support either SSL/TLS and/or UserID & Password authentication.
(1) Please notice where I am defining the MQMessage object (very important).
There is basically no difference in memory usage or speed between the following 2 code snippets:
(A)
MQMessage msg = null;
while (true)
{
msg = new MQMessage();
get.Get(msg, gmo);
Console.WriteLine(msg.ReadString(msg.MessageLength));
}
(B)
MQMessage msg = new MQMessage();
while (true)
{
get.Get(msg, gmo);
Console.WriteLine(msg.ReadString(msg.MessageLength));
msg.ClearMessage();
msg.MessageId = MQC.MQMI_NONE;
msg.CorrelationId = MQC.MQCI_NONE;
}
I prefer (A) because it is cleaner and easier to read.
(2) When you use 'var', you are forcing the .NET framework to guess at what you are doing. Do pure object oriented programming.
i.e.
MQMessage msg = new MQMessage();
(3) Explicitly setting the channel name and connection name in the MQQueueManager does not allow setting of MQ security information. Also, do NOT use the MQEnvironment class as it is NOT thread safe. It is far better to put all the information in a Hashtable and pass the Hashtable to the MQQueueManager class. Here is a MQ .NET managed-mode example using a Hashtable for MQ connection information:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using IBM.WMQ;
namespace MQTest02
{
class MQTest02
{
private Hashtable inParms = null;
private Hashtable qMgrProp = null;
private System.String qManager;
private System.String outputQName;
/*
* The constructor
*/
public MQTest02()
: base()
{
}
/// <summary> Make sure the required parameters are present.</summary>
/// <returns> true/false
/// </returns>
private bool allParamsPresent()
{
bool b = inParms.ContainsKey("-h") && inParms.ContainsKey("-p") &&
inParms.ContainsKey("-c") && inParms.ContainsKey("-m") &&
inParms.ContainsKey("-q");
if (b)
{
try
{
System.Int32.Parse((System.String)inParms["-p"]);
}
catch (System.FormatException e)
{
b = false;
}
}
return b;
}
/// <summary> Extract the command-line parameters and initialize the MQ variables.</summary>
/// <param name="args">
/// </param>
/// <throws> IllegalArgumentException </throws>
private void init(System.String[] args)
{
inParms = Hashtable.Synchronized(new Hashtable());
if (args.Length > 0 && (args.Length % 2) == 0)
{
for (int i = 0; i < args.Length; i += 2)
{
inParms[args[i]] = args[i + 1];
}
}
else
{
throw new System.ArgumentException();
}
if (allParamsPresent())
{
qManager = ((System.String)inParms["-m"]);
outputQName = ((System.String)inParms["-q"]);
qMgrProp = new Hashtable();
qMgrProp.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED);
qMgrProp.Add(MQC.HOST_NAME_PROPERTY, ((System.String)inParms["-h"]));
qMgrProp.Add(MQC.CHANNEL_PROPERTY, ((System.String)inParms["-c"]));
try
{
qMgrProp.Add(MQC.PORT_PROPERTY, System.Int32.Parse((System.String)inParms["-p"]));
}
catch (System.FormatException e)
{
qMgrProp.Add(MQC.PORT_PROPERTY, 1414);
}
if (inParms.ContainsKey("-u"))
qMgrProp.Add(MQC.USER_ID_PROPERTY, ((System.String)inParms["-u"]));
if (inParms.ContainsKey("-x"))
qMgrProp.Add(MQC.PASSWORD_PROPERTY, ((System.String)inParms["-x"]));
if (inParms.ContainsKey("-s"))
qMgrProp.Add(MQC.SECURITY_EXIT_PROPERTY, ((System.String)inParms["-s"]));
System.Console.Out.WriteLine("MQTest02:");
Console.WriteLine(" QMgrName ='{0}'", qManager);
Console.WriteLine(" Output QName ='{0}'", outputQName);
System.Console.Out.WriteLine("QMgr Property values:");
foreach (DictionaryEntry de in qMgrProp)
{
Console.WriteLine(" {0} = '{1}'", de.Key, de.Value);
}
}
else
{
throw new System.ArgumentException();
}
}
/// <summary> Connect, open queue, read a message, close queue and disconnect.
///
/// </summary>
/// <throws> MQException </throws>
private void testReceive()
{
MQQueueManager qMgr = null;
MQQueue queue = null;
int openOptions = MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING;
MQGetMessageOptions gmo = new MQGetMessageOptions();
MQMessage receiveMsg = null;
try
{
qMgr = new MQQueueManager(qManager, qMgrProp);
System.Console.Out.WriteLine("MQTest02 successfully connected to " + qManager);
queue = qMgr.AccessQueue(outputQName, openOptions, null, null, null); // no alternate user id
System.Console.Out.WriteLine("MQTest02 successfully opened " + outputQName);
receiveMsg = new MQMessage();
queue.Get(receiveMsg, gmo);
System.Console.Out.WriteLine("Message Data>>>" + receiveMsg.ReadString(receiveMsg.MessageLength));
}
catch (MQException mqex)
{
System.Console.Out.WriteLine("MQTest02 cc=" + mqex.CompletionCode + " : rc=" + mqex.ReasonCode);
}
catch (System.IO.IOException ioex)
{
System.Console.Out.WriteLine("MQTest02 ioex=" + ioex);
}
finally
{
try
{
queue.Close();
System.Console.Out.WriteLine("MQTest02 closed: " + outputQName);
}
catch (MQException mqex)
{
System.Console.Out.WriteLine("MQTest02 cc=" + mqex.CompletionCode + " : rc=" + mqex.ReasonCode);
}
try
{
qMgr.Disconnect();
System.Console.Out.WriteLine("MQTest02 disconnected from " + qManager);
}
catch (MQException mqex)
{
System.Console.Out.WriteLine("MQTest02 cc=" + mqex.CompletionCode + " : rc=" + mqex.ReasonCode);
}
}
}
/// <summary> main line</summary>
/// <param name="args">
/// </param>
// [STAThread]
public static void Main(System.String[] args)
{
MQTest02 mqt = new MQTest02();
try
{
mqt.init(args);
mqt.testReceive();
}
catch (System.ArgumentException e)
{
System.Console.Out.WriteLine("Usage: MQTest02 -h host -p port -c channel -m QueueManagerName -q QueueName [-u userID] [-x passwd] [-s securityExit]");
System.Environment.Exit(1);
}
catch (MQException e)
{
System.Console.Out.WriteLine(e);
System.Environment.Exit(1);
}
System.Environment.Exit(0);
}
}
}
To run MQTest02 using your information, it would be:
MQTest02.exe -h A1PATA00.WORLDSPAN.COM -p 1414 -m A1PATA00 -c ECACHE -q SPLASH.ECAC.2
In the IBM MQ Knowledge center page "ClearMessage method" it documents the following:
This method clears the data buffer portion of the MQMessage object.
Any Message Data in the data buffer is lost, because MessageLength,
DataLength, and DataOffset are all set to zero.
The Message Descriptor (MQMD) portion is unaffected; an application
might need to modify some of the MQMD fields before reusing the
MQMessage object. To set the MQMD fields back use New to replace the
object with a new instance.
In the IBM MQ Knowledge center page "MQMessage .NET class" it documents the following:
public byte[] MessageId {get; set;}
For an MQQueue.Get call, this field specifies the message identifier
of the message to be retrieved. Normally, the queue manager returns
the first message with a message identifier and correlation identifier
that match the message descriptor fields. Allow any message identifier
to match using the special value MQC.MQMI_NONE.
public byte[] CorrelationId {get; set;}
For an MQQueue.Get call, the correlation identifier of the message to
be retrieved. The queue manager returns the first message with a
message identifier and a correlation identifier that match the message
descriptor fields. The default value, MQC.MQCI_NONE, helps any
correlation identifier to match.
Try this:
var queueMessage = new MQMessage(); <--- i want to do this new once!
while (true) {
//var queueMessage = new MQMessage(); // <-- only works if I do this every time i do a get
queueMessage.ClearMessage();
queueMessage.MessageId = MQC.MQMI_NONE;
queueMessage.CorrelationId = MQC.MQCI_NONE;
get.Get(queueMessage, gmo);
Related
We have a legacy C# Windows Service application (.Net Framework 4.8) that performs some statistical analysis which usually takes hours to complete as the underlying database has millions of rows of historical data.
It works fine if there is no underlying network interruption. However, we recently started using a database which is only accessible over the VPN. Now if there is any VPN connection issue, the analysis stops.
Is there any way to recover from these transient faults silently and gracefully and continue the work?
For the SqlClient I have found a sample code at https://learn.microsoft.com/en-us/sql/connect/ado-net/step-4-connect-resiliently-sql-ado-net?view=sql-server-ver15
The sample code from the link is shown below (just in case if the link dies).
using System; // C#
using CG = System.Collections.Generic;
using QC = Microsoft.Data.SqlClient;
using TD = System.Threading;
namespace RetryAdo2
{
public class Program
{
static public int Main(string[] args)
{
bool succeeded = false;
int totalNumberOfTimesToTry = 4;
int retryIntervalSeconds = 10;
for (int tries = 1;
tries <= totalNumberOfTimesToTry;
tries++)
{
try
{
if (tries > 1)
{
Console.WriteLine
("Transient error encountered. Will begin attempt number {0} of {1} max...",
tries, totalNumberOfTimesToTry
);
TD.Thread.Sleep(1000 * retryIntervalSeconds);
retryIntervalSeconds = Convert.ToInt32
(retryIntervalSeconds * 1.5);
}
AccessDatabase();
succeeded = true;
break;
}
catch (QC.SqlException sqlExc)
{
if (TransientErrorNumbers.Contains
(sqlExc.Number) == true)
{
Console.WriteLine("{0}: transient occurred.", sqlExc.Number);
continue;
}
else
{
Console.WriteLine(sqlExc);
succeeded = false;
break;
}
}
catch (TestSqlException sqlExc)
{
if (TransientErrorNumbers.Contains
(sqlExc.Number) == true)
{
Console.WriteLine("{0}: transient occurred. (TESTING.)", sqlExc.Number);
continue;
}
else
{
Console.WriteLine(sqlExc);
succeeded = false;
break;
}
}
catch (Exception Exc)
{
Console.WriteLine(Exc);
succeeded = false;
break;
}
}
if (succeeded == true)
{
return 0;
}
else
{
Console.WriteLine("ERROR: Unable to access the database!");
return 1;
}
}
/// <summary>
/// Connects to the database, reads,
/// prints results to the console.
/// </summary>
static public void AccessDatabase()
{
//throw new TestSqlException(4060); //(7654321); // Uncomment for testing.
using (var sqlConnection = new QC.SqlConnection
(GetSqlConnectionString()))
{
using (var dbCommand = sqlConnection.CreateCommand())
{
dbCommand.CommandText = #"
SELECT TOP 3
ob.name,
CAST(ob.object_id as nvarchar(32)) as [object_id]
FROM sys.objects as ob
WHERE ob.type='IT'
ORDER BY ob.name;";
sqlConnection.Open();
var dataReader = dbCommand.ExecuteReader();
while (dataReader.Read())
{
Console.WriteLine("{0}\t{1}",
dataReader.GetString(0),
dataReader.GetString(1));
}
}
}
}
/// <summary>
/// You must edit the four 'my' string values.
/// </summary>
/// <returns>An ADO.NET connection string.</returns>
static private string GetSqlConnectionString()
{
// Prepare the connection string to Azure SQL Database.
var sqlConnectionSB = new QC.SqlConnectionStringBuilder();
// Change these values to your values.
sqlConnectionSB.DataSource = "tcp:myazuresqldbserver.database.windows.net,1433"; //["Server"]
sqlConnectionSB.InitialCatalog = "MyDatabase"; //["Database"]
sqlConnectionSB.UserID = "MyLogin"; // "#yourservername" as suffix sometimes.
sqlConnectionSB.Password = "MyPassword";
sqlConnectionSB.IntegratedSecurity = false;
// Adjust these values if you like. (ADO.NET 4.5.1 or later.)
sqlConnectionSB.ConnectRetryCount = 3;
sqlConnectionSB.ConnectRetryInterval = 10; // Seconds.
// Leave these values as they are.
sqlConnectionSB.IntegratedSecurity = false;
sqlConnectionSB.Encrypt = true;
sqlConnectionSB.ConnectTimeout = 30;
return sqlConnectionSB.ToString();
}
static public CG.List<int> TransientErrorNumbers =
new CG.List<int> { 4060, 40197, 40501, 40613,
49918, 49919, 49920, 11001 };
}
/// <summary>
/// For testing retry logic, you can have method
/// AccessDatabase start by throwing a new
/// TestSqlException with a Number that does
/// or does not match a transient error number
/// present in TransientErrorNumbers.
/// </summary>
internal class TestSqlException : ApplicationException
{
internal TestSqlException(int testErrorNumber)
{ this.Number = testErrorNumber; }
internal int Number
{ get; set; }
}
}
However, I couldn't find any helpful material for the OracleClient. Any ideas, please?
I'm having some difficulty setting up a WCF interface. I can connect fine, but as soon as I start sending certain data, I get the following exception:
The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '01:39:59.9084817'.
From looking at a few other questions, I found that this could be due to inefficient timeout. So I increased the following parameters to 100 minutes just for kicks.
tcpBinding.ReceiveTimeout = TimeSpan.FromMinutes(100);
tcpBinding.OpenTimeout = TimeSpan.FromMinutes(100);
tcpBinding.SendTimeout = TimeSpan.FromMinutes(100);
However, this exception is thrown the same instant a certain function is called so I don't think that is the problem. The peculiar thing though is that the exception always shows a time almost the same as whatever I put into those parameters, regardless of how much time actually passed.
I found that I can send the standard types like object, string, int, etc but whenever I try to send a custom type, this error is thrown. That is telling me it might be a bad reference somewhere. Does this make sense?
UPDATE:
Had a chance to upload the code.
Here is the client side:
namespace GenericWCF
{
/// <summary>
/// TODO: Update summary.
/// </summary>
[ServiceContract()]
public interface SimWCFInterface
{
[OperationContract()]
void SimpleMessage(string message);
[OperationContract()]
List<CustomObj> GetDataPoints();
}
}
and the server side:
namespace DifferentNamespace
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// TODO: Update summary.
/// </summary>
[ServiceContract()]
public interface SimWCFInterface
{
[OperationContract()]
void SimpleMessage(string message);
[OperationContract()]
List<CustomObj> GetDataPoints();
}
and the actual implementation
namespace DifferentNamespace
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// TODO: Update summary.
/// </summary>
[ServiceContract()]
public interface SimWCFInterface
{
public void SimpleMessage(string message)
{
Debug.WriteLine("This is a simple message wcf");
MessageBox.Show(message);
}
public List<CustomObj> GetDataPoints()
{
List<CustomObj> points = new List<CustomObj>();
return points;
}
}
So there really isn't anything special going on I don't think. SimpleMessage runs fine no problem. If I change GetDataPoints to return a regular object, it works fine as well. The problem only occurs when I return that CustomObj. One thing I noticed just now is that client and server side live in different namespaces, so could that be an issue? Also, I'm using a tcp conneciton. I'll post that code in a moment.
EDIT:
The tcp connection code:
client
private string endPointAddress;
private NetTcpBinding tcpBinding;
public void Connect(string ipAddress, string portNumber, string interfaceName)
{
try
{
interfaceName = interfaceName.Substring(interfaceName.LastIndexOf(".") + 1);
endPointAddress = "net.tcp://" + ipAddress + ":" + portNumber + "/" + interfaceName;
tcpBinding = new NetTcpBinding();
tcpBinding.TransactionFlow = false;
tcpBinding.Security.Transport.ProtectionLevel = ProtectionLevel.EncryptAndSign;
tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
tcpBinding.Security.Mode = SecurityMode.None;
tcpBinding.ReceiveTimeout = TimeSpan.FromMinutes(100);
tcpBinding.OpenTimeout = TimeSpan.FromMinutes(100);
tcpBinding.SendTimeout = TimeSpan.FromMinutes(100);
tcpBinding.ReliableSession.Ordered = false;
tcpBinding.ReliableSession.InactivityTimeout = TimeSpan.MaxValue;
tcpBinding.MaxReceivedMessageSize = 10000000;
tcpBinding.MaxBufferPoolSize = 10000000;
tcpBinding.MaxBufferSize = 10000000;
EndpointAddress endpointAddress = new EndpointAddress(endPointAddress);
_SimChannelFactory = new ChannelFactory<SimWCFInterface>(tcpBinding, endpointAddress);
_SimWCFInterface = _SimChannelFactory.CreateChannel();
//test connection...working
_SimWCFInterface.SimpleMessage("what a message");
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
and the server
static public void CreateSimHost(string IPAdd, string port, Type interfaceType, Type serviceType)
{
try
{
string interfaceName = interfaceType.ToString();
interfaceName = interfaceName.Substring(interfaceName.LastIndexOf(".") + 1);
// Create the url that is needed to specify where the service should be started
urlService = "net.tcp://" + IPAdd + ":" + port + "/" + interfaceName;
host = new ServiceHost(serviceType);
// The binding is where we can choose what transport layer we want to use. HTTP, TCP ect.
tcpBinding = new NetTcpBinding
{
TransactionFlow = false
};
tcpBinding.Security.Transport.ProtectionLevel = ProtectionLevel.EncryptAndSign;
tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
tcpBinding.Security.Mode = SecurityMode.None; // <- Very crucial
tcpBinding.ReceiveTimeout = TimeSpan.MaxValue;
tcpBinding.SendTimeout = TimeSpan.MaxValue;
tcpBinding.ReliableSession.Ordered = false;
tcpBinding.ReliableSession.InactivityTimeout = TimeSpan.MaxValue;
// Add a endpoint
host.AddServiceEndpoint(interfaceType, tcpBinding, urlService);
host.Open();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
I read the messages from one queue to another queue. However my correlation ids are not preserved.
If the correlation id is "ABC12345" for a message in the import queue, when i put it into the export queue, the value of the correlation id is different.
How do i keep the same correlation id between the 2 queues and always
have a unique message id?
Get:
mqQueue.Get(mqMsg);
string messageID = Convert.ToString(mqMsg.MessageId);
string correlationID = Convert.ToString(mqMsg.CorrelationId);
If for example if the correlation id is "000123456789", then after read, while putting it back , the value gets changed for the same message.
Put:
mqMsg.CorrelationId = System.Text.Encoding.UTF8.GetBytes(correlationID);
mqQueue.Put(mqMsg, mqPutMsgOpts);
I am using MQ PUT and GET options via MQ.NET classes.
The code snippet below preserves the correlation id when I put message to another queue. In my sample I do the following:
1) Put a message to importQ with unique correlation ID.
2) Get that message from importQ.
3) Put the received message to exportQ
public static void preserveCorreLid()
{
Hashtable mqProps = new Hashtable();
MQQueueManager qm = null;
String strCorrelId = "00123456789";
try
{
mqProps.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED);
mqProps.Add(MQC.CHANNEL_PROPERTY, "NET.CLIENT.CHL");
mqProps.Add(MQC.HOST_NAME_PROPERTY, "localhost");
mqProps.Add(MQC.PORT_PROPERTY, 2099);
qm = new MQQueueManager("QM", mqProps);
MQQueue importQ = qm.AccessQueue("IMPORTQ", MQC.MQOO_INPUT_SHARED |MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING );
MQMessage mqPutMsg = new MQMessage();
mqPutMsg.WriteString("This is an import message");
mqPutMsg.CorrelationId = System.Text.Encoding.UTF8.GetBytes(strCorrelId);
MQPutMessageOptions mqpmo = new MQPutMessageOptions();
importQ.Put(mqPutMsg,mqpmo);
MQMessage respMsg = new MQMessage();
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.WaitInterval = 3000;
gmo.Options = MQC.MQGMO_WAIT;
try
{
importQ.Get(respMsg, gmo);
}
catch (MQException ex)
{
Console.Write(ex);
Console.WriteLine("Queue Name : " + importQ.Name + ":");
}
importQ.Close();
MQQueue exportQ = qm.AccessQueue("EXPORTQ", MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING);
exportQ.Put(respMsg);
exportQ.Close();
qm.Disconnect();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
This line of code gets me the correlation id
correlationID = System.Text.Encoding.UTF8.GetString(mqMsg.CorrelationId);
I guess it's already time that I ask others. Is it possible to create a websocket server using C# and server request from HTML5 codes?
I am currently using the System package for websocket. I have a code that I downloaded over the internet and here it is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WebSocketChatServer
{
using WebSocketServer;
class ChatServer
{
WebSocketServer wss;
List<User> Users = new List<User>();
string unknownName = "john doe";
public ChatServer()
{
// wss = new WebSocketServer(8181, "http://localhost:8080", "ws://localhost:8181/chat");
wss = new WebSocketServer(8080, "http://localhost:8080", "ws://localhost:8080/dotnet/Chats");
wss.Logger = Console.Out;
wss.LogLevel = ServerLogLevel.Subtle;
wss.ClientConnected += new ClientConnectedEventHandler(OnClientConnected);
wss.Start();
KeepAlive();
}
private void KeepAlive()
{
string r = Console.ReadLine();
while (r != "quit")
{
if(r == "users")
{
Console.WriteLine(Users.Count);
}
r = Console.ReadLine();
}
}
void OnClientConnected(WebSocketConnection sender, EventArgs e)
{
Console.WriteLine("test");
Users.Add(new User() { Connection = sender });
sender.Disconnected += new WebSocketDisconnectedEventHandler(OnClientDisconnected);
sender.DataReceived += new DataReceivedEventHandler(OnClientMessage);
}
void OnClientMessage(WebSocketConnection sender, DataReceivedEventArgs e)
{
Console.WriteLine(sender);
User user = Users.Single(a => a.Connection == sender);
if (e.Data.Contains("/nick"))
{
string[] tmpArray = e.Data.Split(new char[] { ' ' });
if (tmpArray.Length > 1)
{
string myNewName = tmpArray[1];
while (Users.Where(a => a.Name == myNewName).Count() != 0)
{
myNewName += "_";
}
if (user.Name != null)
wss.SendToAll("server: '" + user.Name + "' changed name to '" + myNewName + "'");
else
sender.Send("you are now know as '" + myNewName + "'");
user.Name = myNewName;
}
}
else
{
string name = (user.Name == null) ? unknownName : user.Name;
wss.SendToAllExceptOne(name + ": " + e.Data, sender);
sender.Send("me: " + e.Data);
}
}
void OnClientDisconnected(WebSocketConnection sender, EventArgs e)
{
try
{
User user = Users.Single(a => a.Connection == sender);
string name = (user.Name == null) ? unknownName : user.Name;
wss.SendToAll("server: "+name + " disconnected");
Users.Remove(user);
}
catch (Exception exc)
{
Console.WriteLine("ehm...");
}
}
}
}
And I have this code for client side:
<!HTML>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
</script>
<script language="javascript" type="text/javascript">
jQuery(document).ready(function(){
var socket = new WebSocket("ws://localhost:8080");
socket.onopen = function(){
alert("Socket has been opened!");
}
});
</script>
</head>
</HTML>
As I run my C# console app and load the client page, the app tells me that there's someone who connected in the port it is listening to. But on my client side, as I look in firebug's console, it gives me the beginner's classic error:
Firefox can't establish a connection to the server at ws://localhost:8080/
What I would like to achieve is establish first a successful websocket connection and push value to my client coming from my server.
I have considered Alchemy but the version I have is Visual Studio express 2010, the free version, and it says that "solution folders are not supported in this version of application".
Any help will be very much appreciated.
I've been developing a server for a JavaScript/HTML 5 game for about 7 months now have you looked into Alchemy Websockets? its pretty easy to use.
Example:
using Alchemy;
using Alchemy.Classes;
namespace GameServer
{
static class Program
{
public static readonly ConcurrentDictionary<ClientPeer, bool> OnlineUsers = new ConcurrentDictionary<ClientPeer, bool>();
static void Main(string[] args)
{
var aServer = new WebSocketServer(4530, IPAddress.Any)
{
OnReceive = context => OnReceive(context),
OnConnected = context => OnConnect(context),
OnDisconnect = context => OnDisconnect(context),
TimeOut = new TimeSpan(0, 10, 0),
FlashAccessPolicyEnabled = true
};
}
private static void OnConnect(UserContext context)
{
var client = new ClientPeer(context);
OnlineUsers.TryAdd(client, false);
//Do something with the new client
}
}
}
As you can see its pretty easy to work with and I find their documentation very good (note ClientPeer is a custom class of mine just using it as an example).
What you are trying to achieve will be far easier if you take a look at ASP.NET SignalR
It has support for high level hubs to implement realtime communication and also has a persistent connection low level class to have a finely grained control over the communication.
Support for multiple client types and fallback if websockets isn't supported at both ends of the communication (it can optionally use long polling or forever frames).
The reason for this error is ( probably ) because you are not responding to the handshake. Once the connection is established browser sends some data and the server must respond appropriately ( otherwise browser will close the connection ). You can read more about this on wiki or directly in specification.
I modified a code i downloaded online and here's what I got now:
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
namespace WebSocketServer
{
public enum ServerLogLevel { Nothing, Subtle, Verbose };
public delegate void ClientConnectedEventHandler(WebSocketConnection sender, EventArgs e);
public class WebSocketServer
{
#region private members
private string webSocketOrigin; // location for the protocol handshake
private string webSocketLocation; // location for the protocol handshake
#endregion
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static IPEndPoint ipLocal;
public event ClientConnectedEventHandler ClientConnected;
/// <summary>
/// TextWriter used for logging
/// </summary>
public TextWriter Logger { get; set; } // stream used for logging
/// <summary>
/// How much information do you want, the server to post to the stream
/// </summary>
public ServerLogLevel LogLevel = ServerLogLevel.Subtle;
/// <summary>
/// Gets the connections of the server
/// </summary>
public List<WebSocketConnection> Connections { get; private set; }
/// <summary>
/// Gets the listener socket. This socket is used to listen for new client connections
/// </summary>
public Socket ListenerSocker { get; private set; }
/// <summary>
/// Get the port of the server
/// </summary>
public int Port { get; private set; }
public WebSocketServer(int port, string origin, string location)
{
Port = port;
Connections = new List<WebSocketConnection>();
webSocketOrigin = origin;
webSocketLocation = location;
}
/// <summary>
/// Starts the server - making it listen for connections
/// </summary>
public void Start()
{
// create the main server socket, bind it to the local ip address and start listening for clients
ListenerSocker = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
ipLocal = new IPEndPoint(IPAddress.Loopback, Port);
ListenerSocker.Bind(ipLocal);
ListenerSocker.Listen(100);
LogLine(DateTime.Now + "> server stated on " + ListenerSocker.LocalEndPoint, ServerLogLevel.Subtle);
ListenForClients();
}
// look for connecting clients
private void ListenForClients()
{
ListenerSocker.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
private void OnClientConnect(IAsyncResult asyn)
{
byte[] buffer = new byte[1024];
string headerResponse = "";
// create a new socket for the connection
var clientSocket = ListenerSocker.EndAccept(asyn);
var i = clientSocket.Receive(buffer);
headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
//Console.WriteLine(headerResponse);
if (clientSocket != null)
{
// Console.WriteLine("HEADER RESPONSE:"+headerResponse);
var key = headerResponse.Replace("ey:", "`")
.Split('`')[1] // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
.Replace("\r", "").Split('\n')[0] // dGhlIHNhbXBsZSBub25jZQ==
.Trim();
var test1 = AcceptKey(ref key);
var newLine = "\r\n";
var name = "Charmaine";
var response = "HTTP/1.1 101 Switching Protocols" + newLine
+ "Upgrade: websocket" + newLine
+ "Connection: Upgrade" + newLine
+ "Sec-WebSocket-Accept: " + test1 + newLine + newLine
+ "Testing lang naman po:" + name
;
// which one should I use? none of them fires the onopen method
clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(response));
}
// keep track of the new guy
var clientConnection = new WebSocketConnection(clientSocket);
Connections.Add(clientConnection);
// clientConnection.Disconnected += new WebSocketDisconnectedEventHandler(ClientDisconnected);
Console.WriteLine("New user: " + ipLocal);
// invoke the connection event
if (ClientConnected != null)
ClientConnected(clientConnection, EventArgs.Empty);
if (LogLevel != ServerLogLevel.Nothing)
clientConnection.DataReceived += new DataReceivedEventHandler(DataReceivedFromClient);
// listen for more clients
ListenForClients();
}
void ClientDisconnected(WebSocketConnection sender, EventArgs e)
{
Connections.Remove(sender);
LogLine(DateTime.Now + "> " + sender.ConnectionSocket.LocalEndPoint + " disconnected", ServerLogLevel.Subtle);
}
void DataReceivedFromClient(WebSocketConnection sender, DataReceivedEventArgs e)
{
Log(DateTime.Now + "> data from " + sender.ConnectionSocket.LocalEndPoint, ServerLogLevel.Subtle);
Log(": " + e.Data + "\n" + e.Size + " bytes", ServerLogLevel.Verbose);
LogLine("", ServerLogLevel.Subtle);
}
/// <summary>
/// send a string to all the clients (you spammer!)
/// </summary>
/// <param name="data">the string to send</param>
public void SendToAll(string data)
{
Connections.ForEach(a => a.Send(data));
}
/// <summary>
/// send a string to all the clients except one
/// </summary>
/// <param name="data">the string to send</param>
/// <param name="indifferent">the client that doesn't care</param>
public void SendToAllExceptOne(string data, WebSocketConnection indifferent)
{
foreach (var client in Connections)
{
if (client != indifferent)
client.Send(data);
}
}
/// <summary>
/// Takes care of the initial handshaking between the the client and the server
/// </summary>
private void Log(string str, ServerLogLevel level)
{
if (Logger != null && (int)LogLevel >= (int)level)
{
Logger.Write(str);
}
}
private void LogLine(string str, ServerLogLevel level)
{
Log(str + "\r\n", level);
}
private static string AcceptKey(ref string key)
{
string longKey = key + guid;
byte[] hashBytes = ComputeHash(longKey);
return Convert.ToBase64String(hashBytes);
}
static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
private static byte[] ComputeHash(string str)
{
return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
}
private void ShakeHands(Socket conn)
{
using (var stream = new NetworkStream(conn))
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
//read handshake from client (no need to actually read it, we know its there):
LogLine("Reading client handshake:", ServerLogLevel.Verbose);
string r = null;
while (r != "")
{
r = reader.ReadLine();
LogLine(r, ServerLogLevel.Verbose);
}
// send handshake to the client
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: " + webSocketOrigin);
writer.WriteLine("WebSocket-Location: " + webSocketLocation);
writer.WriteLine("");
}
// tell the nerds whats going on
LogLine("Sending handshake:", ServerLogLevel.Verbose);
LogLine("HTTP/1.1 101 Web Socket Protocol Handshake", ServerLogLevel.Verbose);
LogLine("Upgrade: WebSocket", ServerLogLevel.Verbose);
LogLine("Connection: Upgrade", ServerLogLevel.Verbose);
LogLine("WebSocket-Origin: " + webSocketOrigin, ServerLogLevel.Verbose);
LogLine("WebSocket-Location: " + webSocketLocation, ServerLogLevel.Verbose);
LogLine("", ServerLogLevel.Verbose);
LogLine("Started listening to client", ServerLogLevel.Verbose);
//conn.Listen();
}
}
}
Connection issue resolved, next would be SENDING DATA to client.
just change shakehands in WebSocketServer.cs file in solution with below code and your error will gone..
private void ShakeHands(Socket conn)
{
using (var stream = new NetworkStream(conn))
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
//read handshake from client (no need to actually read it, we know its there):
LogLine("Reading client handshake:", ServerLogLevel.Verbose);
string r = null;
Dictionary<string, string> headers = new Dictionary<string, string>();
while (r != "")
{
r = reader.ReadLine();
string[] tokens = r.Split(new char[] { ':' }, 2);
if (!string.IsNullOrWhiteSpace(r) && tokens.Length > 1)
{
headers[tokens[0]] = tokens[1].Trim();
}
LogLine(r, ServerLogLevel.Verbose);
}
//string line = string.Empty;
//while ((line = reader.ReadLine()) != string.Empty)
//{
// string[] tokens = line.Split(new char[] { ':' }, 2);
// if (!string.IsNullOrWhiteSpace(line) && tokens.Length > 1)
// {
// headers[tokens[0]] = tokens[1].Trim();
// }
//}
string responseKey = "";
string key = string.Concat(headers["Sec-WebSocket-Key"], "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
using (SHA1 sha1 = SHA1.Create())
{
byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(key));
responseKey = Convert.ToBase64String(hash);
}
// send handshake to the client
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: " + webSocketOrigin);
writer.WriteLine("WebSocket-Location: " + webSocketLocation);
//writer.WriteLine("Sec-WebSocket-Protocol: chat");
writer.WriteLine("Sec-WebSocket-Accept: " + responseKey);
writer.WriteLine("");
}
// tell the nerds whats going on
LogLine("Sending handshake:", ServerLogLevel.Verbose);
LogLine("HTTP/1.1 101 Web Socket Protocol Handshake", ServerLogLevel.Verbose);
LogLine("Upgrade: WebSocket", ServerLogLevel.Verbose);
LogLine("Connection: Upgrade", ServerLogLevel.Verbose);
LogLine("WebSocket-Origin: " + webSocketOrigin, ServerLogLevel.Verbose);
LogLine("WebSocket-Location: " + webSocketLocation, ServerLogLevel.Verbose);
LogLine("", ServerLogLevel.Verbose);
LogLine("Started listening to client", ServerLogLevel.Verbose);
//conn.Listen();
}
You may also take a look at the WebSocketRPC library which should be pretty simple to use, for both the "raw" connection and the RPC connections (the messages can also be mixed).
The following can be useful to you:
The code responsible for sending/receiving raw messages is located in the Connection class.
In the repository you can also find how a base JavaScript client is implemented.
Disclaimer: I am the author.
Here's what's going on. I have an ASP.NET MVC 4 Web API web application. I can call API resources via URL. One of these functions get performance monitoring data for a specified amount of time and returns it in JSON once it has completed. However, what I want to do is return
It is IMPORTANT to note that I am working with a the browser and API resources in the model, not with a View. Please don't casually tell me to use Javascript in a View, because there is no view, or tell me to look at the SignalR wiki because the information for ".NET" sections is meant for desktop applications, not web apps. For example, you can't "Console.WriteLine()" to a browser.
To reiterate, I am using ASP.NET MVC 4 Web API to develop an API, and am calling the API via URL in the browser and it is returning JSON. I am attempting to use SignalR to have the app send JSON to the browser, but it is not doing anything at all. Rather, the application simply returns the completed JSON from the controller action with all of the performance data values once the process has completed. In other words, SignalR is not working.
So what I'm trying to do is while the API resource is gathering all the information, SignalR sends JSON to the browser every second so that the client can see what's going on in real time.
What I need to find out is why SignalR isn't sending it, and how I can send information to be displayed in the browser without Javascript, since I'm working from a model class, not from a view.
As you can see, I subscribe to the event using On, and then use Invoke to call the server-side hub method SendToClient.
Please let me know if I'm trying to do is impossible. I have never heard of a "real-time", dynamic API call via URL.
Here is my hub class. It is located in ~/signalr/hubs and is in a file called LiveHub.cs. The method Send is what I am trying to invoke in the method seen in the next code block.
namespace PerfMon2.signalr.hubs
{
public class LiveHub : Hub
{
public void SendToClient(List<DataValueInfo> json)
{
Clients.showValue(json);
}
}
}
Here is the method from LogDBRepository.cs that includes the SignalR calls.
public List<LogInfo> LogTimedPerfData(string macName, string categoryName, string counterName,
string instanceName, string logName, string live, long? seconds)
{
iModsDBRepository modsDB = new iModsDBRepository();
List<MachineInfo> theMac = modsDB.GetMachineByName(macName);
if (theMac.Count == 0)
return new List<LogInfo>();
else if (instanceName == null)
{
if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
!PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) )
{
return new List<LogInfo>();
}
}
else if (instanceName != null)
{
if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
!PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) ||
!PerformanceCounterCategory.InstanceExists(instanceName, categoryName, macName))
{
return new List<LogInfo>();
}
}
else if (logName == null)
{
return new List<LogInfo>();
}
// Check if entered log name is a duplicate for the authenticated user
List<LogInfo> checkDuplicateLog = this.GetSingleLog(logName);
if (checkDuplicateLog.Count > 0)
{
return new List<LogInfo>();
}
PerformanceCounterCategory category = new PerformanceCounterCategory(categoryName, theMac[0].MachineName);
if (category.CategoryName == null || category.MachineName == null)
{
return new List<LogInfo>();
}
List<LogInfo> logIt = new List<LogInfo>();
if (category.CategoryType != PerformanceCounterCategoryType.SingleInstance)
{
List<InstanceInfo> instances = modsDB.GetInstancesFromCatMacName(theMac[0].MachineName, category.CategoryName);
foreach (InstanceInfo inst in instances)
{
if (!category.InstanceExists(inst.InstanceName))
{
continue;
}
else if (inst.InstanceName.Equals(instanceName, StringComparison.OrdinalIgnoreCase))
{
PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
inst.InstanceName, theMac[0].MachineName);
//CounterSample data = perfCounter.NextSample();
//double value = CounterSample.Calculate(data, perfCounter.NextSample());
string data = "";
List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);
string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
//string[] dataValues = new string[(int)seconds];
List<string> dataValues = new List<string>();
var hubConnection = new HubConnection("http://localhost/PerfMon2/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
var perfMon = hubConnection.CreateProxy("LiveHub");
// perfMon.On("sendValue", message => Console.WriteLine(message));
perfMon.On("showValue", json => Console.WriteLine(json));
hubConnection.Start().Wait();
List<DataValueInfo> lol = new List<DataValueInfo>();
for (int i = 0; i < seconds; i++)
{
data = "Value " + i + ": " + perfCounter.NextValue().ToString();
//dataValues[i] = data;
dataValues.Add(data);
lol.Add(new DataValueInfo
{
Value = perfCounter.NextValue().ToString()
});
// perfMon.Invoke<List<DataValueInfo>>("Send", lol);
perfMon.Invoke("SendToClient", lol);
Thread.Sleep(1000);
}
string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
Log log = new Log
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = string.Join(",", dataValues),
UserID = currUser[0].UserID
};
this.CreateLog(log);
logIt.Add(new LogInfo
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = dataValues.ToList<string>()
});
break;
}
}
}
else
{
PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
"", theMac[0].MachineName);
string data = "";
List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);
string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
//string[] dataValues = new string[(int)seconds];
List<string> dataValues = new List<string>();
var hubConnection = new HubConnection("http://localhost/PerfMon2/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
var perfMon = hubConnection.CreateProxy("LiveHub");
// perfMon.On("sendValue", message => Console.WriteLine(message));
perfMon.On("showValue", json => Console.WriteLine(json));
hubConnection.Start().Wait();
List<DataValueInfo> lol = new List<DataValueInfo>();
for (int i = 0; i < seconds; i++)
{
data = "Value " + i + ": " + perfCounter.NextValue().ToString();
//dataValues[i] = data;
dataValues.Add(data);
lol.Add(new DataValueInfo
{
Value = perfCounter.NextValue().ToString()
});
// perfMon.Invoke<List<DataValueInfo>>("Send", lol);
perfMon.Invoke("SendToClient", lol);
Thread.Sleep(1000);
}
string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
Log log = new Log
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = string.Join(",", dataValues),
UserID = currUser[0].UserID
};
this.CreateLog(log);
logIt.Add(new LogInfo
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = dataValues.ToList<string>()
});
}
return logIt;
}
Here is the controller for the method in LogController.cs :
[AcceptVerbs("GET", "POST")]
public List<LogInfo> Log_Perf_Data(string machine_name, string category_name, string counter_name, string instance_name,
string log_name, long? seconds, string live, string enforceQuery)
{
LogController.CheckUser();
// POST api/log/post_data?machine_name=&category_name=&counter_name=&instance_name=&log_name=&seconds=
if (machine_name != null && category_name != null && counter_name != null && log_name != null && seconds.HasValue && enforceQuery == null)
{
List<LogInfo> dataVal = logDB.LogTimedPerfData(machine_name, category_name, counter_name, instance_name,
log_name, live, seconds);
logDB.SaveChanges();
return dataVal;
}
return new List<LogInfo>();
}
Maybe you can implement it in push technique. Here is how I do it:
Class with message
public class Message
{
/// <summary>
/// The name who will receive this message.
/// </summary>
public string RecipientName { get; set; }
/// <summary>
/// The message content.
/// </summary>
public string MessageContent { get; set; }
}
Class that will represent client:
public class Client
{
private ManualResetEvent messageEvent = new ManualResetEvent(false);
private Queue<Message> messageQueue = new Queue<Message>();
/// <summary>
/// This method is called by a sender to send a message to this client.
/// </summary>
/// <param name="message">the new message</param>
public void EnqueueMessage(Message message)
{
lock (messageQueue)
{
messageQueue.Enqueue(message);
// Set a new message event.
messageEvent.Set();
}
}
/// <summary>
/// This method is called by the client to receive messages from the message queue.
/// If no message, it will wait until a new message is inserted.
/// </summary>
/// <returns>the unread message</returns>
public Message DequeueMessage()
{
// Wait until a new message.
messageEvent.WaitOne();
lock (messageQueue)
{
if (messageQueue.Count == 1)
{
messageEvent.Reset();
}
return messageQueue.Dequeue();
}
}
}
Class to send messages to clients:
public class ClientAdapter
{
/// <summary>
/// The recipient list.
/// </summary>
private Dictionary<string, Client> recipients = new Dictionary<string,Client>();
/// <summary>
/// Send a message to a particular recipient.
/// </summary>
public void SendMessage(Message message)
{
if (recipients.ContainsKey(message.RecipientName))
{
Client client = recipients[message.RecipientName];
client.EnqueueMessage(message);
}
}
/// <summary>
/// Called by a individual recipient to wait and receive a message.
/// </summary>
/// <returns>The message content</returns>
public string GetMessage(string userName)
{
string messageContent = string.Empty;
if (recipients.ContainsKey(userName))
{
Client client = recipients[userName];
messageContent = client.DequeueMessage().MessageContent;
}
return messageContent;
}
/// <summary>
/// Join a user to the recipient list.
/// </summary>
public void Join(string userName)
{
recipients[userName] = new Client();
}
/// <summary>
/// Singleton pattern.
/// This pattern will ensure there is only one instance of this class in the system.
/// </summary>
public static ClientAdapter Instance = new ClientAdapter();
private ClientAdapter() { }
}
Sending messages:
Message message = new Message
{
RecipientName = tbRecipientName.Text.Trim(),
MessageContent = tbMessageContent.Text.Trim()
};
if (!string.IsNullOrWhiteSpace(message.RecipientName) && !string.IsNullOrEmpty(message.MessageContent))
{
// Call the client adapter to send the message to the particular recipient instantly.
ClientAdapter.Instance.SendMessage(message);
}
Receive messages (this is JavaScript functions written in test page. They render content of the message on ASPX page. Here you should implement your logic):
// This method will persist a http request and wait for messages.
function waitEvent() {
CSASPNETReverseAJAX.Dispatcher.WaitMessage("<%= Session["userName"] %>",
function (result) {
displayMessage(result);
// Keep looping.
setTimeout(waitEvent, 0);
}, function () {
// Keep looping.
setTimeout(waitEvent, 0);
});
}
// Append a message content to the result panel.
function displayMessage(message) {
var panel = document.getElementById("<%= lbMessages.ClientID %>");
panel.innerHTML += currentTime() + ": " + message + "<br />";
}
// Return a current time string.
function currentTime() {
var currentDate = new Date();
return currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds();
}