// Copyright (C) 2016 by David Jeske, Barend Erasmus and donated to the public domain using SimpleHttpServer; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using Sog; namespace SimpleHttpServer { public class HttpServer { #region Fields private int Port; private TcpListener Listener; private HttpProcessor Processor; private bool IsActive = true; private uint httpCtxSeq = 1; #endregion //http处理线程结束钱等待时间,有些http response依赖其他服务器返回,没法立即处理,防止垃圾回收造成网络断线影响,增加这个时间 public int ProcessorThreadEndWaitTimeMs = 5000; #region Public Methods public HttpServer(int port, List routes) { this.Port = port; this.Processor = new HttpProcessor(); foreach (var route in routes) { this.Processor.AddRoute(route); } } public void Listen() { this.Listener = new TcpListener(IPAddress.Any, this.Port); this.Listener.Start(); while (this.IsActive) { TcpClient s = this.Listener.AcceptTcpClientAsync().Result; //保证response的时候立即发送,usdk那边总是会出现callback我们支付服务器没有收到response就tcp reset了 s.NoDelay = true; var httpCtx = new HttpContext(s) { id = httpCtxSeq++ }; //Task中使用sleep是不正确的用法,Task底层使用的是线程池,并不会由于调用Sleep把线程让出来,使用sleep导致能够执行的Task数量是受线程池大小限制 zouwei 2023/5/25 //new Thread性能在windows下非常的低,linux高些,后期业务量大了,可以考虑重构这个网络代码,比如用Sog.SessionListener代替,或者简单的用select模型都比这个靠谱,或者使用await异步 try { var t = new Thread(() => { this.Processor.HandleClient(httpCtx); var beginTime = DateTime.Now; // 之前发现微软云经常出现tcp连接被RST导致http response没有发送成功, 看看是否线程内部异常 try { while (true) { if ((DateTime.Now - beginTime).TotalMilliseconds >= ProcessorThreadEndWaitTimeMs) { if (httpCtx != null && httpCtx.httpRequest != null) { TraceLog.Error("SyncHttpServerService request method {0} url {1} timeout!", httpCtx.httpRequest.Method, httpCtx.httpRequest.Url); } else { TraceLog.Error("SyncHttpServerService request timeout!"); } break; } Thread.Sleep(10); if (httpCtx.isSendRes) { Thread.Sleep(200); break; } } //关闭一下,usdk那边总是会出现callback我们支付服务器没有收到response就tcp reset了 httpCtx.client.Close(); } catch (Exception e) { TraceLog.Exception(e); if (e.InnerException != null) { TraceLog.Exception(e.InnerException); } } }, 1024 * 1024); t.IsBackground = true; t.Start(); } catch (Exception ex) { TraceLog.Exception(ex); } } } // this formats the HTTP response... public static void SendResponse(HttpContext httpCtx) { try { HttpResponse response = httpCtx.httpResponse; // default to text/html content type if (!response.Headers.ContainsKey("Content-Type")) { response.Headers["Content-Type"] = "text/html"; } response.Headers["Content-Length"] = response.Content.Length.ToString(); var stream = httpCtx.client.GetStream(); // Keep-Alive在1.0中默认关闭 Write(stream, string.Format("HTTP/1.0 {0} {1}\r\n", response.StatusCode, response.ReasonPhrase)); Write(stream, string.Join("\r\n", response.Headers.Select(x => string.Format("{0}: {1}", x.Key, x.Value)))); Write(stream, "\r\n\r\n"); if (response.Content == null) { response.Content = new byte[] { }; } stream.Write(response.Content, 0, response.Content.Length); } catch (Exception e) { TraceLog.Exception(e); } finally { httpCtx.isSendRes = true; } } private static void Write(Stream stream, string text) { byte[] bytes = Encoding.UTF8.GetBytes(text); stream.Write(bytes, 0, bytes.Length); } #endregion } }