Chat application with WebApi/WebSockets backend - c#

I am trying to write a simple chat application with iOS and Android clients. I am following the tutorial here http://blogs.msdn.com/b/youssefm/archive/2012/07/17/building-real-time-web-apps-with-asp-net-webapi-and-websockets.aspx to use WebSockets. However, I need to be able to send messages to a single "Chatroom" rather than broadcast to all the users (in reality there will only ever be 2 users, me and the person I'm chatting with). Currently I am doing this:
using Microsoft.Web.WebSockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
namespace ChatPractice.Controllers
{
public class ChatController : ApiController
{
public HttpResponseMessage Get(string username, int roomId)
{
HttpContext.Current.AcceptWebSocketRequest(new ChatWebSocketHandler(username, roomId));
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
}
class ChatWebSocketHandler : WebSocketHandler
{
private static WebSocketCollection _chatClients = new WebSocketCollection();
private string _username;
private int _roomId;
public ChatWebSocketHandler(string username, int roomId)
{
_username = username;
_roomId = roomId;
}
public override void OnOpen()
{
_chatClients.Add(this);
}
public override void OnMessage(string message)
{
var room = _chatClients.Where(x => ((ChatWebSocketHandler)x)._roomId == _roomId);
message = _username + ": " + message;
foreach (var user in room)
user.Send(message);
//_chatClients.Broadcast(_username + ": " + message);
}
}
}
Is using linq to get the correct room here correct or is there a better way to get to it?

This is an example of a chat server using WebSockets: Chat application using Reactive Extennsions.
It uses another WebSocket framework, but the idea should be pretty much the same.

It's better to use SignalR. There's a lot of tutorials online.

Related

scope issue with named pipes

I have an app that has two components. One is a C# console app that will run as a service in production. The other component is the UI which is a WPF app that runs as a system tray app. This article is what I used to get off the ground. Both apps target .NET6.
I need to send messages through the named pipe, which is implemented with the H.pipes nuget package, from both the console app and the WPF app. I am currently defining the named pipes object as a static variable in a static class so I can access it across multiple classes. Like so:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WindowsTapAgent
{
internal static class Globals
{
public static NamedPipesServer pipeServer;
}
}
The named pipe is initialized in the console app like this:
using Newtonsoft.Json;
using Serilog;
using WindowsTapAgent;
using System.Net.NetworkInformation;
using System.Reflection;
string logFileName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + "DrawbridgeAgent.log";
Log.Logger = new LoggerConfiguration()
// add a rolling file for all logs
.WriteTo.File(logFileName,
fileSizeLimitBytes: 2000000)
.WriteTo.Console()
.WriteTo.EventLog("Drawbridge Agent Source", manageEventSource: true)
// set default minimum level
.MinimumLevel.Information()
.CreateLogger();
Globals.pipeServer = new NamedPipesServer();
Globals.pipeServer.InitializeAsync().GetAwaiter().GetResult();
...
...
...
...
The actual code that accesses the named pipe is as follows:
using Serilog;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;
namespace WindowsTapAgent
{
public class TapDevice
{
public RsaKey RsaKey { get; set; }
//private NamedPipesServer PipeServer { get; set; }
public TapDevice(AgentConfigInfo info)
{
Log.Information("In TapDevice Constructor");
string containerName = info.RsaKeyContainerName;
Log.Information("ContainerName: " + containerName);
RsaKey = new RsaKey(containerName);
Log.Information("RSAKey Public Key: " + RsaKey.getPublicKeyData());
}
public async Task<AgentConfigInfo> Register(RegistrationToken regToken, AgentConfigInfo info)
{
try
{
Log.Information("-----------------------------Attempting to register------------------------------.");
if (Globals.pipeServer != null)
Globals.pipeServer.SendMessage("Registering with Drawbridge");
...
...
...
NamedPipeServer class:
using H.Pipes;
using H.Pipes.Args;
using Common;
namespace WindowsTapAgent
{
public class NamedPipesServer : IDisposable
{
const string PIPE_NAME = "drawbridgepipe";
private PipeServer<PipeMessage> server;
private PipeConnection<PipeMessage> connection;
public async Task InitializeAsync()
{
server = new PipeServer<PipeMessage>(PIPE_NAME);
server.ClientConnected += async (o, args) => await OnClientConnectedAsync(args);
server.ClientDisconnected += (o, args) => OnClientDisconnected(args);
server.MessageReceived += (sender, args) => OnMessageReceived(args.Message);
server.ExceptionOccurred += (o, args) => OnExceptionOccurred(args.Exception);
await server.StartAsync();
}
private async Task OnClientConnectedAsync(ConnectionEventArgs<PipeMessage> args)
{
//Console.WriteLine($"Client {args.Connection.Id} is now connected!");
connection = args.Connection;
}
...
...
...
The problem is that the TapDevice class is used in both the console app and the WPF app. When the PipeServer object is accessed through the TapDevice class from the console app (where it's also declared and initialized) everything works fine. When it's called from the WPF app, again through the TapDevice class, the PipeServer object is always null, and can't be used. How should I better structure this project?
I figured it out. I had the client and server mixed up. I needed to put the server on the UI side, then I can can create multiple client connections to it as needed. This eliminates the need for the messy global variable that didn't work anyways.

SignalR only works the first time it is called

I have a ASP.NET Core MVC 5 website. I am using SignalR to send "Notifications" from the model layer to the client / view.
If I open my index page, It uses SignalR to send a list of available cameras as they are discovered. I then again use SignalR to send images that the camera is taking on a different model. However, only the first one works.
If I navigate to https://localhost:44303/camera/live/?IP=192.168.50.212 It starts sending images, but will not discover other cameras. If I navigate to the discovery first, the discovery works just fine.
In both the models, the line Hub.Clients.All.SendAsync("method", data); is executing. In both models, the Hub is defined as
public Microsoft.AspNetCore.SignalR.IHubContext<MasterHub> Hub { get; internal set; }`
and each model has a separate controller, that sets the hub context like so:
private readonly IHubContext<MasterHub> _hubContext;
public CameraController(IHubContext<MasterHub> hubContext)
{
_hubContext = hubContext;
}
However, only the first one I navegate to works.
Do I have to close the SignalR connection after sending a message to use it again? If so, How would I do this?
both controllers look like:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Thermal_Screening.Hubs;
using Thermal_Screening.Models;
namespace Thermal_Screening.Controllers
{
public class CameraController : Controller
{
public string CameraName;
private readonly IHubContext<MasterHub> _hubContext;
public CameraController(IHubContext<MasterHub> hubContext)
{
_hubContext = hubContext;
}
public IActionResult Live(string IP)
{
CameraName = getCameraNameFromIP(IP); // doin it this way causes a 2s delay, should get ip in model
return View(new CameraViewModel(IP) { Hub = _hubContext, IP = IP, CameraName = CameraName });
}
public IActionResult Settings(string IP)
{
CameraName = getCameraNameFromIP(IP);
return View(new CameraViewModel(IP) { Hub = _hubContext, IP = IP, CameraName = CameraName });
}
public IActionResult Log(string IP)
{
CameraName = getCameraNameFromIP(IP);
return View(new CameraViewModel(IP) { Hub = _hubContext, IP = IP, CameraName = CameraName });
}
private string getCameraNameFromIP(string IP)
{
WebClient x = new WebClient();
string source = x.DownloadString("http://" + IP);
return Regex.Match(source, #"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>", RegexOptions.IgnoreCase).Groups["Title"].Value;
}
}
}
both viewmodels look like:
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Extensions;
using Flir.Atlas.Live.Discovery;
using Flir.Atlas.Live.Device;
using Flir.Atlas.Live;
using System.IO;
using Flir.Atlas.Image;
using System.Drawing;
using Microsoft.AspNetCore.SignalR;
using Thermal_Screening.Hubs;
namespace Thermal_Screening.Models
{
public class CameraViewModel
{
public Microsoft.AspNetCore.SignalR.IHubContext<MasterHub> Hub { get; internal set; }
//removed for berevity
Hub.Clients.All.SendAsync("method", "data");

PlatformChecks.RequestPermissions is not available

I am using xamamrin for developing apps for android using c#, the IDE I am using is Visual studio.
I want to use zxing in my app, so I add zxing packet to both core and ui layers.
As you see in the below code, I can instantiate some classes, but I do not have any access to
PlatformChecks.IsPermissionInManifest
I need it to check weather the required permissions are granted or not. Please have a look at the error message I am getting
Please let me know how to use it.
error
Error CS0103: The name 'PlatformChecks' does not exist in the current context (CS0103) (MITScan.UI.Droid)
code:
public static readonly string[] RequiredPermissions = new[] {
Android.Manifest.Permission.Camera
};
protected override async void OnCreate(Android.OS.Bundle bundle)
{
base.OnCreate(bundle);
this.Window.AddFlags(WindowManagerFlags.KeepScreenOn);
SetContentView(Resource.Layout.ZxingLibTestView);
_scannerFragment = new ZXingScannerFragment();
SupportFragmentManager.BeginTransaction().Replace(Resource.Id.tscan_4_view_relativelayout_cameraPreview, _scannerFragment, "Scannerfragment").Commit();
var permissionsToRequest = new List<string>();
// Check and request any permissions
foreach (var permission in RequiredPermissions)
{
if (PlatformChecks.IsPermissionInManifest(this,
permission))
{
if (!PlatformChecks.IsPermissionGranted(this,
permission))
permissionsToRequest.Add(permission);
}
}
if (permissionsToRequest.Any())
{
_waitingForPermission =
PlatformChecks.RequestPermissions(this,
permissionsToRequest.ToArray(), 101);
}
}
Note:
I am using Zxing for scanning bar codes.
I was going through the Zebra Crossing's(ZXing) docs after I read this question since I knew there is no such Android class and found that PlatformChecks is a Custom Class as below:
public class PlatformChecks
{
public const string PERMISSION_CAMERA = "android.permission.CAMERA";
public const string PERMISSION_FLASHLIGHT = "android.permission.FLASHLIGHT";
public static bool HasCameraPermission(Context context)
{
return HasPermission (context, PERMISSION_CAMERA);
}
public static bool HasFlashlightPermission(Context context)
{
return HasPermission (context, PERMISSION_FLASHLIGHT);
}
static bool HasPermission(Context context, string permission)
{
PermissionInfo pi = null;
try { pi = context.PackageManager.GetPermissionInfo (PERMISSION_CAMERA, PackageInfoFlags.Permissions); }
catch { }
return pi != null;
}
}
And has the following using's
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Content.PM;

Convert windows form application to Asp lr console application

I am using vs 2017rc and I have compatibility issues. I can't add windows form doll to my project and when I try to convert the code from win forms to Asp k get issues. Maybe I am doing it wrong but it seem to work on vs2015.
Please I need help to solve this. Maybe I am doing it wrong. See the code below.
using DotNetBrowser;
using DotNetBrowser.WinForms;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
namespace GetAjaxResponseBodySample
{
public partial class Form1 : Form
{
private static List<string> ajaxUrls = new List<string>();
private WinFormsBrowserView browserView;
public Form1()
{
InitializeComponent();
browserView = new WinFormsBrowserView();
browserView.Browser.Context.NetworkService.ResourceHandler = new AjaxResourceHandler();
browserView.Browser.Context.NetworkService.NetworkDelegate = new AjaxNetworkDelegate();
Controls.Add(browserView);
browserView.Browser.LoadURL("http://www.w3schools.com/xml/ajax_examples.asp");
}
private class AjaxResourceHandler : ResourceHandler
{
public bool CanLoadResource(ResourceParams parameters)
{
if (parameters.ResourceType == ResourceType.XHR)
{
Debug.WriteLine("Intercepted AJAX request: " + parameters.URL);
ajaxUrls.Add(parameters.URL);
}
return true;
}
}
private class AjaxNetworkDelegate : DefaultNetworkDelegate
{
public override void OnDataReceived(DataReceivedParams parameters)
{
if (ajaxUrls.Contains(parameters.Url))
{
Debug.WriteLine("Captured response for: " + parameters.Url);
Debug.WriteLine("MimeType = " + parameters.MimeType);
Debug.WriteLine("Charset = " + parameters.Charset);
PrintResponseData(parameters.Data);
}
}
private void PrintResponseData(byte[] data) {
Debug.WriteLine("Data = ");
var str = Encoding.Default.GetString(data);
Debug.WriteLine(str);
}
}
}
Am not concerned with the browser view... I already get the Jason I need from the Ajax response body.
It is possible to use Browser in a headless mode without creating BrowserView at all.
The following sample code works in the web application on VS2017rc with ASP.NET Core Web Application (.NET Framework) or ASP.NET Web Application (.NET Framework).
Please take into account that it is necessary to Dispose browser after the response body has been captured.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using DotNetBrowser;
using System.Diagnostics;
using System.Text;
namespace WebApplication7.Controllers
{
public class HomeController : Controller
{
private static List<string> ajaxUrls = new List<string>();
Browser browser;
public string Index()
{
Init();
return "Test page";
}
private void Init()
{
browser = BrowserFactory.Create();
browser.Context.NetworkService.ResourceHandler = new AjaxResourceHandler();
browser.Context.NetworkService.NetworkDelegate = new AjaxNetworkDelegate();
browser.LoadURL("https://www.w3schools.com/xml/ajax_examples.asp");
}
private class AjaxResourceHandler : ResourceHandler
{
public bool CanLoadResource(ResourceParams parameters)
{
if (parameters.ResourceType == ResourceType.XHR)
{
Debug.WriteLine("Intercepted AJAX request: " + parameters.URL);
ajaxUrls.Add(parameters.URL);
}
return true;
}
}
private class AjaxNetworkDelegate : DefaultNetworkDelegate
{
public override void OnDataReceived(DataReceivedParams parameters)
{
if (ajaxUrls.Contains(parameters.Url))
{
Debug.WriteLine("Captured response for: " + parameters.Url);
Debug.WriteLine("MimeType = " + parameters.MimeType);
Debug.WriteLine("Charset = " + parameters.Charset);
PrintResponseData(parameters.Data);
}
}
private void PrintResponseData(byte[] data)
{
Debug.WriteLine("Data = ");
var str = Encoding.UTF8.GetString(data);
Debug.WriteLine(str);
}
}
}
}

Xamarin.Forms Connecting to Web Services

Good Day Everyone. I'm creating a simple Xamarin.Forms Portable Application in my Visual Studio 2015.
I want my Mobile Application to connect to the SQL Database I have in my VS2015 and return a LIST OF CUSTOMERS which should be display to my mobile phone.
I have created a Xamarin Portable project and a WebForms project that will handle my Web Services and Database.
In my WebForms project, I created a Controller that should return the List of Customers. This has a web service URL api/Customer that I used to connect to the RestClient in my Xamarin Portable. I also have CustomerViewModel that should represent the data in my application.
In my Xamarin Portable project, I have a ClientList.xaml that should display the List that comes from my database. I also have a CustomerVM that is connected to Services and my RestClient. My RestClient used the WEB SERVICE URL to get the List of Customer from my WebForms project.
Based on the given steps above, I still wasn't able to display the Data in my mobile phone. What do you think is the reason behind this? Thanks for your help.
Here are some of my codes:
RestClient.cs
public class RestClient_Customer<T>
{
private const string WebServiceUrl = "http://localhost:50857/api/Customer/";
public async Task<List<T>> GetCustomerAsync()
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var json = await httpClient.GetStringAsync(WebServiceUrl);
var taskModels = JsonConvert.DeserializeObject<List<T>>(json);
return taskModels;
}
}
CustomerServices.cs
using Plugin.RestClient;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using XamarinFormsDemo.Models;
namespace XamarinFormsDemo.Services
{
public class CustomerServices
{
public async Task<List<Customer>> GetCustomerAsync()
{
RestClient_Customer<Customer> restClient = new RestClient_Customer<Customer>();
var customerList = await restClient.GetCustomerAsync(); //yung getasync ay pantawag as restclient
return customerList;
}
}
}
CustomerVM.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using XamarinFormsDemo.Models;
using XamarinFormsDemo.Services;
using XamarinFormsDemo.Views;
namespace XamarinFormsDemo.ViewModels
{
public class CustomerVM : INotifyPropertyChanged
{
private List<Customer> _customerList; // keep all customers
private List<Customer> _searchedCustomerList; // keep a copy for searching
private Customer _selectedCustomer = new Customer();
private string _keyword = "";
public string Keyword
{
get
{
return _keyword;
}
set
{
this._keyword = value;
// while keyword changed we filter Employees
//Filter();
}
}
private void Filter()
{
if (string.IsNullOrWhiteSpace(_keyword))
{
CustomerList = _searchedCustomerList;
}
else
{
// var lowerKeyword = _keyword.ToLower();
CustomerList = _searchedCustomerList.Where(r => r.CUSTOMER_NAME.ToLower().Contains(_keyword.ToLower())).ToList();
// EmployeesList = _searchedEmployeesList.Where(r => r.EMPLOYEE_NAME.Contains(_keyword)).ToList();
}
}
public List<Customer> CustomerList
{
get
{
return _customerList;
}
set
{
_customerList = value;
OnPropertyChanged();
}
}
public CustomerVM()
{
InitializeDataAsync();
}
private async Task InitializeDataAsync()
{
var customerServices = new CustomerServices();
_searchedCustomerList = await customerServices.GetCustomerAsync();
CustomerList = await customerServices.GetCustomerAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I think the problem is in your services hope this will help you,
public interface ICustomer
{
Task<string> GetCustomers();
}
public class CustomerService : ICustomer
{
public async Task<string> GetCustomers()
{
var client = new HttpClient();
var response = await client.GetAsync(string.Format("http://mysite/api/Customer"));
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
}
Call it anywhere you like
var _custList = new GetCustomers();
var returnJson = await _custList.GetCustomers();
Note the return is json string format or xml format depending on your REST API so you need to parse this first before you can get the value and display it to ListView
Try running it in UWP. If it works in UWP then you have to take a look at
Xamarin HttpClient.GetStringAsync not working on Xamarin.Droid
I had the same issue but when I tried it in UWP it worked fine. I am still seeking for the solution to run xamarin.android using device.

Categories