After fighting all morning, I got the handshake to work but now I'm stuck actually sending and receiving messages. I've been searching for answers to no avail so thought I'd finally step up here and ask :/
My client so far is very simple:
function testWebSocket() {
if (!window.WebSocket) {
alert('WebSockets are NOT supported by your browser.');
return;
}
try {
var ws = new WebSocket('ws://localhost:8181/websession');
ws.onopen = function () {
alert('Handshake successfully established. Ready for data...');
};
ws.onmessage = function (e) {
alert('Got WebSockets message: ' + e.data);
}
ws.onclose = function () {
alert('Connection closed.');
};
}
catch (e) {
alert(e);
}
}
yes, I've done a lot of code borrowing for this...I'm just trying to get a proof of concept working with a simple "chat application"
my server is composed mostly of two classes, the SocketServer.cs and the SocketClient.cs
they follow:
SocketServer.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
namespace WebSocketServer.Entities
{
class SocketServer
{
public static Form1 parentForm;
TcpListener socketServer;
public static List<SocketClient> ClientList = new List<SocketClient>();
public SocketServer(Form1 pForm)
{
parentForm = pForm;
parentForm.ApplyText("Socket Class Initiated\r\n");
socketServer = new TcpListener(IPAddress.Any, 8181);
// tell the console that it's started
parentForm.ApplyText("Socket Server Started\r\n");
// create continuous loops to listen for new connections
// start the listener
socketServer.Start();
while (true)
{
// check for any incoming pending connections
// create new socket client for new connection
TcpClient socketConnection = socketServer.AcceptTcpClient();
DateTime now = DateTime.Now;
//write message to console to indicate new connection
parentForm.ApplyText("New Client Connected - " + now.ToString("MM/dd/yyyy h:mm:ss tt") + "\r\n");
// create new client object for this connection
SocketClient socketClient = new SocketClient(socketConnection, parentForm);
}
}
public static void CloseClient(SocketClient whichClient)
{
ClientList.Remove(whichClient);
whichClient.Client.Close();
// dispose of the client object
whichClient.Dispose();
whichClient = null;
parentForm.ApplyText("Client Disconnected\r\n");
}
public static void SendTextToClient(SocketClient sc, string text)
{
StreamWriter writer = new StreamWriter(sc.Client.GetStream());
// check if client is still connected, then send the text string
try
{
if (sc.Client.Connected)
{
writer.WriteLine(text);
writer.Flush();
writer = null;
}
}
catch
{
CloseClient(sc);
}
}
public static void SendBroadcast(string text)
{
StreamWriter writer;
// loop through the array and send text to all clients
foreach (SocketClient client in ClientList)
{
if (client.Client.Connected)
{
try
{
writer = new StreamWriter(client.Client.GetStream());
writer.WriteLine(text);
writer.Flush();
writer = null;
}
catch
{
CloseClient(client);
}
}
}
}
}
}
SocketClient.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Security.Cryptography;
namespace WebSocketServer.Entities
{
class SocketClient
{
public TcpClient Client;
StreamReader reader;
StreamWriter writer;
Form1 parentForm;
public SocketClient(TcpClient client, Form1 pForm)
{
parentForm = pForm;
Client = client;
Thread clientThread = new Thread(new ThreadStart(StartClient));
clientThread.Start();
}
private void StartClient()
{
SocketServer.ClientList.Add(this);
// create a reader for this client
reader = new StreamReader(Client.GetStream());
// create a writer for this client
writer = new StreamWriter(Client.GetStream());
var headers = new Dictionary<string, string>();
string line = "";
while ((line = reader.ReadLine()) != string.Empty)
{
if (!string.IsNullOrEmpty(line))
{
var tokens = line.Split(new char[] { ':' }, 2);
if (!string.IsNullOrWhiteSpace(line) && tokens.Length > 1)
{
headers[tokens[0]] = tokens[1].Trim();
}
}
}
String secWebSocketAccept = ComputeWebSocketHandshakeSecurityHash09(headers["Sec-WebSocket-Key"]);
// send handshake to this client only
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: http://localhost:63422/");
writer.WriteLine("WebSocket-Location: ws://localhost:8181/websession");
writer.WriteLine("Sec-WebSocket-Accept: " + secWebSocketAccept);
writer.WriteLine("");
writer.Flush();
SocketServer.SendBroadcast("New Client Connected");
Thread clientRun = new Thread(new ThreadStart(RunClient));
clientRun.Start();
}
public static String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey)
{
const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
String secWebSocketAccept = String.Empty;
// 1. Combine the request Sec-WebSocket-Key with magic key.
String ret = secWebSocketKey + MagicKEY;
// 2. Compute the SHA1 hash
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
// 3. Base64 encode the hash
secWebSocketAccept = Convert.ToBase64String(sha1Hash);
return secWebSocketAccept;
}
private void RunClient()
{
try
{
string line = "";
while (true)
{
line = reader.ReadLine();
if (!string.IsNullOrEmpty(line))
{
parentForm.ApplyText(line + "\r\n");
SocketServer.SendBroadcast(line);
}
}
}
catch
{
parentForm.ApplyText("Client Disconnected\r\n");
SocketServer.CloseClient(this);
}
}
public void Dispose()
{
System.GC.SuppressFinalize(this);
}
}
}
I can connect with multiple instances in Chrome and my server shows all of the clients connecting and I'm seeing the alert that the handshake was successful. But when I try sending text from the client (code not shown above, but it's a pretty straightforward ws.send(text) kinda thing), it comes across to the server as garbled text. When I try and do a writer.WriteLine("whatever") from the server to the client, the onmessage event never fires. I've done a LOT of looking around after I finally got the handshake sorted out, and can't find any good examples of how to resolve this.
should I not be using a StreamWriter? Am I missing something else in my handshake (a protocol maybe).
TIA for looking and helping.
Edit:
The below code works but I don't know how to modify it to allow for dynamic sized text lengths. I'm ok with just being able to send text that is 127 or less at this point, but I can't seem to get a handle on how to even get past 4.
public static void SendBroadcast(string text)
{
StreamWriter writer;
// loop through the array and send text to all clients
foreach (SocketClient client in ClientList)
{
if (client.Client.Connected)
{
try
{
NetworkStream l_Stream = client.Client.GetStream();
List<byte> lb = new List<byte>();
lb.Add(0x81);
lb.Add(0x04);
lb.AddRange(Encoding.UTF8.GetBytes("test"));
l_Stream.Write(lb.ToArray(), 0, 6);
}
catch
{
CloseClient(client);
}
}
}
}
I've tried modifying lb.Add(0x04) to lb.Add(0x07) and sending "testing" no dice. I'm also confused as to what the l_Stream.Write() parameters are. I know it's the byte array, offset, and size but size of what?