I was using Alchemy websockets for both my client and server but ran into a problem with corrupted/dropped messsages. So I'm trying out another server side implementation. I implemented the server using Fleck, and when I send messages using javascript, the server receives all the messages, solving my previous problem.
However, I need to be able to send messages to the websocket server from a C# client also. Since Fleck does not have a client side implementation in C#, I thought I'd stick with Alchemy. I left the client-side code unchanged so I thought it should just connect to the server as before, however, no messages are being received (though they are being sent according to the debugger).
Here is my server side implementation (Fleck):
private void OnStartWebSocketServer()
{
var server = new WebSocketServer("ws://localhost:11005");
server.Start(socket =>
{
socket.OnOpen = () => Console.WriteLine("Open!");
socket.OnClose = () => Console.WriteLine("Close!");
socket.OnMessage = message => OnReceive(message);
});
}
private static void OnReceive(String message)
{
UpdateUserLocation(message);
}
Here is my client side implementation (Alchemy):
class WSclient
{
WebSocketClient aClient;
public WSclient(String host, String port)
{
aClient = new WebSocketClient("ws://" + host + ":" + 11005 + "/chat")
{
OnReceive = OnReceive,
OnSend = OnSend,
OnConnect = OnConnected,
OnConnected = OnConnect,
OnDisconnect = OnDisconnect
};
aClient.Connect();
}
...
public void Send(String data)
{
aClient.Send(data);
}
I thought it might have something to do with the fact that the Alchemy client requires a channel at the end of the connection string '/chat'. However leaving it blank, or just the '/' gives an error.
Related
I built a Websocket Server in Java with Spring Boot
My server configuration looks like:
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/my-websocket")
.setAllowedOrigins("*")
.withSockJS();
}
I've created a client in Javascript which works, and the connection looks like:
function connect() {
var myUsername = document.getElementById('from').value;
var host = 'http://localhost:8080/my-websocket';
var socket = new SockJS(host);
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
stompClient.send('/app/status', {}, JSON.stringify(createUserStatus('AVAILABLE')));
stompClient.subscribe('/topic/status', function (message) {
receiveStatus(JSON.parse(message.body));
});
stompClient.subscribe('/queue/private.message.' + myUsername, function (message) {
displayMessage(JSON.parse(message.body));
});
});
}
Now I want to create a client in .NET, but every Stomp client library I've found, doesn't allow http to be the URI, and my server only accepts http connections. My server fails to parse ws and tcp connections, and I can't find a client out there that uses http.
In my .NET client, when I try to connect to string brokerUri = "tcp://localhost:8080/my-websocket"
My server throws this exception:
client-id:ID:DESKTOP-AA29FJ6-52716-637205011942817986-0:0
host:localhost
accept-version:1.0,1.1
heart-beat:10000,30000
]
2020-03-22 19:13:14.414 DEBUG 25036 --- [nio-8080-exec-2] o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header
java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
How can I create a Websocket connection in .NET that can subscribe to different topics and queues, and send messages over http?
Alternatively, how could I make my server handle ws or tcp connections?
Thank you!
I have written an Application where I am using SignalR. I am sending connectionId from Client to Server(controller).
Everything is working fine with single browser (request will sent to server with connectionId="conn_1") and signalR is sending response to only conn_1, but when i open new browser and send a request from that client the previous connection gets disposed. Which means only one connection with particular connectionId remains alive.
Is there any way SignalR can not dispose and send response to both with data they want?
I am new to SignalR and would really appropriate any help or guidance.
Angular SignalRService to start connection with server
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(this.paymentDraftHubUrl)
.build();
return this.hubConnection
.start()
.then(() => this.hubConnectionStatus = 'Connection started')
.catch(err => (this.hubConnectionStatus = 'Error while starting connection: ' + err));
}
sending connectionId from client component to Api
this.signalRService.startConnection().then((connection) => {
this.connectionId = connection.toString();
//Calling Api
this.getAllTransactionException(
this.connectionId,
this.pageNumber,
this.pageSize
}
MyHub class in C#
public class PaymentDraftServiceHub : Hub, IPaymentDraftHub
{}
Controller for API
using timer to keep calling repository for new data,
[HttpGet]
[Route("GetCsrTranactions")]
public IActionResult GetCsrTranactions([FromQuery] TransactionExceptionDataRequest queryParams)
{
TimeManager.Dispose();
var timerManager = new TimeManager(async () =>
await _paymentDraftHub.Clients.Clients.Client(queryParams.ConnectionId).SendAsync(SignalRConstants.TransferPaymentDraftServiceData, await _paymentTransactionRepository.GetCsrTranactionsAsync(queryParams)));
var response = new ResponseMessage { Message = "Accepted", Code = "201" };
return Ok(response);
}
Client can have multiple connections with multiple connection IDs if client connect from multiple browser windows or tabs.
According to the code you provided, we can find that you just pass connection ID of SignalR client within current active browser tab/window to your controller, and in your controller action, you use this code snippet .Client(queryParams.ConnectionId).SendAsync() to send message to a specific client, so other browser windows or tabs would not receive the message.
If you'd like to send message(s) to a client with multiple connections, you need to map SignalR users to connection Ids and retain information about users-to-connectionIds mapping, then you can get all connectionIds of a client and send messages to that client with with multiple connectionIds, like below.
//code logic here
//to get all connectinIds of a client/user
//from user-to-connectionIds mapping table
await _paymentDraftHub.Clients.Clients(connectionIds_here).SendAsync("method_here",args_here);
I have an P2P network app which acts as a server and client.
The app connects to multiple clients in a mesh and sends "data" to each other once the data has been processed.
If a client gets an incoming request then creates their own request before completing the incoming request it seems to deadlock and neither requests complete. Everything works fine unless a create is sent which processing one.
Is this because I'm not using async on the server methods or is this because I shouldn't send a request while processing one?
proto
rpc submit_data(DataSubmission) returns (DataSubmissionAck);
RPC Server
public async void Start()
{
_server = new Server()
{
Services = {
DataService.BindService(new DataService())),
},
Ports = { new ServerPort(_IpAddress, _Port, ServerCredentials.Insecure) }
};
_server.Start();
}
Client method
public void SubmitData(Data data)
{
...
var serverResponse = _serviceClient.submit_data(request);
...
}
Server method
public override Task<DataSubmissionAck> submit_data(DataSubmission request, ServerCallContext context)
{
DataSubmissionAck clientResponse = new DataSubmissionAck();
return Task.FromResult(clientResponse);
}
What seems to have worked is making the server and client-side calls all async.
I have an API controller that creates a web socket connection and can connect and interact with fine when using JavaScript, but I'd also like to be able to connect to it using a C# client. I've tried a few different libraries (Alchemy, WebSocket4Net, and WebSocket-Sharp), and they all seem to be timing out during connection open.
Here's the code for the server side:
class WsController : ApiController
{
public HttpResponseMessage Get(string id)
{
HttpContext.Current.AcceptWebSocketRequest(new WsHandler(id));
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
internal class WsHandler : WebSocketHandler
{
public WsHandler(string id) { /* ... */ }
}
}
If I'm using a JS client, I can use var ws = new WebSocket(url + "?id=someid"); to connect and then send and receive messages. When using a C# client, I get stuck during the handshake and the connect times out. Here's some example code using WebSocket4Net.
var ws = new WebSocket(url);
ws.Opened += new EventHandler(ws_Connect);
ws.Closed += new EventHandler(ws_Disconnect);
ws.Error += new EventHandler<ErrorEventArgs>(ws_Error);
ws.MessageReceived += new EventHandler<MessageReceivedEventArgs>(ws_Receive);
ws.Open(); // Timeout on this line. No errors are thrown to ws_Error.
Is there another way to create a websocket server using MVC's ApiController?
I have implemented SignalR for my Windows Azure project. I have two clients - Javascript/HTML client in my web role and a console application in my project. And Web role is my SignalR server. When i put the web role and the console application as the start up projects, the messages i send from the HTML client are sent to the console application. But when i put the Cloud project and the console application as the start up projects, the messages from the HTML client are not being sent to the console application. Its really weird, i dont know what could be the difference between the two which is causing the problem.
And if i put a background thread in my web role which will send messages to connected clients periodically, it works on both occasions, i mean the console app and the HTML client are receiving messages irrespective of the start up projects.
Please let me know if you have any idea what the problem is
My Hub:
public class BroadcastHub : Hub
{
public void Send(PersistedAudioRecord record)
{
// Call the BroadcastAudio method to update clients.
Clients.All.BroadcastAudio(record);
}
}
My HTML/Javascript client:
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var broadcast = $.connection.broadcastHub;
// Create a function that the hub can call to broadcast messages.
broadcast.client.broadcastAudio = function (record) {
// Html encode user name, channel and title.
var encodedName = $('<div />').text(record.Username).html();
var encodedChannel = $('<div />').text(record.Channel).html();
var encodedTitle = $('<div />').text(record.Title).html();
// Add the broadcast to the page.
$('#broadcasts').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedChannel + '</strong>: ' + encodedTitle + '</li>');
};
// Get the user name.
$('#displayname').val(prompt('Enter your name:', ''));
// Get the Channel name to which you want to broadcast.
$('#channelname').val(prompt('Enter Channel:', ''));
// Set initial focus to message input box.
$('#title').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendbroadcast').click(function () {
// Call the Send method on the hub.
var broadcastMessage = {}
broadcastMessage.Username = $('#displayname').val();
broadcastMessage.Channel = $('#channelname').val();
broadcastMessage.Title = $('#title').val();
broadcast.server.send(broadcastMessage);
// Clear text box and reset focus for next broadcast.
$('#title').val('').focus();
});
});
});
</script>
My Console app client:
class Program
{
static void Main(string[] args)
{
HubConnection connection = new HubConnection("http://localhost:35540/");
IHubProxy proxy = connection.CreateHubProxy("BroadcastHub");
proxy.On<AudioRecord>("BroadcastAudio", BroadcastAudio);
connection.Start().Wait();
Console.ReadLine();
}
static void BroadcastAudio(AudioRecord record)
{
Console.WriteLine("Broadcast: {0} {1} {2}", record.Username, record.Channel, record.Title);
}
}
Background Thread:
public class BackgroundThread
{
private static Random _random = new Random();
public static void Start()
{
ThreadPool.QueueUserWorkItem(_ =>
{
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<BroadcastHub>();
while (true)
{
PersistedAudioRecord record = new PersistedAudioRecord();
record.Channel = _random.Next(10).ToString();
record.Username = new string('a', Convert.ToInt32(record.Channel));
record.Title = new string('b', Convert.ToInt32(record.Channel));
try
{
hubContext.Clients.All.BroadcastAudio(record);
}
catch (Exception ex)
{
System.Diagnostics.Trace.TraceError("SignalR error thrown: {0}", ex);
}
Thread.Sleep(TimeSpan.FromSeconds(2));
}
});
}
}
I tried this scenario with my application and I was able to send messages from a webrole to a console application. Is it possible to zip your project and send it to see if this reproes...