/// <summary> /// 响应客户端 /// </summary> /// <param name="build"></param> /// <param name="status"></param> protected override void Response(StringBuilder build, HttpStatusCode status) { byte[] contentBuffer = null; byte[] headerBuffer = null; //header if (this.chunkWriteStatus == HttpServerChunkStatus.NoBegin) { if (this.contentBuffer != null) { contentBuffer = this.contentBuffer; } else if (!string.IsNullOrEmpty(this.content)) { contentBuffer = this.Encoding.GetBytes(this.content); } else { contentBuffer = new byte[0]; } //gzip if (this.acceptGzip && this.gzipThreshold != 0 && contentBuffer.Length > this.gzipThreshold) { build.AppendLine("Content-Encoding: gzip"); using (var m = new MemoryStream()) using (var stream = new GZipStream(m, CompressionMode.Compress, true)) { stream.Write(contentBuffer, 0, contentBuffer.Length); stream.Close(); //content contentBuffer = m.ToArray(); } } build.AppendLine(string.Concat("Content-Length: ", contentBuffer.Length)); build.AppendLine(); } else { if (this.acceptGzip && this.gzipThreshold != 0) { build.AppendLine("Content-Encoding: gzip"); } build.AppendLine("Transfer-Encoding: chunked"); build.AppendLine(); } headerBuffer = Encoding.ASCII.GetBytes(build.ToString()); // if (this.chunkWriteStatus == HttpServerChunkStatus.NoBegin) { if (this.isRequestHead == false && contentBuffer != null && contentBuffer.Length > 0) { var buffers = new ArraySegment <byte> [2]; //header buffers[0] = new ArraySegment <byte>(headerBuffer); //content buffers[1] = new ArraySegment <byte>(contentBuffer); //send this.socket.Send(buffers); } else { this.socket.Send(headerBuffer); } } else { try { this.socket.Send(headerBuffer); } catch { SocketHelper.TryClose(this.socket); StreamHelper.TryClose(this.chunkGzipStream); StreamHelper.TryClose(this.chunkStream); throw; } } }
/// <summary> /// 打开连接 /// </summary> /// <exception cref="IOException">connection failure</exception> /// <exception cref="WebException">web handshake exception</exception> /// <returns></returns> public void Connection() { this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { var localEP = this.localEndPoint; if (localEP != null) { this.socket.Bind(localEP); } this.socket.Connect(this.host, this.port); } catch (Exception exception) { throw new IOException(exception.Message, exception); } this.stream = new NetworkStream(this.socket, false); //header var builder = new StringBuilder(128); builder.AppendFormat("GET {0} HTTP/1.1", this.Path); builder.AppendLine(); foreach (var item in this.RequestHeader) { builder.AppendFormat("{0}: {1}", item.Key, item.Value); builder.AppendLine(); } //end builder.AppendLine(); //send var buffer = this.Encoding.GetBytes(builder.ToString()); this.stream.Write(buffer, 0, buffer.Length); //receive header string line; string[] harr; //HTTP/1.1 101 Switching Protocols line = StreamHelper.ReadLine(this.stream, Encoding.ASCII); if (!line.StartsWith("HTTP/1.1 101")) { throw new WebException("Handshake failure, " + line); } while ((line = StreamHelper.ReadLine(this.stream, Encoding.ASCII)) != null && !line.Equals(string.Empty)) { harr = line.Split(':'); this.ResponseHeader[harr[0]] = harr[1].Trim(); } //1、如果从服务器接收到的状态码不是101,按HTTP【RFC2616】程序处理响应。在特殊情况下,如果客户端接收到401状态码,可能执行认证;服务器可能用3xx状态码重定向客户端(但不要求客户端遵循他们),等等。否则按下面处理。 //2、如果响应缺失Upgrade头域或Upgrade头域的值没有包含大小写不敏感的ASCII 值"websocket",客户端必须使WebSocket连接失败。 //3、如果响应缺失Connection头域或其值不包含大小写不敏感的ASCII值"Upgrade",客户端必须使WebSocket连接失败。 //4、如果响应缺失Sec-WebSocket-Accept头域或其值不包含 |Sec-WebSocket-Key |(作为字符串,非base64解码的)+ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"的base64编码 SHA-1值,客户端必须使WebSocket连接失败。 //5、如果响应包含Sec-WebSocket-Extensions头域,且其值指示使用的扩展不出现在客户端发送的握手(服务器指示的扩展不是客户端要求的),客户端必须使WebSocket连接失败。(解析此头域来决定哪个扩展是要求的在第9.1节描述。) //6、如果响应包含Sec-WebSocket-Protocol头域,且这个头域指示使用的子协议不包含在客户端的握手(服务器指示的子协议不是客户端要求的),客户端必须使WebSocket连接失败。 //check, 此客户端仅验证 Sec-WebSocket-Accept,其它均不处理 string sec_WebSocket_Accept; if (!this.ResponseHeader.TryGetValue("Sec-WebSocket-Accept", out sec_WebSocket_Accept)) { throw new WebException("No Response Sec-WebSocket-Accept"); } if (!this.handshakeSecurityHash09.Equals(sec_WebSocket_Accept)) { throw new WebException("Handshake failure for Sec-WebSocket-Accept"); } //receive this.Read(); //ping start this.isConnectioned = true; //trigger event this.OnConnectioned(); }
/// <summary> /// End Write /// </summary> /// <exception cref="InvalidOperationException">No Invoke BeginWrite / Has Been Invoked EndWrite</exception> public void EndWrite() { lock (this.chunkWriteEvent) { if (this.chunkWriteStatus == HttpServerChunkStatus.NoBegin) { throw new InvalidOperationException("No Invoke BeginWrite"); } if (this.chunkWriteStatus == HttpServerChunkStatus.End) { throw new InvalidOperationException("Has Been Invoked EndWrite"); } if (this.chunkWriteStatus == HttpServerChunkStatus.Writing) { this.chunkWriteStatus = HttpServerChunkStatus.End; try { //gzip if (this.acceptGzip && this.gzipThreshold != 0) { this.CloseChunkGzip(); } if (this.isRequestHead == false) { var crlfBuffer = this.Encoding.GetBytes("\r\n"); var buffers = new ArraySegment <byte> [3]; using (var m = new MemoryStream(512)) { //len var lenBuffer = this.Encoding.GetBytes("0"); buffers[0] = new ArraySegment <byte>(lenBuffer); buffers[1] = new ArraySegment <byte>(crlfBuffer); //content buffers[2] = new ArraySegment <byte>(crlfBuffer); } // try { this.socket.Send(buffers); } catch { SocketHelper.TryClose(this.socket); throw; } finally { StreamHelper.TryClose(this.chunkGzipStream); StreamHelper.TryClose(this.chunkStream); this.chunkWriteEvent.Set(); } } else { this.chunkWriteEvent.Set(); } } catch (Exception exception) { throw new InvalidOperationException("End check write failure", exception); } } } }