WebSocket connection is closing - c#

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.

Related

C# Websocket server not receiving messages from HTML5 client

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

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

Proxy won't work in chrome and only partly in firefox

I've written a proxy in c#. It works by getting the http request from the browser sending the request to the site and sending back the site's response to the client. It works in firefox but some of the pages are cut like if it did not send all the response and in chrome it gives blank pages and for google.co.uk in chrome the browser gives "no data received". Can you see a mistake in my code which might be causing all this?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
namespace LexProxy
{
class ProxyServer
{
private TcpListener tcpListener;
public ProxyServer()
{
this.tcpListener = new TcpListener(IPAddress.Any, 3000);
this.tcpListener.Start();
while (true)
{
Console.Write("Waiting for a connection... ");
TcpClient client = tcpListener.AcceptTcpClient();
Thread thread = new Thread(delegate()
{
Serve(client);
});
thread.Start();
}
}
private void Serve(TcpClient client)
{
Console.WriteLine("Connected!");
NetworkStream stream = client.GetStream();
byte[] request = GetBytesFromStream(stream, client.ReceiveBufferSize);
if (request != null)
{
string requestString = System.Text.Encoding.UTF8.GetString(request);
string[] requestParts = requestString.Split(' ');
if (requestParts.Length >= 2)
{
string method = requestParts[0];
if (!requestParts[1].Contains("http://") && !requestParts[1].Contains("https://"))
requestParts[1] = "http://" + requestParts[1];
Uri uri = new Uri(requestParts[1], UriKind.RelativeOrAbsolute);
string host = StringUtils.ReplaceFirst(uri.Host, "www.", "");
int port = uri.Port;
byte[] response = getResponse(host, port, request);
if (response != null)
stream.Write(response, 0, response.Length);
client.Close();
}
}
}
private byte[] getResponse(string host, int port, byte[] request)
{
TcpClient client = new TcpClient(host, port);
NetworkStream stream = client.GetStream();
stream.Write(request, 0, request.Length);
byte[] response = GetBytesFromStream(stream, client.ReceiveBufferSize);
return response;
}
private byte[] GetBytesFromStream(NetworkStream stream, int bufferSize)
{
Byte[] bytes = new Byte[bufferSize];
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
return bytes;
}
return null;
}
}
}
One minor error is that you're using UTF8 to read first line of request. HTTP request line and headers are ASCII (and body may be UTF8, but doesn't have to be, and may not even be a string at all). The reason it works is b/c for english charset, UTF8 and ASCII happen to be encoded using the same bytes. But that's the minor issue.
Most likely primary issue is hiding in your GetBytesFromStream. You only call .Read once, but that doesn't guarantee that the whole message is returned. It may return as little as just 1 byte... so you need to continue getting more data. How do you know how much more? It's dictated by HTTP protocol which you'd need to properly parse and examine. (At a minimum you're reading until you reach \r\n\r\n byte sequence indicating the end of request headers.
However, this is not enough as there may be a request body, length of which will be specified via an HTTP header, Content-Length: (IIRC), or possibly using chunked encoding. I don't see any code beyond URI examination, so most likely you do not handle HTTP messaging protocol itself, and so it is unlikely to work properly (unless you somehow manage to force browser to use HTTP/0.9 or HTTP/1.0 since those do not reuse connection and send one message per connection, at which point you could just blindly read everything the socket has to give you until browser signals the end of stream by closing its write endpoint of the connection).
But your main problem is that GetBytesFromStream as written won't give you "all" bytes.

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