You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
6.0 KiB

1 month ago
// 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<Route> 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
}
}