I'm trying to create a simple C# websocket server with websocket-sharp
and I followed the official guide but I got some peculiar reply instead.
C# Snippet
using System;
using WebSocketSharp.Server;
namespace WebSocketSharp
{
class MainClass
{
public static void Main(string[] args)
{
startServer();
}
public static void startServer()
{
var ws = new WebSocketServer(System.Net.IPAddress.Any, 8181);
ws.Start();
Console.WriteLine("Server started");
Console.ReadKey(true);
ws.Stop();
Console.WriteLine("Server stopped");
}
}
}
When I tried to connect my client (HTML) to the server, I got a message
WebSocket connection to 'ws://127.0.0.1:8181/' failed: Error during WebSocket handshake: Unexpected response code: 501
and this showed up at my terminal at the same time
According to HTTP Protocol, code 501 means
The server does not support the functionality required to fulfill the request. This is the appropriate response when the server does not recognize the request method and is not capable of supporting it for any resource.
This is my client code and I believe it conforms with the request method.
<!doctype html>
<head>
</head>
<body>
<form>
<input id="textField1" type="text" style="margin: 0 5px; width: 200px; height: 40px;" placeholder="Enter data">
<button id="sendTextField1" type="button" class="btn btn-info">Send</button>
</form>
<script type="text/javascript">
var start = function () {
var wsImpl = window.WebSocket || window.MozWebSocket;
window.ws = new wsImpl('ws://127.0.0.1:8181/');
ws.onmessage = function (evt) {
console.log(evt.data);
};
ws.onopen = function () {
console.log("Connected");
};
ws.onclose = function () {
console.log("Closed");
};
document.getElementById("sendTextField1").onclick = function() {
sendToSocket(document.getElementById('textField1').value);
};
function sendToSocket(value){
console.log("Sending value to socket " + value + " ");
ws.send(value);
};
}
window.onload = start;
Can anyone tell me where is my mistake?
I think the problem is you're not adding a behavior to the server. I did the "laputa" thing that is mentioned in the docs here: https://github.com/sta/websocket-sharp.
Don't forget to connect to the right endpoint. In the example the endpoint is "/laputa", so you have to connect in your javascript like:
var socket = new WebSocket('ws://127.0.0.1:8081/laputa');
You need to add a class inherited from WebSocketBehavior, I`m not sure the library will work without it.
The example did not work for me though, I had to call server.Start() before adding behaviors.
I thought this would be helpful as the core of the question is on how to setup the WebSocket server and having some basic exchange with a client. This minimal code creates both, a local Server & Client, who perform a primitive exchange in purpose of demonstrating the basic communication between them:
using System;
using WebSocketSharp;
using WebSocketSharp.Server;
namespace ConsoleApp1
{
public class ConsoleApplication
{
public class ServerService : WebSocketBehavior
{
protected override void OnMessage(MessageEventArgs e)
{
Console.WriteLine("I, the server, received: " + e.Data);
Send("I, the server, send you this!");
}
}
public static void Main(string[] args)
{
// Server
var wssv = new WebSocketServer(1236);
wssv.AddWebSocketService<ServerService>("/service");
wssv.Start();
Console.WriteLine("Server is setup.");
Console.ReadLine();
// Client
var ws = new WebSocket("ws://127.0.0.1:1236/service");
ws.OnMessage += (sender, e) => Console.WriteLine("I, the client, received: " + e.Data);
ws.Connect();
Console.WriteLine("Client is setup.");
Console.ReadLine();
// The client sends a message to the server:
ws.Send("Server, please send me something!");
Console.ReadLine();
}
}
}
Related
So, I have a SignalR self hosted console application and a website running on WAMP. Everything works just fine when I'm using localhost as domain-name. Obviously this only works locally.
I want it to work on other computer too. So I have tried to change localhost to my local ip, both in the c# console application, and also on the website. The console application crashes when I have my local ip, with the error :
An unhandled exception of type
'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
I have also tried to use * instead of localhost in the console application. Like this:
string url = "http://*:8080/servers/2/";
Same goes there, it crashes. What am I doing wrong?
Console application code:
namespace SignalRSelfHost
{
class Program
{
public static IPAddress ipAd { get; set; }
public static TcpListener myList { get; set; }
static void Main(string[] args)
{
// This will *ONLY* bind to localhost, if you want to bind to all addresses
// use http://*:8080 to bind to all addresses.
// See http://msdn.microsoft.com/en-us/library/system.net.httplistener.aspx
// for more information.
string url = "http://localhost:8080/servers/2/";
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
});
}
}
public class MyHub : Hub
{
public void Send(string message)
{
Clients.All.addMessage(message);
}
}
}
And my website's code:
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" />
<input type="hidden" id="displayname" />
<ul id="discussion"></ul>
<!--Script references. -->
<!--Reference the jQuery library. -->
<script src="/assets/js/jquery-1.6.4.min.js"></script>
<!--Reference the SignalR library. -->
<script src="/assets/js/jquery.signalR-2.0.3.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="http://localhost:8080/servers/<?php echo $server->row()->id; ?>/signalr/hubs"></script>
<!--Add script to update the page and send messages.-->
<script type="text/javascript">
$(function () {
var url = "http://localhost:8080/servers/<?php echo $server->row()->id; ?>/signalr";
//Set the hubs URL for the connection
$.connection.hub.url = url;
// Declare a proxy to reference the hub.
var chat = $.connection.myHub;
// Create a function that the hub can call to broadcast messages.
chat.client.addMessage = function (message) {
// Html encode display name and message.
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li>' + encodedMsg + '</li>');
};
// Get the user name and store it to prepend to messages.
//$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
//$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
</script>
Solved it by running the program as administrator.
Problem:
A websocket client(POCO 1.5.1, c++) will not connect to a websocket c# server (command line app with Fleck Library). A timeout is reached with an exception thrown:
Cannot upgrade to WebSocket connection: OK
Exception Code: 1
Poco WebException Documentation
WS_ERR_NO_HANDSHAKE = 1 :
No Connection: Upgrade or Upgrade: websocket header in handshake request.
Fact 1: This websocket client will connect to a Ruby Event Machine websocket server.
Fact 2: A javascript client will connect to the websocket c# server.
Fact 3: The same javascript client will also connect to the websocket ruby server.
Fact 4: The websocket client will not connect to an Alchemy Websocket server neigther. https://github.com/Olivine-Labs/Alchemy-Websockets
Update with Wireshark
POCO is using
GET / HTTP/1.0\r\n
Javascript version:
GET / HTTP/1.1\r\n
All Source Code
Client code in c++
#include "Game.h"
#include <irrlicht.h>
#include "driverChoice.h"
#include <iostream>
#include <assert.h>
#include "Poco/Net/WebSocket.h"
#include "Poco/Net/HTTPClientSession.h"
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/NetException.h"
#include "Poco/Exception.h"
using Poco::Net::HTTPClientSession;
using Poco::Net::HTTPRequest;
using Poco::Net::HTTPResponse;
using Poco::Net::HTTPServerRequest;
using Poco::Net::HTTPServerResponse;
using Poco::Net::WebSocket;
using Poco::Net::WebSocketException;
using Poco::Exception;
// VS2010
// POCO 1.5.1
// Irrlicht 3D engine
// Windows 7 Enterprise edition
Game::Game(void)
{
}
Game::~Game(void)
{
}
//...
void Game::TestWebSocketClient()
{
char buffer[1024];
int flags;
int n;
std::string payload;
try
{
HTTPClientSession cs("localhost", 8080);
HTTPRequest request(HTTPRequest::HTTP_GET, "/ws");
HTTPResponse response;
std::string cmd;
WebSocket * ws = new WebSocket(cs, request, response); // Causes the timeout
payload = "SGClient: Hello World!";
ws->sendFrame(payload.data(), payload.size(), WebSocket::FRAME_TEXT);
n = ws->receiveFrame(buffer, sizeof(buffer), flags);
while( cmd != "exit")
{
cmd = "";
std::cin >> cmd;
ws->sendFrame(cmd.data(), cmd.size(), WebSocket::FRAME_TEXT);
n = ws->receiveFrame(buffer, sizeof(buffer), flags);
if( n > 0 )
{
std::cout << buffer << std::endl;
}
}
ws->shutdown();
}
catch (Exception ex)
{
return;
}
Server code in c#
// vs2010
// fleck library
// Windows 7 enterprise edition
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Fleck;
using System.Timers;
namespace TestWebsocket
{
class Program
{
static void Main()
{
FleckLog.Level = LogLevel.Debug;
var allSockets = new List<IWebSocketConnection>();
var server = new WebSocketServer("ws://localhost:8080");
server.Start(socket =>
{
socket.OnOpen = () =>
{
Console.WriteLine("Open!");
allSockets.Add(socket);
};
socket.OnClose = () =>
{
Console.WriteLine("Close!");
allSockets.Remove(socket);
};
socket.OnMessage = message =>
{
Console.WriteLine(message);
allSockets.ToList().ForEach(s => s.Send("Echo: " + message));
};
});
var input = Console.ReadLine();
while (input != "exit")
{
foreach (var socket in allSockets.ToList())
{
socket.Send(input);
}
input = Console.ReadLine();
}
}
}
}
Alternate HTML/Javascript client code, running in Chrome Version 31.0.1650.57 m
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>websocket client</title>
<script type="text/javascript">
var start = function () {
var inc = document.getElementById('incomming');
var wsImpl = window.WebSocket || window.MozWebSocket;
var form = document.getElementById('sendForm');
var input = document.getElementById('sendText');
inc.innerHTML += "connecting to server ..<br/>";
// create a new websocket and connect
window.ws = new wsImpl('ws://localhost:8080/');
// when data is comming from the server, this metod is called
ws.onmessage = function (evt) {
inc.innerHTML += evt.data + '<br/>';
};
// when the connection is established, this method is called
ws.onopen = function () {
inc.innerHTML += '.. connection open<br/>';
};
// when the connection is closed, this method is called
ws.onclose = function () {
inc.innerHTML += '.. connection closed<br/>';
}
form.addEventListener('submit', function(e){
e.preventDefault();
var val = input.value;
ws.send(val);
input.value = "";
});
}
window.onload = start;
</script>
</head>
<body>
<form id="sendForm">
<input id="sendText" placeholder="Text to send" />
</form>
<pre id="incomming"></pre>
</body>
</html>
Alternate ruby server code using eventmachine running on command line interface
// Ruby 1.9
// gem install em-websocket required.
require 'em-websocket'
EventMachine::WebSocket.start(:host => "localhost", :port => 8080) do |ws|
ws.onopen { ws.send "RS: Hello Client!"}
ws.onmessage {
|msg| ws.send "RS: Pong: #{msg}"
puts msg
}
ws.onclose { puts "WebSocket closed" }
end
The problem was that POCO default HTTP request version was 1.0.
Section 4.1 of the RFC specs indicates that minimum is 1.1:
https://www.rfc-editor.org/rfc/rfc6455#section-4.1
The method of the request MUST be GET, and the HTTP version MUST
be at least 1.1.
For example, if the WebSocket URI is "ws://example.com/chat",
the first line sent should be "GET /chat HTTP/1.1".
Problem was solved by replacing the HTTPRequest construction by:
HTTPRequest request(HTTPRequest::HTTP_GET, "/ws", "HTTP/1.1" );
Cheers!
This simple web socket example is returning a 200 error.
Edit: I am reposting the code in C# in hopes that more people will be able to advise me on why I'm having this issue.
I am running VS2012 Express, on my local IIS Machine, the project is configured for 4.5.1 framework and I have imported the Nuget Microsoft.Websockets package.
The three pieces of code I have included below are the only three pieces of code in the project and I have made no modifications to the rest of the project.
There is no break before the unexpected error, it does not break on open or on message on either side. The 200 comes up as an error in the chrome console, but there is no response preview.
Here is the client (index.htm):
<!doctype html>
<html>
<head>
<title></title>
<script src="Scripts/jquery-1.8.1.js" type="text/javascript"></script>
<script src="test.js" type="text/javascript"></script>
</head>
<body>
<input id="txtMessage" />
<input id="cmdSend" type="button" value="Send" />
<input id="cmdLeave" type="button" value="Leave" />
<br />
<div id="chatMessages" />
</body>
</html>
and the client script (test.js):
$(document).ready(function () {
var name = prompt('what is your name?:');
var url = 'ws://' + window.location.hostname + window.location.pathname.replace('index.htm', 'ws.ashx') + '?name=' + name;
alert('Connecting to: ' + url);
var ws = new WebSocket(url);
ws.onopen = function () {
$('#messages').prepend('Connected <br/>');
$('#cmdSend').click(function () {
ws.send($('#txtMessage').val());
$('#txtMessage').val('');
});
};
ws.onmessage = function (e) {
$('#chatMessages').prepend(e.data + '<br/>');
};
$('#cmdLeave').click(function () {
ws.close();
});
ws.onclose = function () {
$('#chatMessages').prepend('Closed <br/>');
};
ws.onerror = function (e) {
$('#chatMessages').prepend('Oops something went wrong<br/>');
};
});
Here is the generic handler (ws.ashx):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Web.WebSockets;
namespace WebSockets
{
public class ws : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
context.AcceptWebSocketRequest(new TestWebSocketHandler());
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Here is the class (TestWebSocketHandler):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using Microsoft.Web.WebSockets;
namespace WebSockets
{
public class TestWebSocketHandler : WebSocketHandler
{
private static WebSocketCollection clients = new WebSocketCollection();
private string name;
public override void OnOpen()
{
this.name = this.WebSocketContext.QueryString["name"];
clients.Add(this);
clients.Broadcast(name + " has connected.");
}
public override void OnMessage(string message)
{
clients.Broadcast(string.Format("{0} said: {1}", name, message));
}
public override void OnClose()
{
clients.Remove(this);
clients.Broadcast(string.Format("{0} has gone away.", name));
}
}
}
It didn't come up with an error to any effect of this problem, but I found the error which Chrome failed to return alongside my 200 response.
I simply had to turn on the WebSocket protocol under Control Panel --> Programs --> Turn Windows Features on or Off --> IIS --> Application Development Features --> WebSocket Protocol.
At very least I've added a simple VB.NET WebSocket to the site.
Just make sure you have IIS 7+ and are running windows 8 in the 4.5 Framework...and to turn on the WebSocket feature above.
The 200 code is not an error, it's the HTTP OK response, which is what you want. Inspect the response using your browser, is there content in the response? Try to add a break point on your ws.onmessage to see if it fires up.
simply just check on the web socket protocol form add or remove programs
go to Control Panel
then click on add or remove programs
then Turn Windows Features on or Off
then IIS
then Application Development Features
then check the check box of WebSocket Protocol option
i tested it on Microsoft windows server 2012
I found this in wikipedia:
200 OK
Standard response for successful HTTP requests. The actual response will depend on the request method used. In a GET request, the response will contain an entity corresponding to the requested resource. In a POST request the response will contain an entity describing or containing the result of the action.
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.
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...