How to use signalr in Android - c#
I am trying to integrate signalR in android app but no luck. I've been looking at various links but none of them provide proper information about implementation.
I've the following questions.
SignalR integration has to be done inside Service/Intent Service?
If we want to receive response via same calling method then how to get?
I've added three libraries i.e signalr android,signalr client and gson but unable to understand how code works, no proper documentation is available to understand the code.
Some of the questions asked but not much information
SignalR in Android Studio
Unable to implement p2p chat using SignalR in Android
If anyone experienced in signal for native apps, it would be very helpful for me.
Update
public class SignalRService extends Service {
private static final String TAG = "Service";
private HubConnection mHubConnection;
private HubProxy mHubProxy;
private Handler mHandler; // to display Toast message
private final IBinder mBinder = new LocalBinder();
private SharedPreferences sp;
#Override
public void onCreate() {
super.onCreate();
Utility.showLog(TAG, "Service Created");
sp = getSharedPreferences(Utility.SHARED_PREFS, MODE_PRIVATE);
mHandler = new Handler(Looper.myLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int result = super.onStartCommand(intent, flags, startId);
startSignalR();
return result;
}
#Override
public IBinder onBind(Intent intent) {
startSignalR();
return mBinder;
}
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public SignalRService getService() {
// Return this instance of SignalRService so clients can call public methods
return SignalRService.this;
}
}
/**
* method for clients (activities)
*/
public void sendMessage() {
String SERVER_METHOD_SEND = "iAmAvailable";
final String string = new String();
mHubProxy.invoke(new String(), SERVER_METHOD_SEND, sp.getString("user_id", null), sp.getString("pass", null), "TransMedic").done(new Action() {
#Override
public void run(Object o) throws Exception {
Utility.showLog(TAG, o.toString());
}
}).onError(new ErrorCallback() {
#Override
public void onError(Throwable throwable) {
}
});
}
private void startSignalR() {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
String serverUrl = "http://transit.alwaysaware.org/signalr";
mHubConnection = new HubConnection(serverUrl);
String SERVER_HUB_CHAT = "ChatHub";
mHubProxy = mHubConnection.createHubProxy(SERVER_HUB_CHAT);
ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());
SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return;
}
sendMessage();
}
#Override
public void onDestroy() {
mHubConnection.stop();
super.onDestroy();
}
}
UPDATE 2018:
If you are using SignalR.net Core use this library otherwise you will get error on connection.
SERVER SIDE:
The following is my sample server-side code, you can pay attention to public void Send(string message) and public void SendChatMessage(string to, string message).
Server-side app: public void SendChatMessage(string to, string message)
Android client app: mHubProxy.invoke("SendChatMessage", receiverName, message);
Server-side app: public void Send(string message)
Android client app: mHubProxy.invoke("Send", message);
namespace SignalRDemo
{
public class ChatHub : Hub
{
private static ConcurrentDictionary<string, string> FromUsers = new ConcurrentDictionary<string, string>(); // <connectionId, userName>
private static ConcurrentDictionary<string, string> ToUsers = new ConcurrentDictionary<string, string>(); // <userName, connectionId>
private string userName = "";
public override Task OnConnected()
{
DoConnect();
Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Online" });
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
if (stopCalled) // Client explicitly closed the connection
{
string id = Context.ConnectionId;
FromUsers.TryRemove(id, out userName);
ToUsers.TryRemove(userName, out id);
Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Offline" });
}
else // Client timed out
{
// Do nothing here...
// FromUsers.TryGetValue(Context.ConnectionId, out userName);
// Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Offline By TimeOut"});
}
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
DoConnect();
Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = "I'm Online Again" });
return base.OnReconnected();
}
private void DoConnect()
{
userName = Context.Request.Headers["User-Name"];
if (userName == null || userName.Length == 0)
{
userName = Context.QueryString["User-Name"]; // for javascript clients
}
FromUsers.TryAdd(Context.ConnectionId, userName);
String oldId; // for case: disconnected from Client
ToUsers.TryRemove(userName, out oldId);
ToUsers.TryAdd(userName, Context.ConnectionId);
}
public void Send(string message)
{
// Call the broadcastMessage method to update clients.
string fromUser;
FromUsers.TryGetValue(Context.ConnectionId, out fromUser);
Clients.AllExcept(Context.ConnectionId).broadcastMessage(new ChatMessage() { UserName = fromUser, Message = message });
}
public void SendChatMessage(string to, string message)
{
FromUsers.TryGetValue(Context.ConnectionId, out userName);
string receiver_ConnectionId;
ToUsers.TryGetValue(to, out receiver_ConnectionId);
if (receiver_ConnectionId != null && receiver_ConnectionId.Length > 0)
{
Clients.Client(receiver_ConnectionId).broadcastMessage(new ChatMessage() { UserName = userName, Message = message });
}
}
}
public class ChatMessage
{
public string UserName { get; set; }
public string Message { get; set; }
}
}
CLIENT SIDE:
If you have not read my answer at the following question:
SignalR integration in android studio
Then, here is my working basic code:
public class SignalRService extends Service {
private HubConnection mHubConnection;
private HubProxy mHubProxy;
private Handler mHandler; // to display Toast message
private final IBinder mBinder = new LocalBinder(); // Binder given to clients
public SignalRService() {
}
#Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int result = super.onStartCommand(intent, flags, startId);
startSignalR();
return result;
}
#Override
public void onDestroy() {
mHubConnection.stop();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// Return the communication channel to the service.
startSignalR();
return mBinder;
}
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public SignalRService getService() {
// Return this instance of SignalRService so clients can call public methods
return SignalRService.this;
}
}
/**
* method for clients (activities)
*/
public void sendMessage(String message) {
String SERVER_METHOD_SEND = "Send";
mHubProxy.invoke(SERVER_METHOD_SEND, message);
}
private void startSignalR() {
Platform.loadPlatformComponent(new AndroidPlatformComponent());
Credentials credentials = new Credentials() {
#Override
public void prepareRequest(Request request) {
request.addHeader("User-Name", "BNK");
}
};
String serverUrl = "http://192.168.1.100";
mHubConnection = new HubConnection(serverUrl);
mHubConnection.setCredentials(credentials);
String SERVER_HUB_CHAT = "ChatHub";
mHubProxy = mHubConnection.createHubProxy(SERVER_HUB_CHAT);
ClientTransport clientTransport = new ServerSentEventsTransport(mHubConnection.getLogger());
SignalRFuture<Void> signalRFuture = mHubConnection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return;
}
String HELLO_MSG = "Hello from Android!";
sendMessage(HELLO_MSG);
String CLIENT_METHOD_BROADAST_MESSAGE = "broadcastMessage";
mHubProxy.on(CLIENT_METHOD_BROADAST_MESSAGE,
new SubscriptionHandler1<CustomMessage>() {
#Override
public void run(final CustomMessage msg) {
final String finalMsg = msg.UserName + " says " + msg.Message;
// display Toast message
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), finalMsg, Toast.LENGTH_SHORT).show();
}
});
}
}
, CustomMessage.class);
}
}
Activity:
public class MainActivity extends AppCompatActivity {
private final Context mContext = this;
private SignalRService mService;
private boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setClass(mContext, SignalRService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
super.onStop();
}
public void sendMessage(View view) {
if (mBound) {
// Call a method from the SignalRService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
EditText editText = (EditText) findViewById(R.id.edit_message);
if (editText != null && editText.getText().length() > 0) {
String message = editText.getText().toString();
mService.sendMessage(message);
}
}
}
/**
* Defines callbacks for service binding, passed to bindService()
*/
private final ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to SignalRService, cast the IBinder and get SignalRService instance
SignalRService.LocalBinder binder = (SignalRService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
CustomMessage Class:
public class CustomMessage {
public String UserName;
public String Message;
}
You can also see my sample client project at this GitHub link
UPDATE FOR RESPONSE FROM INVOKE:
I have just added new sample methods:
Server side:
public string iAmAvailable(string username, string password, string message)
{
return "BNK Response for testing Android INVOKE";
}
Client side:
mHubProxy.invoke(String.class, "iAmAvailable", "username", "password", "TransMedic").done(new Action<String>() {
#Override
public void run(String s) throws Exception {
Log.w("SimpleSignalR", s);
}
}).onError(new ErrorCallback() {
#Override
public void onError(Throwable throwable) {
Log.e("SimpleSignalR", throwable.toString());
}
});
And here is the screenshot:
This work for me : Full source Android (Client) & Server GitHub
Server Slide If one argument must use this interface SubscriptionHandler1 if two argument must use this interfaceSubscriptionHandler2 ,...
Sample for two argument like :
Server slide :
using Microsoft.AspNet.SignalR;
namespace SignalRChat
{
public class ChatHub : Hub
{
public void Send(string name, string message)
{
// Two argument must use this interfaceSubscriptionHandler2 .
Clients.All.broadcastMessage(name, message);
}
}
}
Client slide :
mHubProxy.on(CLIENT_METHOD_BROADAST_MESSAGE,
new SubscriptionHandler2<String, String>() {
#Override
public void run(final String name,final String msg) {
final String finalMsg = msg.toString();
// display Toast message
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), finalMsg, Toast.LENGTH_SHORT).show();
}
});
}
}
, String.class,String.class);
For catch all message can use this :
mHubConnection.received(new MessageReceivedHandler() {
#Override
public void onMessageReceived(final JsonElement json) {
Log.e("onMessageReceived ", json.toString());
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), json.toString(), Toast.LENGTH_SHORT).show();
}
});
}
});
The SignalR team recently released a Java client for ASP.NET Core SignalR. Here is a link to getting started docs https://learn.microsoft.com/en-us/aspnet/core/signalr/java-client?view=aspnetcore-2.2
do this tutorial step by step :
https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr?tabs=visual-studio-mac&view=aspnetcore-5.0
1.According above tutorial publish your chat server to favorite host
2.add this dependency to your android sample:
implementation 'com.microsoft.signalr:signalr:3.0.0'
3.add these permission to manifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
4.below code is MainActivity.class:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HubConnection hubConnection =
HubConnectionBuilder.create("https://your_chat_server_url/chatHub").build();
TextView textView = (TextView)findViewById(R.id.tvMain);
ListView listView = (ListView)findViewById(R.id.lvMessages);
Button sendButton = (Button)findViewById(R.id.bSend);
EditText editText = (EditText)findViewById(R.id.etMessageText);
List<String> messageList = new ArrayList<String>();
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_list_item_1, messageList);
listView.setAdapter(arrayAdapter);
hubConnection.on("ReceiveMessage", (user, message)-> {
runOnUiThread(new Runnable() {
#Override
public void run() {
arrayAdapter.add( user + " : " + message);
arrayAdapter.notifyDataSetChanged();
}
});
}, String.class,String.class);
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String message = editText.getText().toString();
String user = "SAEID";
editText.setText("");
try {
hubConnection.send("SendMessage", user,message);
} catch (Exception e) {
e.printStackTrace();
}
}
});
new HubConnectionTask().execute(hubConnection);
}
static class HubConnectionTask extends AsyncTask<HubConnection, Void, Void>{
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(HubConnection... hubConnections) {
HubConnection hubConnection = hubConnections[0];
hubConnection.start().blockingAwait();
return null;
}
}
}
5.below code is activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/tvMain" />
<ListView
android:layout_height="0dp"
android:layout_weight="1"
android:layout_width="fill_parent"
android:id="#+id/lvMessages"
android:transcriptMode="alwaysScroll">
</ListView>
<EditText
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="#+id/etMessageText"
android:hint="Enter Message" />
<Button
android:text="Send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/bSend" />
</LinearLayout>
For those who are implementing signalR client in android and the given answer here doesn't help in receiving the messages can check out this answer by rejnev.
The answer implements a different method connection.received() which is able to receive message callbacks from the server in my case.
Related
C# SignalR2 receive online user list from server
I have created a chat using SignalR2. The client and server itself works fine. Now, I'm trying to implement a 'users online' function. The server code seems about right, but I'm struggling to make the client receive the data that the server pushes back to the client. This is the server code below: public static List<string> Users = new List<string>(); public void Send(string name, string message) { // Call the broadcastMessage method to update clients. Clients.All.broadcastMessage(name, message); Clients.All.addMessage(name, message); } public void SendUserList(List<string> users) { var context = GlobalHost.ConnectionManager.GetHubContext<chatHub>(); context.Clients.All.updateUserList(users); } public override Task OnConnected() { string clientId = GetClientId(); //if (Users.IndexOf(clientId) == -1) //{ Users.Add(clientId); //} SendCount(Users.Count); return base.OnConnected(); } public override Task OnDisconnected(bool stopCalled) { System.Diagnostics.Debug.WriteLine("Disconnected"); SendCount(Users.Count); return base.OnDisconnected(stopCalled); } private string GetClientId() { string clientId = ""; if (Context.QueryString["clientId"] != null) { // clientId passed from application clientId = this.Context.QueryString["clientId"]; } if (string.IsNullOrEmpty(clientId.Trim())) { clientId = Context.ConnectionId; } return clientId; } public void SendCount(int count) { // Call the addNewMessageToPage method to update clients. var context = GlobalHost.ConnectionManager.GetHubContext<chatHub>(); context.Clients.All.updateUsersOnlineCount(count); } Below is the client code for connecting / receiving messages: public static async void ConnectAsync(RadChat ChatInternal) { ChatInternal.Author = new Author(null, Varribles.Agent); var querystringData = new Dictionary<string, string>(); querystringData.Add("clientId", Varribles.Agent); Connection = new HubConnection(ServerURI, querystringData); HubProxy = Connection.CreateHubProxy("chatHub"); //Handle incoming event from server: use Invoke to write to console from SignalR's thread HubProxy.On<string, string>("AddMessage", (name, message) => ChatInternal.Invoke((Action)(() => Backend.GET.Messages(ChatInternal) ))); try { await Connection.Start(); Backend.GET.Messages(ChatInternal); } catch (System.Net.Http.HttpRequestException) { //No connection: Don't enable Send button or show chat UI return; } } Now, my question is, how can I retrieve the 'Users' list from the server? Thanks in advance
SignalR remote client
I want to connect to signalr with a client thats on a different pc. This means i wont be using localhost. I already made a simple networkdiscovery to get the correct ip address but it seems signalr does not allow remote clients to connect even though I already use CorsOptions.AllowAll. class Startup { public void Configuration(IAppBuilder app) { var hubConfiguration = new HubConfiguration { #if DEBUG EnableDetailedErrors = true #else EnableDetailedErrors = false #endif }; app.UseCors(CorsOptions.AllowAll); app.MapSignalR(hubConfiguration); } } Iam using duality which is a 2d game engine. Here is the server: public class SignalRServer : Component, ICmpInitializable { private IDisposable _signalRServer; public int _port { get; set; } = 8080; public void StopServer() { if (_signalRServer != null) _signalRServer.Dispose(); } public void OnInit(InitContext context) { if (context == InitContext.Activate && DualityApp.ExecContext == DualityApp.ExecutionContext.Game) { var networkDiscovery = new NetworkDiscovery(_port, "TestGame"); //Network discovery to get the ip adres of the server if one is found IPEndPoint ipEndPoint; if (networkDiscovery.LookForServer(out ipEndPoint)) { try { ConnectToServer(ipEndPoint).Wait(); Debug.WriteLine($"Connection established to {ipEndPoint}"); } catch (Exception ex) { Debug.WriteLine("Could not find server"); } } else //No server was found so we create one { Debug.WriteLine("Starting signalR server"); string url = $"http://*:{_port}"; //To test go to http://localhost:8080/signalr/hubs networkDiscovery.Start(); _signalRServer = WebApp.Start<Startup>(url); } } } private async Task ConnectToServer(IPEndPoint ipEndPoint) { var hubConnection = new HubConnection($"http://{ipEndPoint}/"); IHubProxy hubProxy = hubConnection.CreateHubProxy(nameof(MyHub)); hubProxy.On<string, string>(nameof(MyHub.Send), (name, message) => { Debug.WriteLine("Incoming data: {0} {1}", name, message); }); ServicePointManager.DefaultConnectionLimit = 10; await hubConnection.Start(); } public void OnShutdown(ShutdownContext context) { StopServer(); } } And the hub: public class MyHub : Hub { public void Send(string name, string message) { Clients.All.addMessage(name, message); } public override Task OnConnected() { Debug.WriteLine("Client connected: " + Context.ConnectionId); Send("Server", $"Client with id {Context.ConnectionId} has connected"); return base.OnConnected(); } public override Task OnDisconnected(bool stopCalled) { Debug.WriteLine("Client disconnected: " + Context.ConnectionId); return base.OnDisconnected(stopCalled); } }
WebSocket Implementation
I want to Create WebSocket Example in which i do not want to refresh the page for getting latest data. I Create one Html page in which create one object of websocket. E.g ClientSide Implementation var ws = new WebSocket(hostURL); ws.onopen = function () { // When Connection Open }; ws.onmessage = function (evt) { // When Any Response come from WebSocket } ws.onclose = function (e) { // OnClose of WebSocket Conection } Server Side Implementation public class WebSocketManager : WebSocketHandler { private static WebSocketCollection WebSocketObj4AddMessage = new WebSocketCollection(); public override void OnOpen() { // Do when Connection Is Open } public override void OnClose() { // Close Connection } public override void OnMessage(string message) { // When Any Message Sent to Client } } Is I am doing right way to use WebSocket ? Please help me to clear out in this section.
Here a sample. First you have to install Asp.net SignalR package along with its dependenies. You have call the SignalR when the app starts namespace ABC { public partial class Startup { public void Configuration(IAppBuilder app) { ConfigureAuth(app); app.MapSignalR(); <--{Add this line} } } } You have start the SqlDependency when app start and stop when app stops in the Global.asax file. string ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionStringsName"].ConnectionString; protected void Application_Start() { SqlDependency.Start(ConnectionString); } protected void Application_End() { SqlDependency.Stop(ConnectionString); } You have to create custom Hubclass extending Hub Base class public class MessagesHub : Hub { [HubMethodName("sendMessages")] public void SendMessages() { IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MessagesHub>(); context.Clients.All.updateMessages(); } } Then in the client page, you have add these code in the javascript section $(function () { // Declare a proxy to reference the hub. var notifications = $.connection.messagesHub; //debugger; // Create a function that the hub can call to broadcast messages. notifications.client.updateMessages = function () { getAllMessages() }; // Start the connection. $.connection.hub.start().done(function () { getAllMessages(); }).fail(function (e) { alert(e); }); }); function getAllMessages() { $.ajax({ url: '../../Notifications/GetNotificationMessages', . . } The server call this function when there there is any change in the database table using sqlDependency The getAllMessages() is the controller for your code to handle, that should be shown in the view page and it will be call when the app starts and any change in db public ActionResult GetNotificationMessages() { NotificationRepository notification = new NotificationRepository(); return PartialView("_NotificationMessage"); } The in model class public class NotificationRepository { readonly string connectionString = ConfigurationManager.ConnectionStrings["InexDbContext"].ConnectionString; public IEnumerable<Notification> GetAllMessages(string userId) { var messages = new List<Notification>(); using(var connection = new SqlConnection(connectionString)) { connection.Open(); using (var command = new SqlCommand(#"SELECT [NotificationID], [Message], [NotificationDate], [Active], [Url], [userId] FROM [dbo].[Notifications] WHERE [Active] = 1 AND [userId] ='" + userId + "'", connection)) { command.Notification = null; var dependency = new SqlDependency(command); dependency.OnChange += new OnChangeEventHandler(dependency_OnChange); if (connection.State == ConnectionState.Closed) { connection.Open(); } var reader = command.ExecuteReader(); while (reader.Read()) { messages.Add(item: new Notification { NotificationID = (int)reader["NotificationID"], Message = (string)reader["Message"], Url = (string)reader["Url"] }); } } } return messages; } private void dependency_OnChange(object sender, SqlNotificationEventArgs e) { if (e.Type == SqlNotificationType.Change) { MessagesHub message = new MessagesHub(); message.SendMessages(); } } } This well show latest data when the database table is updated. the message will shown at runtime. Hope this helps
You are on the right path You can refer this if I am not late ...This is working example CLIENT SIDE var ws; var username = "JOHN"; function startchat() { var log= $('log'); var url = 'ws://<server path>/WebSocketsServer.ashx?username=' + username; ws = new WebSocket(url); ws.onerror = function (e) { log.appendChild(createSpan('Problem with connection: ' + e.message)); }; ws.onopen = function () { ws.send("I am Active-" +username); }; ws.onmessage = function (e) { if (e.data.toString() == "Active?") { ws.send("I am Active-" + username); } else { } }; ws.onclose = function () { log.innerHTML = 'Closed connection!'; }; } </script> <div id="log"> </div> Server Side in Websocketserver.ashx page public class WebSocketsServer : IHttpHandler { public void ProcessRequest(HttpContext context) { if (context.IsWebSocketRequest) { context.AcceptWebSocketRequest(new MicrosoftWebSockets()); } } public bool IsReusable { get { return false; } } } Add below class in the server side public class MicrosoftWebSockets : WebSocketHandler { private static WebSocketCollection clients = new WebSocketCollection(); private string msg; public override void OnOpen() { this.msg = this.WebSocketContext.QueryString["username"]; clients.Add(this); clients.Broadcast(msg); } public override void OnMessage(string message) { clients.Broadcast(string.Format(message)); } public override void OnClose() { clients.Remove(this); clients.Broadcast(string.Format(msg)); } add this dll to the above class using Microsoft.Web.WebSockets; I donot remeber where I got the reference ...but above code is derived from my current working application
How can I find the message I added in my msmq
I wet through this except that I added it to a windows service like this public partial class TriggerHostProcesses : ServiceBase { private const string MESSAGE_QUEUE = #".\Private$\Sample Queue"; private MessageQueue _queue; public TriggerHostProcesses() { InitializeComponent(); } protected override void OnStart(string[] args) { SendMessage("Hope This Works"); } protected override void OnStop() { } internal void start() { OnStart(null); } private void SendMessage(string message) { _queue = new MessageQueue(MESSAGE_QUEUE); Message msg = new Message(); msg.Body = message; msg.Label = "Testing " + DateTime.Now.ToString(); _queue.Send(msg,new MessageQueueTransaction()); } } and to get the message partial class HostListener : ServiceBase { private const string MESSAGE_QUEUE = #".\Private$\Sample Queue"; private MessageQueue _queue; public HostListener() { InitializeComponent(); } protected override void OnStart(string[] args) { try { var myTransaction = new MessageQueueTransaction(); var queue = new MessageQueue(MESSAGE_QUEUE); var message = queue.Receive(new TimeSpan(0, 0, 20),myTransaction); message.Formatter = new XmlMessageFormatter( new String[] { "System.String,mscorlib" }); Console.WriteLine(message.Body.ToString()); } catch(Exception ex) { Console.WriteLine("No Message"); } } protected override void OnStop() { // TODO: Add code here to perform any tear-down necessary to stop your service. } internal void start() { OnStart(null); } } in my main I added this var ServiceToRun1 = new TriggerHostProcesses(); var ServiceToRun2 = new HostListener(); if (Environment.UserInteractive) { // This used to run the service as a console (development phase only) ServiceToRun1.start(); ServiceToRun2.start(); Console.WriteLine("Press Enter to terminate ..."); Console.ReadLine(); ServiceToRun1.Stop(); ServiceToRun2.Stop(); } else { ServiceBase.Run(ServiceToRun1); } I get the exception Timeout for the requested operation has expired. Can someone please check if they can see what the problem is?
I don't believe you are using transactions correctly. For example, when sending a message you use: _queue.Send(msg,new MessageQueueTransaction()); However, this does not begin or commit a transaction. Looking in MSDN the example uses the following code (edited by me): var myTransaction = new MessageQueueTransaction(); myTransaction.Begin(); myQueue.Send("hello world", myTransaction); myTransaction.Commit(); I don't believe your message is getting sent, and so your Receive times out. Similarly your receive logic doesn't seem to correctly use transactions: myTransaction.Begin(); var myMessage = myQueue.Receive(myTransaction); var body myOrder = (string)myMessage.Body; myTransaction.Commit(); You should Rollback in the event of an exception processing your messages so they can be placed back on the queue.
Here is my final product. I'm using this in a windows service. 20 s at a time to see if I have a message then do my processes. public class MSMQueue:IQueue { public MSMQueue(string queueName) { Message_Queue = queueName; } public string Message_Queue { get; private set; } public string Pop() { MessageQueue queue = new MessageQueue(Message_Queue); if (queue.Transactional) return popTransactionalQueue(queue, new TimeSpan(0, 0, 1)); else return popNormalQueue(queue, new TimeSpan(0, 0, 1)); } public string Pop(TimeSpan timeSpan) { MessageQueue myQueue = new MessageQueue(Message_Queue); if (myQueue.Transactional) return popTransactionalQueue(myQueue, timeSpan); else return popNormalQueue(myQueue, timeSpan); } public void Add(string message) { // Connect to a queue on the local computer. MessageQueue myQueue = new MessageQueue(Message_Queue); // Send a message to the queue. if (myQueue.Transactional) { var myTransaction = new MessageQueueTransaction(); myTransaction.Begin(); myQueue.Send(message, myTransaction); myTransaction.Commit(); } else myQueue.Send(message); } #region private methods private string popNormalQueue(MessageQueue queue, TimeSpan timeOut) { var message = queue.Receive(timeOut); message.Formatter = new XmlMessageFormatter( new String[] { "System.String,mscorlib" }); return message.Body.ToString(); } private string popTransactionalQueue(MessageQueue queue, TimeSpan timeOut) { // Set the formatter. queue.Formatter = new XmlMessageFormatter(new Type[] {typeof(String)}); // Create a transaction. MessageQueueTransaction myTransaction = new MessageQueueTransaction(); String message=string.Empty; try { myTransaction.Begin(); Message myMessage = queue.Receive(timeOut, myTransaction); message = (String)myMessage.Body; myTransaction.Commit(); } catch (MessageQueueException e) { myTransaction.Abort(); throw e; } return message; } #endregion }
How do I get the XML SOAP request of an WCF Web service request?
I'm calling this web service within code and I would like to see the XML, but I can't find a property that exposes it.
I think you meant that you want to see the XML at the client, not trace it at the server. In that case, your answer is in the question I linked above, and also at How to Inspect or Modify Messages on the Client. But, since the .NET 4 version of that article is missing its C#, and the .NET 3.5 example has some confusion (if not a bug) in it, here it is expanded for your purpose. You can intercept the message before it goes out using an IClientMessageInspector: using System.ServiceModel.Dispatcher; public class MyMessageInspector : IClientMessageInspector { } The methods in that interface, BeforeSendRequest and AfterReceiveReply, give you access to the request and reply. To use the inspector, you need to add it to an IEndpointBehavior: using System.ServiceModel.Description; public class InspectorBehavior : IEndpointBehavior { public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(new MyMessageInspector()); } } You can leave the other methods of that interface as empty implementations, unless you want to use their functionality, too. Read the how-to for more details. After you instantiate the client, add the behavior to the endpoint. Using default names from the sample WCF project: ServiceReference1.Service1Client client = new ServiceReference1.Service1Client(); client.Endpoint.Behaviors.Add(new InspectorBehavior()); client.GetData(123); Set a breakpoint in MyMessageInspector.BeforeSendRequest(); request.ToString() is overloaded to show the XML. If you are going to manipulate the messages at all, you have to work on a copy of the message. See Using the Message Class for details. Thanks to Zach Bonham's answer at another question for finding these links.
Option 1 Use message tracing/logging. Have a look here and here. Option 2 You can always use Fiddler to see the HTTP requests and response. Option 3 Use System.Net tracing.
I just wanted to add this to the answer from Kimberly. Maybe it can save some time and avoid compilation errors for not implementing all methods that the IEndpointBehaviour interface requires. Best regards Nicki /* // This is just to illustrate how it can be implemented on an imperative declarared binding, channel and client. string url = "SOME WCF URL"; BasicHttpBinding wsBinding = new BasicHttpBinding(); EndpointAddress endpointAddress = new EndpointAddress(url); ChannelFactory<ISomeService> channelFactory = new ChannelFactory<ISomeService>(wsBinding, endpointAddress); channelFactory.Endpoint.Behaviors.Add(new InspectorBehavior()); ISomeService client = channelFactory.CreateChannel(); */ public class InspectorBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { // No implementation necessary } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(new MyMessageInspector()); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // No implementation necessary } public void Validate(ServiceEndpoint endpoint) { // No implementation necessary } } public class MyMessageInspector : IClientMessageInspector { public object BeforeSendRequest(ref Message request, IClientChannel channel) { // Do something with the SOAP request string request = request.ToString(); return null; } public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { // Do something with the SOAP reply string replySoap = reply.ToString(); } }
OperationContext.Current.RequestContext.RequestMessage this context is accesible server side during processing of request. This doesn`t works for one-way operations
Simply we can trace the request message as. OperationContext context = OperationContext.Current; if (context != null && context.RequestContext != null) { Message msg = context.RequestContext.RequestMessage; string reqXML = msg.ToString(); }
I am using below solution for IIS hosting in ASP.NET compatibility mode. Credits to Rodney Viana's MSDN blog. Add following to your web.config under appSettings: <add key="LogPath" value="C:\\logpath" /> <add key="LogRequestResponse" value="true" /> Replace your global.asax.cs with below (also fix namespace name): using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Security; using System.Web.SessionState; using System.Text; using System.IO; using System.Configuration; namespace Yournamespace { public class Global : System.Web.HttpApplication { protected static bool LogFlag; protected static string fileNameBase; protected static string ext = "log"; // One file name per day protected string FileName { get { return String.Format("{0}{1}.{2}", fileNameBase, DateTime.Now.ToString("yyyy-MM-dd"), ext); } } protected void Application_Start(object sender, EventArgs e) { LogFlag = bool.Parse(ConfigurationManager.AppSettings["LogRequestResponse"].ToString()); fileNameBase = ConfigurationManager.AppSettings["LogPath"].ToString() + #"\C5API-"; } protected void Session_Start(object sender, EventArgs e) { } protected void Application_BeginRequest(object sender, EventArgs e) { if (LogFlag) { // Creates a unique id to match Rquests with Responses string id = String.Format("Id: {0} Uri: {1}", Guid.NewGuid(), Request.Url); FilterSaveLog input = new FilterSaveLog(HttpContext.Current, Request.Filter, FileName, id); Request.Filter = input; input.SetFilter(false); FilterSaveLog output = new FilterSaveLog(HttpContext.Current, Response.Filter, FileName, id); output.SetFilter(true); Response.Filter = output; } } protected void Application_AuthenticateRequest(object sender, EventArgs e) { } protected void Application_Error(object sender, EventArgs e) { } protected void Session_End(object sender, EventArgs e) { } protected void Application_End(object sender, EventArgs e) { } } class FilterSaveLog : Stream { protected static string fileNameGlobal = null; protected string fileName = null; protected static object writeLock = null; protected Stream sinkStream; protected bool inDisk; protected bool isClosed; protected string id; protected bool isResponse; protected HttpContext context; public FilterSaveLog(HttpContext Context, Stream Sink, string FileName, string Id) { // One lock per file name if (String.IsNullOrWhiteSpace(fileNameGlobal) || fileNameGlobal.ToUpper() != fileNameGlobal.ToUpper()) { fileNameGlobal = FileName; writeLock = new object(); } context = Context; fileName = FileName; id = Id; sinkStream = Sink; inDisk = false; isClosed = false; } public void SetFilter(bool IsResponse) { isResponse = IsResponse; id = (isResponse ? "Reponse " : "Request ") + id; // // For Request only read the incoming stream and log it as it will not be "filtered" for a WCF request // if (!IsResponse) { AppendToFile(String.Format("at {0} --------------------------------------------", DateTime.Now)); AppendToFile(id); if (context.Request.InputStream.Length > 0) { context.Request.InputStream.Position = 0; byte[] rawBytes = new byte[context.Request.InputStream.Length]; context.Request.InputStream.Read(rawBytes, 0, rawBytes.Length); context.Request.InputStream.Position = 0; AppendToFile(rawBytes); } else { AppendToFile("(no body)"); } } } public void AppendToFile(string Text) { byte[] strArray = Encoding.UTF8.GetBytes(Text); AppendToFile(strArray); } public void AppendToFile(byte[] RawBytes) { bool myLock = System.Threading.Monitor.TryEnter(writeLock, 100); if (myLock) { try { using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite)) { stream.Position = stream.Length; stream.Write(RawBytes, 0, RawBytes.Length); stream.WriteByte(13); stream.WriteByte(10); } } catch (Exception ex) { string str = string.Format("Unable to create log. Type: {0} Message: {1}\nStack:{2}", ex, ex.Message, ex.StackTrace); System.Diagnostics.Debug.WriteLine(str); System.Diagnostics.Debug.Flush(); } finally { System.Threading.Monitor.Exit(writeLock); } } } public override bool CanRead { get { return sinkStream.CanRead; } } public override bool CanSeek { get { return sinkStream.CanSeek; } } public override bool CanWrite { get { return sinkStream.CanWrite; } } public override long Length { get { return sinkStream.Length; } } public override long Position { get { return sinkStream.Position; } set { sinkStream.Position = value; } } // // For WCF this code will never be reached // public override int Read(byte[] buffer, int offset, int count) { int c = sinkStream.Read(buffer, offset, count); return c; } public override long Seek(long offset, System.IO.SeekOrigin direction) { return sinkStream.Seek(offset, direction); } public override void SetLength(long length) { sinkStream.SetLength(length); } public override void Close() { sinkStream.Close(); isClosed = true; } public override void Flush() { sinkStream.Flush(); } // For streamed responses (i.e. not buffered) there will be more than one Response (but the id will match the Request) public override void Write(byte[] buffer, int offset, int count) { sinkStream.Write(buffer, offset, count); AppendToFile(String.Format("at {0} --------------------------------------------", DateTime.Now)); AppendToFile(id); AppendToFile(buffer); } } } It should create log file in the folder LogPath with request and response XML.
There is an another way to see XML SOAP - custom MessageEncoder. The main difference from IClientMessageInspector is that it works on lower level, so it captures original byte content including any malformed xml. In order to implement tracing using this approach you need to wrap a standard textMessageEncoding with custom message encoder as new binding element and apply that custom binding to endpoint in your config. Also you can see as example how I did it in my project - wrapping textMessageEncoding, logging encoder, custom binding element and config.