Skip to content

Instantly share code, notes, and snippets.

@dearing
Created February 10, 2012 07:33
Show Gist options
  • Save dearing/1787430 to your computer and use it in GitHub Desktop.
Save dearing/1787430 to your computer and use it in GitHub Desktop.
RFC6455: simple echo test with websocket after handshake
/* http://tools.ietf.org/html/rfc6455#section-5.2
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
*/
namespace websockets
{
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net.Sockets;
public class WebSocketFrame : IDisposable
{
public enum Opcodes : byte
{
Continue = 0x0, // =frrr-0000 : non-control = continuation of fragment sequence
Text = 0x1, // =frrr-0001 : non-control = data to be handled as utf-8
Binary = 0x2, // =frrr-0010 : non-control = data to be handled as binary
Close = 0x8, // =frrr-1000 : control = close connection
Ping = 0x9, // =frrr-1001 : control = ping request
Pong = 0xA // =frrr-1010 : control = pong responce
}
public enum PayloadTypes : byte
{
Tall = 0x00, // =m000-0000 : frame[2] = payload length; 0..125 bytes
Grande = 0x7E, // =m111-1110 : frame[3,4] = payload length; 16-bit as unsigned integer (uShort)
Venti = 0x7D // =m111-1111 : frame[3..11] = payload length; 64-bit as unsigned integer (uLong)
}
public List<byte> echo{get;set;}
public UInt64 PayloadLength { get; set; }
public Opcodes Opcode { get; set; }
public Byte[] Mask { get; set; }
public Boolean isFinal { get; set; }
public Boolean isMasked { get; set; }
public void ProcessHeader(NetworkStream stream)
{
byte[] header = new byte[2];
byte[] mask = new byte[4];
echo = new List<byte>();
stream.Read(header, 0, 2);
echo.AddRange(header);
this.isFinal = (header[0] & 0x80) == 0x80;
this.Opcode = (Opcodes)(header[0] & 0x7F);
this.isMasked = (header[1] & 0x80) == 0x80;
this.PayloadLength = (ulong)(header[1] & 0x7F);
if (PayloadLength == 126)
{
byte[] len = new byte[2];
stream.Read(len, 0, 2);
Array.Reverse(len);
this.PayloadLength = (ulong)BitConverter.ToUInt16(len, 0);
}
else if (PayloadLength == 127)
{
byte[] len = new byte[8];
stream.Read(len, 0, 8);
Array.Reverse(len);
this.PayloadLength = (ulong)BitConverter.ToUInt64(len, 0);
}
byte[] data = new byte[PayloadLength];
stream.Read(mask, 0, 4);
echo.AddRange(mask);
stream.Read(data, 0, data.Length);
echo.AddRange(data);
if(isMasked)
for (int i = 0; i < data.Length; i++)
data[i] ^= mask[i % 4];
Console.WriteLine("{0}: isFinal?{1}: Length:{2}", this.Opcode,this.isFinal,this.PayloadLength);
if(Opcode == Opcodes.Text)
Console.WriteLine(Encoding.UTF8.GetString(data));
stream.Write(echo.ToArray(), 0, echo.Count);
stream.Flush();
}
public void Dispose()
{
Console.WriteLine("Echo complete.");
}
}
}
@dearing
Copy link
Author

dearing commented Feb 10, 2012

those looking may find this snippet helpful when reading http://tools.ietf.org/html/rfc6455#section-5 and drinking some coffee

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment