C# Websocket server not receiving messages from HTML5 client - c#

I'm attempting to create a bare bones websocket chat room server. My client is able to connect to the server, but it is unable to send messages to the server, despite the server being in a listening state.
When the client connects, a bunch of what looks like header information gets written to the console. But when WebSocket.send() gets executed in Javascript, nothing occurs server side.
HTML:
<button id="closeSocket">disconnect</button><br />
<input id = "inputField" /><button id="sendMessage">send</button>
<div id = "output"></div>
<script type = 'text/javascript' src = 'websockets.js'></script>
Javascript:
websocket = new WebSocket("ws://127.0.0.1:80");
document.getElementById("closeSocket").onclick = closeSocket;
document.getElementById("sendMessage").onclick = sendMessage;
websocket.onopen = function(){
output("connected");
}
function sendMessage(){
output("sent: " + document.getElementById('inputField').value);
websocket.send(document.getElementById('inputField').value);
}
websocket.onmessage = function(e){
output("got response: " + e.data);
}
function closeSocket(){
websocket.close();
}
websocket.onclose = function(){
output("disconnected");
}
function output(t){
document.getElementById("output").innerHTML += t + "<br />";
}
C# server:
using System;
using System.Net;
using System.Net.Sockets;
namespace WebSocketsTutorial {
class Program {
static void Main(string[] args) {
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
TcpClient client = default(TcpClient);
server.Start();
Console.WriteLine("server started");
while (true) {
client = server.AcceptTcpClient();
byte[] receivedBuffer = new byte[100];
NetworkStream stream = client.GetStream();
stream.Read(receivedBuffer, 0, receivedBuffer.Length);
foreach (byte b in receivedBuffer) {
Console.Write(Convert.ToChar(b).ToString());
}
}
}
}
}
This is what is output on the console when the client connects:
What I'm mainly looking to do is to allow an arbitrary number of connections and ultimately have the server echo a user's submission to all connected clients.

First of all, a WebSocket is not just a regular socket. It defines a connection handshake using HTTP and its own framing protocol that you need to use.
https://en.wikipedia.org/wiki/WebSocket
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
https://hpbn.co/websocket/
Second, you are reading just the first 100 bytes of the request. You should read until the Read operation returns 0.
There is a myriad of components you can use to create a WebSocket server, including the default one: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/websockets

Related

C# TCP Socket stream message confusion

I'm trying to write a C# TCP server and get my web app connected. This is the tutorial I'm following. Link
I managed to start the server but the message I got from my webapp is encrypted.
C# Snippet
using System;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace Application
{
class MainClass
{
public static void Main()
{
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 8181);
server.Start();
Console.WriteLine("Server has started on 127.0.0.1:8181.{0}Waiting for a connection...", Environment.NewLine);
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("A client connected.");
NetworkStream stream = client.GetStream();
while (true)
{
while (!stream.DataAvailable) ;
Byte[] bytes = new Byte[client.Available];
stream.Read(bytes, 0, bytes.Length);
String data = Encoding.UTF8.GetString(bytes);
//handshaking
if (new Regex("^GET").IsMatch(data))
{
Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine
+ "Connection: Upgrade" + Environment.NewLine
+ "Upgrade: websocket" + Environment.NewLine
+ "Sec-WebSocket-Accept: " + Convert.ToBase64String(
SHA1.Create().ComputeHash(
Encoding.UTF8.GetBytes(
new Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
)
)
) + Environment.NewLine
+ Environment.NewLine);
stream.Write(response, 0, response.Length);
}
else
{
Console.WriteLine(data);
}
}
}
}
}
HTML Snippet
<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;
This is what I got from my console
I have tried to encode the message to another format but no luck at all.
According to this stackoverflow user,
Messages are not sent in raw format, but they have Data Framing.
Is it relevant to the problem I'm having now? Do I need to find a way to deframe the data I got from the app?
Well, this isn't a great start:
String data = Encoding.UTF8.GetString(bytes);
The data sent after the handshake isn't UTF8 text. Parts of it are, but there are binary headers, and there's also something (in RFC6455 at least - possibly not if your client is using Hixie-76) called "Client-to-Server Masking", which means that the data is indeed munged (this is, IIRC, largely to avoid problems with proxy servers, and is not intended to be encryption).
There's also the problem of you looping over the handshake, but: you only handshake once.
Basically, the web-sockets protocol is quite nuanced (and: it is badly/incorrectly implemented by some browsers/versions). Unless you have a very good reason*, I strongly recommend using a library instead of trying to write this by hand. Microsoft provides some level of support in the framework, which should help.
*=I've done this, but for our scenario, the key design reason was scalability - we currently have 464515 open web-socket connections, and the MS implementation just wasn't going to cut it. We made our code freely available, if it helps.

Simple socket server in C# unexpectedly closes connection when trying to write data to client

I'm trying to figure out why this C# code:
int port = 8080;
var localAddr = IPAddress.Parse("127.0.0.1");
var server = new TcpListener(localAddr, port);
server.Start();
Console.WriteLine("Waiting for a connection... ");
var client = server.AcceptTcpClient();
Console.WriteLine("Connected!"); // Writes to console when "open" button
// is pushed in browser.
NetworkStream stream = client.GetStream();
while (client.Connected) // successfully loops twice.
{
string message = DateTime.Now.ToString();
byte[] msg = Encoding.ASCII.GetBytes(message);
stream.Write(msg, 0, msg.Length); // Exception is thrown here; data
// is never received in browser.
Console.WriteLine("Sent: {0}", message);
Thread.Sleep(1000);
}
client.Close();
is throwing the following exception (referenced in code comment above):
An unhandled exception of type 'System.IO.IOException' occurred in
System.dll
Additional information: Unable to write data to the transport
connection: An established connection was aborted by the software in
your host machine.
The code I'm using on the browser looks like this:
<!DOCTYPE html>
<html>
<head>
<title>Socket Test</title>
</head>
<body>
<div>
<input id=message size=50 />
<button type="button" onclick="sendMessage();" >Send</button> <br />
<button type="button" onclick="openSocket();" >Open</button>
<button type="button" onclick="closeSocket();" >Close</button>
</div>
<div id="messages"></div>
<script type="text/javascript">
var webSocket;
var messages = document.getElementById("messages");
function openSocket(){
if(webSocket !== undefined && webSocket.readyState !== WebSocket.CLOSED){
writeResponse("WebSocket is already opened.");
return;
}
webSocket = new WebSocket("ws://127.0.0.1:8080");
webSocket.onopen = function(event){
writeResponse("Connection opened.");
};
webSocket.onmessage = function(event){
writeResponse(event.data);
};
webSocket.onclose = function(event){
writeResponse("Connection closed with event code " + event.code);
};
webSocket.onerror = function(event){
writeResponse(event.data);
};
}
function closeSocket(){
webSocket.close();
}
function sendMessage(){
webSocket.send(message.value);
}
function writeResponse(text){
messages.innerHTML += "<br/>" + text;
}
</script>
</body>
</html>
...which I know works perfectly, because I can change the line
webSocket = new WebSocket("ws://127.0.0.1:8080");
to
webSocket = new WebSocket("ws://echo.websocket.org");
and get a good connection and echo replies from http://websocket.org.
Note: Unlike the websocket.org test, the browser never reports that the connection has been opened with the C# server, even though the C# server believes it has established a successful connection.
You can't use a TCP socket to communicate with a WebSocket directly. WebSockets are built on top of HTTP, and the initial request is an HTTP request to do the handshake and upgrade from HTTP connection to WebSocket connection, so you will need to implement a small http server first to handle the HTTP connection upgrade request.
An upgrade header looks like this
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
so either you use an existing library or read the relevant parts of https://www.rfc-editor.org/rfc/rfc6455

UdpClient.Receive(...) doesn't receive any data

I have a socket server (written on C++) which receives requests from clients and sends responses back. So, my test client application (C#) is very simple:
try {
UdpClient udpClient = new UdpClient(10241);
// Connect
udpClient.Connect(server_ip_address, 10240);
// prepare the packet to send
iWritableBuff wbuff = new iWritableBuff();
[ ... ]
// Send
udpClient.Send(wbuff.GetBuff(), (int)wbuff.Written());
// Receive a response
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
Console.WriteLine("We've got some data from the server!!! " + receiveBytes.Length + " bytes.");
udpClient.Close();
} catch (Exception e) {
Console.WriteLine("ERROR: " + e.ToString());
}
I run both applications on the same computer. Server receives a request to port 10240 from the client port 10241 and sends a response back to client port 10241, but client never receive it. So, I'm sure that server sends packet back, because everything works perfectly with C++ client. It means that I'm, doing something wrong on my C# client. Any idea?
Thanks!
P.S.> Just test it with Berkley Socket C# client:
try {
// Create socket
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// Bind
IPEndPoint myEP = new IPEndPoint(IPAddress.Any, 0);
s.Bind(myEP);
// prepare the packet to send
iWritableBuff wbuff = new iWritableBuff();
[ ... ]
// Send it
IPEndPoint sEP = new IPEndPoint(IPAddress.Parse(server_ip_address), 10240);
int res = s.SendTo(wbuff.GetBuff(), (int)wbuff.Written(), 0, sEP);
// Receive the response
byte[] receiveBytes = new Byte[1024];
EndPoint recEP = new IPEndPoint(IPAddress.Any, 0);
res = s.ReceiveFrom(receiveBytes, ref recEP);
Console.WriteLine("We've got some data from the server!!! " + res + " bytes.");
} catch (Exception e) {
Console.WriteLine("ERROR: " + e.ToString());
}
And it works perfect! What's is wrong with UdpSocket?
Not sure if this applies to UDPClients that don't broadcast packets but simply send one to a specified address, but I ran into a similar roadblock.
I had a UDPClient that broadcasted a udp packet for discovery of some custom machines on our network. When I tried to receive the message that the servers would simply echo back, it would not receive the information and would timeout. Turns out that if you use a UDPClient that broadcasts a message out, it WILL NOT be able to receive messages back. It's not documented anywhere, except for one forum topic on msdn that I luckily came across.
The solution was to send the message, immediately close the socket, open a NEW UDPClient on the same port, then use this new UDPClient to receive the echoed back UDP packet. Very annoying.
Give that a shot and see if it works. It definitely works for sending out a broadcasted packet.

WebSocket connection is closing

I'm a beginner with the WebSocket API. I'm trying to connect to my server locally but I'm obtaining the connection closed message. Can anyone tell me what I am doing wrong?
That's my code:
Server
using System.Net.Sockets;
using System.Net;
using System.IO;
using System;
class Program
{
static void Main(string[] args)
{
var listener = new TcpListener(IPAddress.Loopback, 8181);
listener.Start();
while (true)
{
Console.WriteLine("Listening...");
using (var client = listener.AcceptTcpClient())
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
string line = null, key = "", responseKey = "";
string MAGIC_STRING = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
while (line != "")
{
line = reader.ReadLine();
if (line.StartsWith("Sec-WebSocket-Key:"))
{
key = line.Split(':')[1].Trim();
}
}
if (key != "")
{
key += MAGIC_STRING;
using (var sha1 = SHA1.Create())
{
responseKey = Convert.ToBase64String(sha1.ComputeHash(Encoding.ASCII.GetBytes(key)));
}
}
// send handshake to the client
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: http://localhost:8080");
writer.WriteLine("WebSocket-Location: ws://localhost:8181/websession");
if (!String.IsNullOrEmpty(responseKey))
writer.WriteLine("Sec-WebSocket-Accept: " + responseKey);
writer.WriteLine("");
Console.ReadLine();
writer.Flush();
}//using
Console.WriteLine("Finished");
}//while
}
}
The Client
<!DOCTYPE HTML>
<html>
<head><title></title>
<script type="text/javascript">
function WebSocketTest() {
var msg = document.getElementById("msg");
if ("WebSocket" in window) {
msg.innerHTML="WebSocket is supported by your Browser!";
// Let us open a web socket
var ws = new WebSocket("ws://localhost:8181/websession");
ws.onopen = function () {
// Web Socket is connected, send data using send()
msg.innerHTML="connection open";
//ws.send("Message to send");
//msg.innerHTML="Message is sent...";
};
ws.onclose = function () {
// websocket is closed.
msg.innerHTML = "Connection is closed...";
};
ws.onerror = function(error){
console.log('Error detected: ' + error);
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
msg.innerHTML="Message is received...";
};
}
else {
// The browser doesn't support WebSocket
msg.innerHTML="WebSocket NOT supported by your Browser!";
}
}
</script>
</head>
<body>
<div id="sse">
Run WebSocket<br />
<p id="msg"></p>
</div>
</body>
</html>
Any solution would be appreciated and thank you :)
That is not a valid web-socket response under any specification. The initial web-socket response always requires you to crunch some numbers as part of the response headers, to prove you're a web-socket server. Which headers to read and write, and what crunching to do, depends on the version of web-sockets (hibi/hixie 76/rfc). It actually looks like your server is using the headers of a client.
For example, a RFC6455 (13) response would start:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: {crunch some numbers}
Note that a Hixie-76 response is different, and there are bits in the above that I have omitted.
From the RFC6455 specification:
To prove that the handshake was received, the server has to take two
pieces of information and combine them to form a response. The first
piece of information comes from the |Sec-WebSocket-Key| header field
in the client handshake:
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
For this header field, the server has to take the value (as present in
the header field, e.g., the base64-encoded [RFC4648] version minus any
leading and trailing whitespace) and concatenate this with the
Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-
95CA-C5AB0DC85B11" in string form, which is unlikely to be used by
network endpoints that do not understand the WebSocket Protocol. A
SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of
[RFC4648]), of this concatenation is then returned in the server's
handshake.
After the javascript code connects to the webserver. C# runs through all using statements and prints all the lines to your file after which all using statements are closed and thus the generated client is disposed, which closes the connection.
A quick (but dirty) fix for this problem is to add "Console.Readline" at the end of the using statements. Be carefull with this though, your process will hang!
For more information about TcpClients accepting clients, go to msdn.
Notice that the given example there, only allows one connection at a time.
Last remark: there exist libraries for accepting websocket connections like Marc suggested.

Can't get entire response from proxy C#

I'm building a small HTTP proxy that runs between the browser and squid proxy. The browser sends the HTTP request to my proxy that redirects it to the squid proxy, then my application gets the response from the squid proxy and returns it back to the browser.
the problem is that i can't get the full response from the proxy, i get HTTP 200 OK ... (just the response header), but with out the body then i have to call receive method another time to get the body. but if i debug my code (which make the application slower) it get all the response (response header and body)
is there any propriety in the TCPClass that indicates to me that the remote server still have data to send to me ?
here is my code :
static void Main(string[] args)
{
int ServerPort = 8888;
IPAddress localHost = new IPAddress(0x0100007f);
TcpListener listener = new TcpListener(localHost,ServerPort);
listener.Start();
while(true)
{
string requestString = "";
String respenseString = "";
TcpClient application = listener.AcceptTcpClient();
string source = application.Client.RemoteEndPoint.ToString();
byte[] dataFromApp = new byte[application.ReceiveBufferSize];
application.Client.Receive(dataFromApp);
TcpClient tunnel = new TcpClient("127.0.0.1",8080);
tunnel.Client.Send(dataFromApp);
while (tunnel.Client.Connected ==true)
{
if(tunnel.Available != 0)
{
byte[] responseFromProxy = new byte[tunnel.ReceiveBufferSize];
tunnel.Client.Receive(responseFromProxy);
respenseString += Encoding.UTF8.GetString(responseFromProxy);
}
else
{
break;
}
}
application.Client.Send(Encoding.UTF8.GetBytes(respenseString));
}
You should check the return value of tunnel.Client.Receive and application.Client.Receive. Receive doesn't gurantee that it will read dataFromApp.Length bytes
REMARKS: The Receive method reads data into the buffer parameter and returns the number of bytes successfully read
PS: You may also want to try FiddlerCore to write an Http Proxy
There is no "there are N bytes remaining for this message" property on a socket, because a TCP socket is streaming: it sends and receives bytes, not messages.
HTTP defines messages, and if you are implementing an HTTP proxy, you should be familiar with the HTTP 1.1 RFC. There are various ways to determine the lenght of an HTTP message, all of which you have to implement to make sure you can successfully receive and send HTTP messages.
Thanks guys
I've done it :
while (tunnel.Client.Receive(oneByte) != 0)
{
byte[] responseFromProxy = new byte[tunnel.Available];
tunnel.Client.Receive(responseFromProxy);
application.Client.Send(oneByte);
application.Client.Send(responseFromProxy);
}

Categories

Resources