public async void Close(ushort code, string reason) { if (_closed) { return; } byte[] data = null; if (reason != null && reason.Length > 0) { try { data = Encoding.UTF8.GetBytes(reason); } catch (Exception e) { data = new byte[0]; } } else { data = new byte[0]; } byte[] frame = FrameParser.BuildFrame(data, FrameParser.OP_CLOSE, code, IS_CLIENT, true); await SendFrame(frame); this.Disconnect(); }
/// <summary> /// Ping /// </summary> /// <param name="data"></param> /// <returns></returns> public async Task Ping(byte[] data = null) { if (_closed) { return; } byte[] frame = FrameParser.BuildFrame(data, FrameParser.OP_PING, -1, IS_CLIENT, true); await SendFrame(frame); }
public async Task SendFragment(byte[] data, bool isFirst, bool isLast) { if (_closed) { return; } byte[] frame = FrameParser.BuildFrame(data, isFirst ? FrameParser.OP_BINARY : FrameParser.OP_CONTINUATION, -1, IS_CLIENT, isLast); await SendFrame(frame); }
public async Task SendFragment(string str, bool isFirst, bool isLast) { if (_closed) { return; } byte[] data = Encoding.UTF8.GetBytes(str); byte[] frame = FrameParser.BuildFrame(data, isFirst ? FrameParser.OP_TEXT : FrameParser.OP_CONTINUATION, -1, IS_CLIENT, isLast); await SendFrame(frame); }
private async Task ProcessIncomingFrame(FrameParser.Frame frame) { switch (frame.opCode) { case FrameParser.OP_CONTINUATION: await _stream.WriteAsync(frame.payload?.ToBuffer()); if (frame.isFinal) { var buffer = _stream.ToBuffer(); byte[] message = buffer.ToByteArray(); if (this.OnMessage != null) { this.OnMessage(this, new WebSocketMessageEventArgs(this.Control.MessageType, message)); } this.ResetStream(); } break; case FrameParser.OP_TEXT: if (frame.isFinal) { if (this.OnMessage != null) { this.OnMessage(this, new WebSocketMessageEventArgs(this.Control.MessageType, frame.payload)); } } else { if (_stream.Size != 0) { throw new IOException("no FIN frame"); } this.Control.MessageType = WebSocketMessageType.Text; await _stream.WriteAsync(frame.payload.ToBuffer()); } break; case FrameParser.OP_BINARY: if (frame.isFinal) { if (this.OnMessage != null) { this.OnMessage(this, new WebSocketMessageEventArgs(this.Control.MessageType, frame.payload)); } } else { if (_stream.Size != 0) { throw new IOException("no FIN frame"); } this.Control.MessageType = WebSocketMessageType.Binary; await _stream.WriteAsync(frame.payload.ToBuffer()); } break; case FrameParser.OP_CLOSE: int code = 0; if (frame.payload.Length >= 2) { code = ((frame.payload[0] << 8) | (frame.payload[1] & 0xFF)) & 0xFFFF; } string reason = null; if (frame.payload.Length > 2) { var temp = frame.payload.CopyOfRange(2, frame.payload.Length); reason = Encoding.UTF8.GetString(temp, 0, temp.Length); } if (this.Closed != null) { this.Closed(this, new WebSocketClosedEventArgs(code, reason)); } this.Disconnect(); break; case FrameParser.OP_PING: if (frame.payload.Length > 125) { throw new IOException("Ping payload too large"); } byte[] data = FrameParser.BuildFrame(frame.payload, FrameParser.OP_PONG, -1, IS_CLIENT, true); await SendFrame(data); break; case FrameParser.OP_PONG: if (this.OnPong != null) { this.OnPong(this, frame.payload); } break; } }
private async Task ConnectClientInternal() { if (_socket != null) { throw new Exception("connect() is already called"); } try { int port = (_uri.Port != -1) ? _uri.Port : (_uri.Scheme.Equals("wss") ? 443 : 80); string path = (_uri.AbsolutePath != null) ? _uri.AbsolutePath : "/"; if (_uri.Query != null) { path += "?" + _uri.Query; } string originScheme = _uri.Scheme.Equals("wss") ? "https" : "http"; Uri origin = new Uri(originScheme + "://" + _uri.Host); // To fix: get Origin from extraHeaders if set there. this._socket = new InternalSocket(); await this._socket.ConnectAsync(_uri.Host, port); if (_uri.Scheme.Equals("wss")) { await this._socket.UpgradeToSslAsync(_uri.Host); } string key = WebSocketHelper.CreateClientKey(); DataWriter writer = new DataWriter(_socket.OutputStream); writer.WriteString("GET " + path + " HTTP/1.1\r\n"); writer.WriteString("Upgrade: websocket\r\n"); writer.WriteString("Connection: Upgrade\r\n"); writer.WriteString("Host: " + _uri.Host + "\r\n"); writer.WriteString("Origin: " + origin.ToString() + "\r\n"); writer.WriteString("Sec-WebSocket-Key: " + key + "\r\n"); writer.WriteString("Sec-WebSocket-Version: 13\r\n"); if (_extraRequestHeaders != null) { foreach (Http.Header header in _extraRequestHeaders) { writer.WriteString(header.ToString() + "\r\n"); } } writer.WriteString("\r\n"); await writer.StoreAsync(); //异步发送数据 writer.DetachStream(); //分离 writer.Dispose(); //结束writer DataReader reader = new DataReader(_socket.InputStream); reader.ByteOrder = ByteOrder.LittleEndian; //// Read HTTP response status line. var startLine = await ReadLine(reader); if (startLine == null) { throw new Exception("Received no reply from server."); } Http.StatusLine statusLine = new Http.StatusLine(startLine); int statusCode = statusLine.StatusCode; if (statusCode != 101) { throw new Exception("wrong HTTP response code: " + statusCode); } // Read HTTP response headers. string line; while ((line = await ReadLine(reader)) != null && line.Length > 0) { Http.Header header = new Http.Header(line); Debug.WriteLine(line); if (header.HeaderName.Equals("Sec-WebSocket-Accept", StringComparison.OrdinalIgnoreCase)) { string receivedAccept = header.HeaderValue; string shouldBeAccept = WebSocketHelper.CreateAccept(key); if (!receivedAccept.Equals(shouldBeAccept)) { throw new Exception("Wrong Sec-WebSocket-Accept: " + receivedAccept + " should be: " + shouldBeAccept); } } if (_serverResponseHeaders == null) { _serverResponseHeaders = new List <Http.Header>(); } _serverResponseHeaders.Add(header); } if (this.Opened != null) { this.Opened(this, EventArgs.Empty); } //Upgrade: websocket //Connection: Upgrade //Sec-WebSocket-Accept: 1xY289lHcEMbLpEBgOYRBBL9N9c= //Sec-WebSocket-Protocol: chat //Content-Type: application/octet-stream //Seq-Id: 667035124 // Read & process frame while (true) { FrameParser.Frame frame = await FrameParser.ReadFrame(reader); if (frame != null) { await ProcessIncomingFrame(frame); } } } catch (IOException ex) { if (this.Closed != null) { this.Closed(this, new WebSocketClosedEventArgs(0, "EOF")); } } catch (Exception ex) { if (this.Closed != null) { this.Closed(this, new WebSocketClosedEventArgs(0, ex.Message)); } } finally { Disconnect(); } }
private async Task ProcessIncomingFrame(FrameParser.Frame frame) { switch (frame.opCode) { case FrameParser.OP_CONTINUATION: //if (this.Control.MessageType == default(WebSocketMessageType)) //{ // throw new IOException("Unexpected CONTINUATION frame"); //} _stream.Write(frame.payload, 0, frame.payload.Length); if (frame.isFinal) { byte[] message = _stream.StreamToBytes(); if (this.OnMessage != null) { this.OnMessage(this, new WebSocketMessageEventArgs(this.Control.MessageType, message)); } ResetBuffer(); } break; case FrameParser.OP_TEXT: if (frame.isFinal) { if (this.OnMessage != null) { this.OnMessage(this, new WebSocketMessageEventArgs(this.Control.MessageType, frame.payload)); } } else { if (_stream.Length != 0) { throw new IOException("no FIN frame"); } this.Control.MessageType = WebSocketMessageType.Text; _stream.Write(frame.payload, 0, frame.payload.Length); } break; case FrameParser.OP_BINARY: if (frame.isFinal) { if (this.OnMessage != null) { this.OnMessage(this, new WebSocketMessageEventArgs(this.Control.MessageType, frame.payload)); } } else { if (_stream.Length != 0) { throw new IOException("no FIN frame"); } this.Control.MessageType = WebSocketMessageType.Binary; _stream.Write(frame.payload, 0, frame.payload.Length); } break; case FrameParser.OP_CLOSE: int code = 0; if (frame.payload.Length >= 2) { code = ((frame.payload[0] << 8) | (frame.payload[1] & 0xFF)) & 0xFFFF; } string reason = null; if (frame.payload.Length > 2) { reason = frame.payload.CopyOfRange(2, frame.payload.Length).BytesToString(); } if (this.Closed != null) { this.Closed(this, new WebSocketClosedEventArgs(code, reason)); } Disconnect(); break; case FrameParser.OP_PING: if (frame.payload.Length > 125) { throw new IOException("Ping payload too large"); } byte[] data = FrameParser.BuildFrame(frame.payload, FrameParser.OP_PONG, -1, IS_CLIENT, true); await SendFrame(data); break; case FrameParser.OP_PONG: if (this.OnPong != null) { this.OnPong(this, frame.payload); } break; } }
private void ConnectServer() { Task.Factory.StartNew(async() => { try { DataReader stream = new DataReader(this._socket.InputStream); // Read HTTP request line. string startLine = await this.ReadLine(stream); if (startLine == null) { throw new Exception("Cannot read HTTP request start line"); } Http.RequestLine requestLine = new Http.RequestLine(startLine); this._uri = new Uri(requestLine.RequestURI); // can be checked in // onConnect() // Read HTTP response headers Dictionary <string, string> map = new Dictionary <string, string>(); string line; while ((line = await this.ReadLine(stream)) != null && line.Length > 0) { Http.Header header = new Http.Header(line); map.Add(header.HeaderName.ToLower(), header.HeaderValue); } string value = map["sec-websocket-version"]; if (!"13".Equals(value)) { throw new IOException("wrong Sec-WebSocket-Version"); } string key = map["sec-websocket-key"]; if (key == null) { throw new IOException("missed Sec-WebSocket-Key"); } string accept = WebSocketHelper.CreateAccept(key); string upgrade = map["upgrade"]; if (upgrade == null || !upgrade.Equals("websocket", StringComparison.OrdinalIgnoreCase)) { throw new IOException("wrong Upgrade"); } string connection = map["connection"]; if (connection == null || !connection.Equals("upgrade", StringComparison.OrdinalIgnoreCase)) { throw new IOException("wrong Connection"); } // Host and Origin can be checked later in onConnect() callback. this._host = map["host"]; if (this._host == null) { throw new IOException("Missed 'Host' header"); } this._origin = map["origin"]; if (this._origin == null) { throw new IOException("Missed 'Origin' header"); } // Some naive protocol selection. string protocols = map["sec-websocket-protocol"]; string selectedProtocol = null; if (protocols != null && protocols.Contains("chat")) { selectedProtocol = "chat"; } DataWriter writer = new DataWriter(this._socket.OutputStream); writer.WriteString("HTTP/1.1 101 Switching Protocols\r\n"); writer.WriteString("Upgrade: websocket\r\n"); writer.WriteString("Connection: Upgrade\r\n"); writer.WriteString("Sec-WebSocket-Accept: " + accept + "\r\n"); if (selectedProtocol != null) { writer.WriteString("Sec-WebSocket-Protocol: " + selectedProtocol + "\r\n"); } writer.WriteString("\r\n"); await writer.FlushAsync(); if (this.Opened != null) { this.Opened(this, EventArgs.Empty); } // Read & process frame for (;;) { FrameParser.Frame frame = await FrameParser.ReadFrame(stream); await this.ProcessIncomingFrame(frame); } } catch (IOException ex) { if (this.Closed != null) { this.Closed(this, new WebSocketClosedEventArgs(0, "EOF")); } } catch (Exception ex) { if (this.Closed != null) { this.Closed(this, new WebSocketClosedEventArgs(0, "Exception")); } } finally { this.Disconnect(); } }); }