Websockets - disconnects on server message - c#

I'm new to Websockets and looking for working samples using WS and c# console application.
I ran into this one, but i already have a problem. It seems that connection is closed when server sends a message to client. I'm not sure but i think handshake is ok. Here is a code:
Server:
using System;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static void Main(string[] args)
{
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
serverSocket.Listen(128);
serverSocket.BeginAccept(null, 0, OnAccept, null);
Console.Read();
}
private static void OnAccept(IAsyncResult result)
{
byte[] buffer = new byte[1024];
try
{
Socket client = null;
string headerResponse = "";
if (serverSocket != null && serverSocket.IsBound)
{
client = serverSocket.EndAccept(result);
var i = client.Receive(buffer);
headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
// write received data to the console
Console.WriteLine(headerResponse);
Console.WriteLine("=====================");
}
if (client != null)
{
/* Handshaking and managing ClientSocket */
var key = headerResponse.Replace("ey:", "`")
.Split('`')[1] // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
.Replace("\r", "").Split('\n')[0] // dGhlIHNhbXBsZSBub25jZQ==
.Trim();
// key should now equal dGhlIHNhbXBsZSBub25jZQ==
var test1 = AcceptKey(ref key);
var newLine = "\r\n";
var response = "HTTP/1.1 101 Switching Protocols" + newLine
+ "Upgrade: websocket" + newLine
+ "Connection: Upgrade" + newLine
+ "Sec-WebSocket-Accept: " + test1 + newLine + newLine
//+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
//+ "Sec-WebSocket-Version: 13" + newLine
;
// which one should I use? none of them fires the onopen method
client.Send(System.Text.Encoding.UTF8.GetBytes(response));
var i = client.Receive(buffer); // wait for client to send a message
// once the message is received decode it in different formats
Console.WriteLine(Convert.ToBase64String(buffer).Substring(0, i));
Console.WriteLine("=====================");
Console.WriteLine("\n\nPress enter to send data to client");
Console.Read();
var subA = SubArray<byte>(buffer, 0, i);
client.Send(subA);
Console.Read();
Thread.Sleep(10000);//wait for message to be send
}
}
catch (SocketException exception)
{
throw exception;
}
finally
{
if (serverSocket != null && serverSocket.IsBound)
{
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}
}
public static T[] SubArray<T>(T[] data, int index, int length)
{
T[] result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}
private static string AcceptKey(ref string key)
{
string longKey = key + guid;
byte[] hashBytes = ComputeHash(longKey);
return Convert.ToBase64String(hashBytes);
}
static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
private static byte[] ComputeHash(string str)
{
return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
}
}
}
Clent:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
function connect() {
var ws = new WebSocket("ws://localhost:8080/service");
ws.onopen = function () {
alert("About to send data");
ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
alert("Message sent!");
};
ws.onmessage = function (evt) {
alert("About to receive data");
var received_msg = evt.data;
alert("Message received = "+received_msg);
};
ws.onclose = function () {
// websocket is closed.
alert("Connection is closed...");
};
};
</script>
</head>
<body style="font-size:xx-large" >
<div>
Click here to start</div>
</body>
</html>
Here is a connection request and message from client:
Any help will be appreciated.

You cannot just "echo" the buffer you received. The RFC says, that frames from client to server must be masked. It doesn't say that messages from server to client must not, but at least for Chrome it shouldn't.
Hence, if you want to echo data, you have to decode (unmask) the frame and construct a new one. The payload needs to be XOR'ed byte-by-byte with the mask key.
Or simply send data like this:
byte[] send = new byte[3 + 2];
send[0] = 0x81; // last frame, text
send[1] = 3; // not masked, length 3
send[2] = 0x41;
send[3] = 0x42;
send[4] = 0x43;
nwStream.Write(send, 0, send.Length); // nwStream = client.GetStream(), client is a TcpClient

JeffRSon is correct. But it should be noted that section 5.1 of RFC 6455 does mention;
... A server MUST NOT mask any frames that it sends to the client. A client MUST close a connection if it detects a masked frame. ...

Related

Basic HttpServer | How to read data from NetworkStream properly

I am trying to make a basic webserver for practice, however i think that i'm not reading from the NetworkStream properly... On the picture bellow you can see that request 2 and 3 do not have 2 newlines \r\n\r\n separating them , which means that i'm reading less data.
Also the requests hang for no obvious reason. I think that I am properly closing the stream and the client, by using networkStream.Close(); and tcpClient.Close();
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace HttpServer
{
class Program
{
static void Main(string[] args)
{
var webserver = new Program();
webserver.Listen(12345);
}
public static void ProcessTcpClientAsync(TcpClient tcpClient)
{
// Get a stream object for reading and writing
NetworkStream networkStream = tcpClient.GetStream();
byte[] buffer = readByteBufferFromNetworkStream(networkStream);
string request = Encoding.UTF8.GetString(buffer);
Console.WriteLine(request);
const string HTTPNewLine = "\r\n";
string html = "<h1>Hello from TestServer</h1>";
string response = "HTTP/1.1 200 OK" + HTTPNewLine;
response += "Server: TestServer 2020" + HTTPNewLine;
response += "Content-Type: text/html; charset=utf-8" + HTTPNewLine;
response += "Content-Length: " + html.Length + HTTPNewLine;
response += HTTPNewLine;
response += html;
response += HTTPNewLine;
byte[] responseBytes = Encoding.UTF8.GetBytes(response);
networkStream.Write(responseBytes);
networkStream.Close();
tcpClient.Close();
}
public void Listen(Int32 port)
{
//Int32 port = 12345;
IPAddress localaddr = IPAddress.Parse("127.0.0.1");
TcpListener tcpListener = new TcpListener(localaddr, port);
tcpListener.Start();
// Enter the listening loop (service/daemon)
while (true)
{
//# Sync Version for debugging
TcpClient tcpClient = tcpListener.AcceptTcpClient();
ProcessTcpClientAsync(tcpClient);
//tcpListener.AcceptTcpClientAsync().ContinueWith((res) => ProcessTcpClientAsync(res.GetAwaiter().GetResult()));
}
}
public static byte[] readByteBufferFromNetworkStream(NetworkStream networkStream)
{
byte[] buffer = new byte[256];
int bytesRead = 0;
while (true)
{
int currentBytesRead = networkStream.Read(buffer, bytesRead, buffer.Length - bytesRead);
bytesRead += currentBytesRead;
// If we've read less data than the size of our buffer -> it means there is no more information, so break.
if (currentBytesRead < buffer.Length) { break; }
// If we've reached the end of our buffer -> check to see if there's any more information, or break.
if (bytesRead == buffer.Length)
{
int nextByte = networkStream.ReadByte();
// End of stream? If so, we're done.
if (nextByte == -1)
{
break;
}
// Otherwise resize the buffer, put in the byte we've just read and continue.
else
{
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[bytesRead] = (byte)nextByte;
buffer = newBuffer;
bytesRead++;
}
}
}
// Buffer is now too big. Shrink it.
byte[] resultBuffer = new byte[bytesRead];
Array.Copy(buffer, resultBuffer, bytesRead);
return resultBuffer;
}
}
}
It's part of the standard for this type of response to seperate the header from the body content.
See https://www.rfc-editor.org/rfc/rfc7230#section-3
All HTTP/1.1 messages consist of a start-line followed by a sequence
of octets in a format similar to the Internet Message Format
[RFC5322]: zero or more header fields (collectively referred to as
the "headers" or the "header section"), an empty line indicating the
end of the header section, and an optional message body.
HTTP-message = start-line
*( header-field CRLF )
CRLF
[ message-body ]
(CRLF above will be treated like \r\n)

TcpListener AcceptSocket() accepts two connections for each single GET request

I have this web server program up and running, using TcpListener. The problem is that for each get request made through web browser (or chrome Postman extension), it captures two requests.
namespace Server
{
class WebServer2
{
private TcpListener listener;
private int port = 8080;
public WebServer2()
{
try
{
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), port);
listener.Start();
Console.WriteLine("Listening...");
//start the thread which calls the method 'StartListen'
Thread th = new Thread(new ThreadStart(StartListen));
th.Start();
}
catch (Exception e)
{
Console.WriteLine("An Exception Occurred While Listening: " + e.ToString());
}
}
//Recieve Request
public void StartListen()
{
int iStartPos = 0;
String sRequest;
String sRequestedFile;
String sResponse = "";
while (true)
{
//Accept a new connection
Socket socket = listener.AcceptSocket();
if (socket.Connected)
{
Console.WriteLine("\nClient Connected");
Console.WriteLine("----------------");
//Receive data from the client
Byte[] bReceive = new Byte[1024];
int i = socket.Receive(bReceive, bReceive.Length, 0);
//Convert Byte to String
string sBuffer = Encoding.ASCII.GetString(bReceive);
//Only GET Request is accepted
if (sBuffer.Substring(0, 3) != "GET")
{
Console.WriteLine("Not a Get Request.");
socket.Close();
continue;
}
// Look for HTTP request
iStartPos = sBuffer.IndexOf("HTTP", 1);
// Get the HTTP text and version e.g. it will return "HTTP/1.1"
string sHttpVersion = sBuffer.Substring(iStartPos, 8);
// Extract the Requested Type and Requested file/directory
sRequest = sBuffer.Substring(0, iStartPos - 1);
//If file name not provided
if (sRequest.IndexOf(".") < 1)
{
Console.WriteLine("File name not Provided!");
socket.Close();
continue;
}
//Extract the requested file name
iStartPos = sRequest.LastIndexOf("/") + 1;
sRequestedFile = sRequest.Substring(iStartPos);
Console.WriteLine("Requested File: " + sRequestedFile);
int iTotBytes = 0;
sResponse = "";
FileStream fs = new FileStream(sRequestedFile, FileMode.Open, FileAccess.Read, FileShare.Read);
BinaryReader reader = new BinaryReader(fs);
byte[] bytes = new byte[fs.Length];
int read;
while ((read = reader.Read(bytes, 0, bytes.Length)) != 0)
{
// Read from the file and write the data to the network
sResponse = sResponse + Encoding.ASCII.GetString(bytes, 0, read);
iTotBytes = iTotBytes + read;
}
reader.Close();
fs.Close();
SendHeader(sHttpVersion, "text/html", iTotBytes, " 200 OK", ref socket);
SendToBrowser(bytes, ref socket);
socket.Send(bytes, bytes.Length, 0);
socket.Close();
}
}
}
// Overloaded Function, takes string, convert to bytes and calls
// overloaded sendToBrowserFunction.
public void SendToBrowser(String sData, ref Socket socket)
{
SendToBrowser(Encoding.ASCII.GetBytes(sData), ref socket);
}
/// Sends data to the browser (client)
public void SendToBrowser(Byte[] bSendData, ref Socket socket)
{
int numBytes = 0;
try
{
if (socket.Connected)
{
if ((numBytes = socket.Send(bSendData, bSendData.Length, 0)) == -1)
Console.WriteLine("Socket Error");
}
else
Console.WriteLine("Connection Dropped!");
}
catch (Exception e)
{
Console.WriteLine("Error: {0} ", e);
}
}
// This function send the Header Information to the client (Browser)
public void SendHeader(string sHttpVersion, string sMIMEHeader, int iTotBytes, string sStatusCode, ref Socket socket)
{
String sBuffer = "";
Byte[] bSendData;
if (sStatusCode.Equals("404") || sStatusCode.Equals("400"))
{
sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
sBuffer = sBuffer + "Server: MyServer\r\n";
sBuffer = sBuffer + "Content-Length: " + 0 + "\r\n\r\n";
bSendData = Encoding.ASCII.GetBytes(sBuffer);
SendToBrowser(bSendData, ref socket);
}
else
{
sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
sBuffer = sBuffer + "Server: MyServer\r\n";
sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n";
sBuffer = sBuffer + "Accept-Ranges: bytes\r\n";
sBuffer = sBuffer + "Content-Length: " + iTotBytes + "\r\n\r\n";
bSendData = Encoding.ASCII.GetBytes(sBuffer);
SendToBrowser(bSendData, ref socket);
}
}
}
}
Single request made by chrome against http://localhost:8080/page1.html
Request made by Postman Extension
The funny thing is, everything works find when I send request through my client program (using TcpClient).
I tested your server and got similar results:
Chrome automatically asks for a favicon.ico file for every request. This generates the extra GET request to your server.
Firefox doesn't do this and simply generates one request per refresh.
Then there is a second problem I experienced, which is the one in your Postman screenshot.
Chrome (including the Postman Extension running inside it) reconnects
without sending anything, so the server "hangs" until you get a
response filled with \0 (nul-characters).
This is part of Chrome's prediction service to load subsequent requests faster. You could disable this behavior under Settings (advanced) > Privacy:
In your server implementation, this results in the "Not a Get Request" output if no subsequent GET request follows (instead, Chrome sends \0 to signal its not sending anything anyway).
Note: I didn't experience any of this in the actual Postman application.
While the server waits for chrome to send a request, it cannot service any other requests. To prevent this from happening, consider using an asynchronous approach.

I want to send data from node.js to C#

I work on Asterisk ami with node.js server
I want to send data from [node.js server] to C# client in real time
server.js
var app = require('http').createServer().listen(3001);
var io = require('socket.io').listen(app);
var AsteriskAmi = require('asterisk-ami');
var ami = new AsteriskAmi( { host: '127.0.0.1', username: 'admin', password: '123456' } );
io.sockets.on('connection', function(socket) {
socket.emit('notification', {message: "connected"});
});
ami.on('ami_data', function(data) {
io.sockets.emit('ami_event', data);
// or the same
// io.emit('ami_event', data);
});
ami.connect(function(){
});
C# sharp client
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
class MyTcpListener
{
public static void Main()
{
TcpListener server = null;
try
{
// Set the TcpListener on port 13000.
Int32 port = 3001;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
while (true)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// Process the data sent by the client.
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
// Send back a response.
stream.Write(msg, 0, msg.Length);
Console.WriteLine("Sent: {0}", data);
}
// Shutdown and end connection
//client.Close();
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Console.WriteLine("\nHit enter to continue...");
Console.Read();
}
}

between html5 and C# web socket communication

trying to send string data to windows c# application with this code;
try {
var ws = new WebSocket('ws://192.168.1.77:10048');
ws.onopen = function () {
ws.send("Sunucuya mesaj"); // I WANT TO SEND THIS MESSAGE TO SERVER!
};
ws.onclose = function () {
alert('Bağlantı kapandı.');
};
}
catch (e) {
alert(e);
}
and trying to get data from windows c# application with this code;
static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8181));
serverSocket.Listen(128);
serverSocket.BeginAccept(null, 0, OnAccept, null);
private static void OnAccept(IAsyncResult result)
{
byte[] buffer = new byte[1024];
try
{
Socket client = null;
string headerResponse = "";
if (serverSocket != null && serverSocket.IsBound)
{
client = serverSocket.EndAccept(result);
var i = client.Receive(buffer);
headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
// write received data to the console
Console.WriteLine(headerResponse);
}
if (client != null)
{
var key = headerResponse.Replace("ey:", "`")
.Split('`')[1] // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
.Replace("\r", "").Split('\n')[0] // dGhlIHNhbXBsZSBub25jZQ==
.Trim();
// key should now equal dGhlIHNhbXBsZSBub25jZQ==
var test1 = AcceptKey(ref key);
var newLine = "\r\n";
var response = "HTTP/1.1 101 Switching Protocols" + newLine
+ "Upgrade: websocket" + newLine
+ "Connection: Upgrade" + newLine
+ "Sec-WebSocket-Accept: " + test1 + newLine + newLine
//+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
//+ "Sec-WebSocket-Version: 13" + newLine
;
// which one should I use? none of them fires the onopen method
client.Send(System.Text.Encoding.UTF8.GetBytes(response));
var i = client.Receive(buffer); // wait for client to send a message
// once the message is received decode it in different formats
Console.WriteLine(Convert.ToBase64String(buffer).Substring(0, i));
Console.WriteLine("\n\nPress enter to send data to client");
Console.Read();
var subA = SubArray<byte>(buffer, 0, i);
client.Send(subA);
Thread.Sleep(10000);//wait for message to be send
}
}
catch (SocketException exception)
{
throw exception;
}
finally
{
if (serverSocket != null && serverSocket.IsBound)
{
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}
}
with this code i can communicate between client and server but i can't get ws.send("Sunucuya mesaj"); message from server that send from client. headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i); in this row you can see the header that is getting from server;
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 192.168.1.77:8181
Origin: http://localhost
Sec-WebSocket-Key: dHI34r7feV/Ar4G0/fONCg==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
How can i get data that is coming from client from server-side?
Thanks...
Messages between client and server are not plain text. See the data framing section of the protocol spec for details on how to encode/decode messages.
There are several liberally licensed open source C# servers you can take examples from. Such as Fleck

Creating a "Hello World" WebSocket example

I don't understand why I cannot make the following code work. I want to connect with JavaScript to my server console application. And then send data to the server.
Here is the server code:
static void Main(string[] args)
{
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 9998);
server.Start();
var client = server.AcceptTcpClient();
var stream = client.GetStream();
while (true)
{
var buffer = new byte[1024];
// wait for data to be received
var bytesRead = stream.Read(buffer, 0, buffer.Length);
var r = System.Text.Encoding.UTF8.GetString(buffer);
// write received data to the console
Console.WriteLine(r.Substring(0, bytesRead));
}
}
and here is the JavaScript:
var ws = new WebSocket("ws://localhost:9998/service");
ws.onopen = function () {
ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
alert("Message is received...");
};
ws.onclose = function () {
// websocket is closed.
alert("Connection is closed...");
};
When I run that code this is what happens:
Note that when I run the JavaScript the server accepts and successfully establishes a connection. JavaScript is not able to send data though. Whenever I place the send method it will not send even though a connection is established. How can I make this work?
WebSockets is protocol that relies on TCP streamed connection. Although WebSockets is Message based protocol.
If you want to implement your own protocol then I recommend to use latest and stable specification (for 18/04/12) RFC 6455.
This specification contains all necessary information regarding handshake and framing. As well most of description on scenarios of behaving from browser side as well as from server side.
It is highly recommended to follow what recommendations tells regarding server side during implementing of your code.
In few words, I would describe working with WebSockets like this:
Create server Socket (System.Net.Sockets) bind it to specific port, and keep listening with asynchronous accepting of connections. Something like that:
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
serverSocket.Listen(128);
serverSocket.BeginAccept(null, 0, OnAccept, null);
You should have accepting function "OnAccept" that will implement handshake. In future it has to be in another thread if system is meant to handle huge amount of connections per second.
private void OnAccept(IAsyncResult result) {
try {
Socket client = null;
if (serverSocket != null && serverSocket.IsBound) {
client = serverSocket.EndAccept(result);
}
if (client != null) {
/* Handshaking and managing ClientSocket */
}
} catch(SocketException exception) {
} finally {
if (serverSocket != null && serverSocket.IsBound) {
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}
}
After connection established, you have to do handshake. Based on specification 1.3 Opening Handshake, after connection established you will receive basic HTTP request with some information. Example:
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
This example is based on version of protocol 13. Bear in mind that older versions have some differences but mostly latest versions are cross-compatible. Different browsers may send you some additional data. For example Browser and OS details, cache and others.
Based on provided handshake details, you have to generate answer lines, they are mostly same, but will contain Accept-Key, that is based on provided Sec-WebSocket-Key. In specification 1.3 it is described clearly how to generate response key.
Here is my function I've been using for V13:
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private string AcceptKey(ref string key) {
string longKey = key + guid;
SHA1 sha1 = SHA1CryptoServiceProvider.Create();
byte[] hashBytes = sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(longKey));
return Convert.ToBase64String(hashBytes);
}
Handshake answer looks like that:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
But accept key have to be the generated one based on provided key from client and method AcceptKey I provided before. As well, make sure after last character of accept key you put two new lines "\r\n\r\n".
After handshake answer is sent from server, client should trigger "onopen" function, that means you can send messages after.
Messages are not sent in raw format, but they have Data Framing. And from client to server as well implement masking for data based on provided 4 bytes in message header. Although from server to client you don't need to apply masking over data. Read section 5. Data Framing in specification.
Here is copy-paste from my own implementation. It is not ready-to-use code, and have to be modified, I am posting it just to give an idea and overall logic of read/write with WebSocket framing. Go to this link.
After framing is implemented, make sure that you receive data right way using sockets. For example to prevent that some messages get merged into one, because TCP is still stream based protocol. That means you have to read ONLY specific amount of bytes. Length of message is always based on header and provided data length details in header it self. So when you receiving data from Socket, first receive 2 bytes, get details from header based on Framing specification, then if mask provided another 4 bytes, and then length that might be 1, 4 or 8 bytes based on length of data. And after data it self. After you read it, apply demasking and your message data is ready to use.
You might want to use some Data Protocol, I recommend to use JSON due traffic economy and easy to use on client side in JavaScript. For server side you might want to check some of parsers. There is lots of them, google can be really helpful.
Implementing own WebSockets protocol definitely have some benefits and great experience you get as well as control over protocol it self. But you have to spend some time doing it, and make sure that implementation is highly reliable.
In same time you might have a look in ready to use solutions that google (again) have enough.
(Posted answer on behalf of the OP).
I am able to send data now. This is my new version of the program thanks to your answers and the code of #Maksims Mihejevs.
Server
using System;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static Socket serverSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.IP);
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static void Main(string[] args)
{
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
serverSocket.Listen(128);
serverSocket.BeginAccept(null, 0, OnAccept, null);
Console.Read();
}
private static void OnAccept(IAsyncResult result)
{
byte[] buffer = new byte[1024];
try
{
Socket client = null;
string headerResponse = "";
if (serverSocket != null && serverSocket.IsBound)
{
client = serverSocket.EndAccept(result);
var i = client.Receive(buffer);
headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0,i);
// write received data to the console
Console.WriteLine(headerResponse);
}
if (client != null)
{
/* Handshaking and managing ClientSocket */
var key = headerResponse.Replace("ey:", "`")
.Split('`')[1] // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
.Replace("\r", "").Split('\n')[0] // dGhlIHNhbXBsZSBub25jZQ==
.Trim();
// key should now equal dGhlIHNhbXBsZSBub25jZQ==
var test1 = AcceptKey(ref key);
var newLine = "\r\n";
var response = "HTTP/1.1 101 Switching Protocols" + newLine
+ "Upgrade: websocket" + newLine
+ "Connection: Upgrade" + newLine
+ "Sec-WebSocket-Accept: " + test1 + newLine + newLine
//+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
//+ "Sec-WebSocket-Version: 13" + newLine
;
// which one should I use? none of them fires the onopen method
client.Send(System.Text.Encoding.UTF8.GetBytes(response));
var i = client.Receive(buffer); // wait for client to send a message
// once the message is received decode it in different formats
Console.WriteLine(Convert.ToBase64String(buffer).Substring(0, i));
Console.WriteLine("\n\nPress enter to send data to client");
Console.Read();
var subA = SubArray<byte>(buffer, 0, i);
client.Send(subA);
Thread.Sleep(10000);//wait for message to be send
}
}
catch (SocketException exception)
{
throw exception;
}
finally
{
if (serverSocket != null && serverSocket.IsBound)
{
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}
}
public static T[] SubArray<T>(T[] data, int index, int length)
{
T[] result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}
private static string AcceptKey(ref string key)
{
string longKey = key + guid;
byte[] hashBytes = ComputeHash(longKey);
return Convert.ToBase64String(hashBytes);
}
static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
private static byte[] ComputeHash(string str)
{
return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
}
}
}
JavaScript:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
function connect() {
var ws = new WebSocket("ws://localhost:8080/service");
ws.onopen = function () {
alert("About to send data");
ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
alert("Message sent!");
};
ws.onmessage = function (evt) {
alert("About to receive data");
var received_msg = evt.data;
alert("Message received = "+received_msg);
};
ws.onclose = function () {
// websocket is closed.
alert("Connection is closed...");
};
};
</script>
</head>
<body style="font-size:xx-large" >
<div>
Click here to start</div>
</body>
</html>
When I run that code I am able to send and receive data from both the client and the server. The only problem is that the messages are encrypted when they arrive to the server. Here are the steps of how the program runs:
Note how the message from the client is encrypted.
I couldnt find a simple working example anywhere (as of Jan 19), so here is an updated version. I have chrome version 71.0.3578.98.
C# Websocket server :
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
namespace WebSocketServer
{
class Program
{
static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static void Main(string[] args)
{
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
serverSocket.Listen(1); //just one socket
serverSocket.BeginAccept(null, 0, OnAccept, null);
Console.Read();
}
private static void OnAccept(IAsyncResult result)
{
byte[] buffer = new byte[1024];
try
{
Socket client = null;
string headerResponse = "";
if (serverSocket != null && serverSocket.IsBound)
{
client = serverSocket.EndAccept(result);
var i = client.Receive(buffer);
headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
// write received data to the console
Console.WriteLine(headerResponse);
Console.WriteLine("=====================");
}
if (client != null)
{
/* Handshaking and managing ClientSocket */
var key = headerResponse.Replace("ey:", "`")
.Split('`')[1] // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
.Replace("\r", "").Split('\n')[0] // dGhlIHNhbXBsZSBub25jZQ==
.Trim();
// key should now equal dGhlIHNhbXBsZSBub25jZQ==
var test1 = AcceptKey(ref key);
var newLine = "\r\n";
var response = "HTTP/1.1 101 Switching Protocols" + newLine
+ "Upgrade: websocket" + newLine
+ "Connection: Upgrade" + newLine
+ "Sec-WebSocket-Accept: " + test1 + newLine + newLine
//+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
//+ "Sec-WebSocket-Version: 13" + newLine
;
client.Send(System.Text.Encoding.UTF8.GetBytes(response));
var i = client.Receive(buffer); // wait for client to send a message
string browserSent = GetDecodedData(buffer, i);
Console.WriteLine("BrowserSent: " + browserSent);
Console.WriteLine("=====================");
//now send message to client
client.Send(GetFrameFromString("This is message from server to client."));
System.Threading.Thread.Sleep(10000);//wait for message to be sent
}
}
catch (SocketException exception)
{
throw exception;
}
finally
{
if (serverSocket != null && serverSocket.IsBound)
{
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}
}
public static T[] SubArray<T>(T[] data, int index, int length)
{
T[] result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}
private static string AcceptKey(ref string key)
{
string longKey = key + guid;
byte[] hashBytes = ComputeHash(longKey);
return Convert.ToBase64String(hashBytes);
}
static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
private static byte[] ComputeHash(string str)
{
return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
}
//Needed to decode frame
public static string GetDecodedData(byte[] buffer, int length)
{
byte b = buffer[1];
int dataLength = 0;
int totalLength = 0;
int keyIndex = 0;
if (b - 128 <= 125)
{
dataLength = b - 128;
keyIndex = 2;
totalLength = dataLength + 6;
}
if (b - 128 == 126)
{
dataLength = BitConverter.ToInt16(new byte[] { buffer[3], buffer[2] }, 0);
keyIndex = 4;
totalLength = dataLength + 8;
}
if (b - 128 == 127)
{
dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);
keyIndex = 10;
totalLength = dataLength + 14;
}
if (totalLength > length)
throw new Exception("The buffer length is small than the data length");
byte[] key = new byte[] { buffer[keyIndex], buffer[keyIndex + 1], buffer[keyIndex + 2], buffer[keyIndex + 3] };
int dataIndex = keyIndex + 4;
int count = 0;
for (int i = dataIndex; i < totalLength; i++)
{
buffer[i] = (byte)(buffer[i] ^ key[count % 4]);
count++;
}
return Encoding.ASCII.GetString(buffer, dataIndex, dataLength);
}
//function to create frames to send to client
/// <summary>
/// Enum for opcode types
/// </summary>
public enum EOpcodeType
{
/* Denotes a continuation code */
Fragment = 0,
/* Denotes a text code */
Text = 1,
/* Denotes a binary code */
Binary = 2,
/* Denotes a closed connection */
ClosedConnection = 8,
/* Denotes a ping*/
Ping = 9,
/* Denotes a pong */
Pong = 10
}
/// <summary>Gets an encoded websocket frame to send to a client from a string</summary>
/// <param name="Message">The message to encode into the frame</param>
/// <param name="Opcode">The opcode of the frame</param>
/// <returns>Byte array in form of a websocket frame</returns>
public static byte[] GetFrameFromString(string Message, EOpcodeType Opcode = EOpcodeType.Text)
{
byte[] response;
byte[] bytesRaw = Encoding.Default.GetBytes(Message);
byte[] frame = new byte[10];
long indexStartRawData = -1;
long length = (long)bytesRaw.Length;
frame[0] = (byte)(128 + (int)Opcode);
if (length <= 125)
{
frame[1] = (byte)length;
indexStartRawData = 2;
}
else if (length >= 126 && length <= 65535)
{
frame[1] = (byte)126;
frame[2] = (byte)((length >> 8) & 255);
frame[3] = (byte)(length & 255);
indexStartRawData = 4;
}
else
{
frame[1] = (byte)127;
frame[2] = (byte)((length >> 56) & 255);
frame[3] = (byte)((length >> 48) & 255);
frame[4] = (byte)((length >> 40) & 255);
frame[5] = (byte)((length >> 32) & 255);
frame[6] = (byte)((length >> 24) & 255);
frame[7] = (byte)((length >> 16) & 255);
frame[8] = (byte)((length >> 8) & 255);
frame[9] = (byte)(length & 255);
indexStartRawData = 10;
}
response = new byte[indexStartRawData + length];
long i, reponseIdx = 0;
//Add the frame bytes to the reponse
for (i = 0; i < indexStartRawData; i++)
{
response[reponseIdx] = frame[i];
reponseIdx++;
}
//Add the data bytes to the response
for (i = 0; i < length; i++)
{
response[reponseIdx] = bytesRaw[i];
reponseIdx++;
}
return response;
}
}
}
Client html and javascript:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
var socket = new WebSocket('ws://localhost:8080/websession');
socket.onopen = function() {
// alert('handshake successfully established. May send data now...');
socket.send("Hi there from browser.");
};
socket.onmessage = function (evt) {
//alert("About to receive data");
var received_msg = evt.data;
alert("Message received = "+received_msg);
};
socket.onclose = function() {
alert('connection closed');
};
</script>
</head>
<body>
</body>
</html>
WebSockets are implemented with a protocol that involves handshake between client and server. I don't imagine they work very much like normal sockets. Read up on the protocol, and get your application to talk it. Alternatively, use an existing WebSocket library, or .Net4.5beta which has a WebSocket API.
Issue
Since you are using WebSocket, spender is correct. After recieving the initial data from the WebSocket, you need to send the handshake message from the C# server before any further information can flow.
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: websocket
Connection: Upgrade
WebSocket-Origin: example
WebSocket-Location: something.here
WebSocket-Protocol: 13
Something along those lines.
You can do some more research into how WebSocket works on w3 or google.
Links and Resources
Here is a protocol specifcation: https://datatracker.ietf.org/doc/html/draft-hixie-thewebsocketprotocol-76#section-1.3
List of working examples:
https://stackoverflow.com/a/5866975/184746
https://stackoverflow.com/a/2212752/184746
http://ashishware.com/websocksample.shtml
http://www.sencha.com/forum/showthread.php?99842-WebSocket-server-in-C

Categories

Resources