/** * Async callback for data sent by client */ private void ClientDataCallback(IAsyncResult state) { try { WsCallbackState cs = (WsCallbackState)state.AsyncState; NetworkStream stream = cs.client.GetStream(); int bytes = stream.EndRead(state); if (bytes > 0) { byte[] frames = new byte[bytes]; Array.Copy(cs.buffer, 0, frames, 0, bytes); bool closed = false; try { // decode client message WsDataFrame frame = FramesToMessage(frames); if (frame.opcode == 0x8) // close frame { int close_code = 0; if (frame.payload.Length == 2) { close_code = ((frame.payload[0] & 0xff) << 8) + (frame.payload[1] & 0xff); } byte[] close_msg = WsDataFrame.CloseFrame(close_code); stream.Write(close_msg, 0, close_msg.Length); cs.client.Close(); _clients.Remove(cs.client); closed = true; } else if (frame.opcode == 0x9) // ping frame { byte[] pong_msg = WsDataFrame.PongFrame(frame.payload); stream.Write(pong_msg, 0, pong_msg.Length); } } catch (WsFatalClientException ex) { // client did something wrong. kill connection if (_verbose > 0) { System.Console.WriteLine(ex); } cs.client.Close(); _clients.Remove(cs.client); closed = true; } catch (Exception ex) { if (_verbose > 0) { System.Console.WriteLine(ex); } } if (!closed) { MonitorClient(cs.client); } } else // zero-bytes read implies closed connection, I think { cs.client.Close(); _clients.Remove(cs.client); } } catch (Exception) { } }
private WsDataFrame FramesToMessage(byte[] frames) { if (frames.Length < 2) { throw new WsProtocolException("Invalid frame: too short (header)"); } int opcode = frames[0] & 0xf; int last_fragment = frames[0] & 0x80; int masked = frames[1] & 0x80; long length = frames[1] & 0x7f; int data_starts = 6; if (masked == 0) { throw new WsFatalClientException("Client data not masked"); } byte[] mask_key = new byte[4]; if (length <= 125) { if (frames.Length < 6) { throw new WsProtocolException("Invalid frame: too short (mask)"); } Array.Copy(frames, 2, mask_key, 0, 4); } else if (length == 126) { if (frames.Length < 8) { throw new WsProtocolException("Invalid frame: too short (mask)"); } length = ((frames[2] << 8) & 0xff00) + (frames[3] & 0xff); Array.Copy(frames, 4, mask_key, 0, 4); data_starts = 8; } else if (length == 127) { if (frames.Length < 14) { throw new WsProtocolException("Invalid frame: too short (mask)"); } length = ((frames[2] & 0xff) << 56) + ((frames[3] & 0xff) << 48) + ((frames[4] & 0xff) << 40) + ((frames[5] & 0xff) << 32) + ((frames[6] & 0xff) << 24) + ((frames[7] & 0xff) << 16) + ((frames[8] & 0xff) << 8) + (frames[9] & 0xff); Array.Copy(frames, 10, mask_key, 0, 4); data_starts = 14; } if (frames.Length < (data_starts + length)) { throw new WsProtocolException("Invalid frame: too short (payload)"); } byte[] payload = new byte[length]; for (int i = 0; i < length; i++) { payload[i] = (byte)(frames[i + data_starts] ^ mask_key[i % 4]); } if (last_fragment == 0) { byte[] remainder = new byte[frames.Length - (data_starts + length)]; Array.Copy(frames, data_starts + length, remainder, 0, remainder.Length); WsDataFrame others = FramesToMessage(remainder); byte[] full_payload = new byte[payload.Length + others.payload.Length]; Array.Copy(payload, 0, full_payload, 0, payload.Length); Array.Copy(others.payload, 0, full_payload, payload.Length, others.payload.Length); return(new WsDataFrame(opcode, full_payload)); } else { return(new WsDataFrame(opcode, payload)); } }