WPF开发-WEB服务器

前言

现在我想实现客户端项目内需要集成WEB服务器,用来提供文件的展示功能,有两种方法

  • 集成第三方WEB服务器如Nginx
  • C#实现

目前我的项目已经从集成Nginx更换为了C#实现,因为需求还是比较简单的,不需要Nginx那么多功能。

集成Nginx

下载Nginx放在项目根目录:如nginx/

属性=>生成事件=>生成前事件命令行中添加

1
2
taskkill /f /t /im nginx.exe
xcopy /Y /i /e $(ProjectDir)\nginx $(TargetDir)\nginx

项目中启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/// <summary>
/// 启动Nginx.
/// </summary>
private static void StartNginx()
{
try
{
StopNginx();
string basePath = AppDomain.CurrentDomain.BaseDirectory;
string nginxPath = System.IO.Path.Combine(basePath, "nginx");
string nginxTempPath = System.IO.Path.Combine(nginxPath, "temp");
DirectoryInfo di = new DirectoryInfo(nginxTempPath);
if (!di.Exists)
{
di.Create();
}
Process mps = new Process();
ProcessStartInfo mpsi = new ProcessStartInfo("nginx.exe")
{
WorkingDirectory = nginxPath,
UseShellExecute = true,
RedirectStandardInput = false,
RedirectStandardOutput = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
mps.StartInfo = mpsi;
mps.Start();
}
catch (Exception ex)
{
LogHelper.WriteErrLog("【启动Nginx】(StartNginx)无法启动Nginx," + ex.Message, ex);
}
}

/// <summary>
/// 关闭Nginx.
/// </summary>
private static void StopNginx()
{
try
{
Process[] processes = Process.GetProcessesByName("nginx");
foreach (Process p in processes)
{
string basePath = AppDomain.CurrentDomain.BaseDirectory;
string nginxPath = System.IO.Path.Combine(basePath, "nginx", "nginx.exe");
try
{
if (nginxPath == p.MainModule.FileName)
{
p.Kill();
p.Close();
}
}
catch (Exception ex)
{
LogHelper.WriteErrLog("【停止Nginx】(StartNginx)无法停止Nginx," + ex.Message, ex);
}
}
}
catch (Exception ex)
{
LogHelper.WriteErrLog("【停止Nginx】(StartNginx)无法和获取到系统进程," + ex.Message, ex);
}
}

端口号和服务目录都在Nginx的配置文件中配置

C#实现

工具类(ZServerHelper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using System;
using System.Net;

namespace SchoolClient.Utils.HttpServer
{
public class ZServerHelper
{
private HttpListener httpListener = new HttpListener();
private string basePath = "";

public void Setup(string basePath, int port = 8080)
{
this.basePath = basePath;
httpListener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
httpListener.Prefixes.Add(string.Format("http://*:{0}/", port));
httpListener.Start();//开启服务

Receive();//异步接收请求
}

private void Receive()
{
httpListener.BeginGetContext(new AsyncCallback(EndReceive), null);
}

private void EndReceive(IAsyncResult ar)
{
HttpListenerContext context = httpListener.EndGetContext(ar);
ZDispather(context);//解析请求
Receive();
}

private ZRequestHelper RequestHelper;
private ZResponseHelper ResponseHelper;

private void ZDispather(HttpListenerContext context)
{
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
RequestHelper = new ZRequestHelper(request, basePath);
ResponseHelper = new ZResponseHelper(response);
RequestHelper.DispatchResources(fs =>
{
ResponseHelper.WriteToClient(fs);// 对相应的请求做出回应
});
}
}
}

请求(ZRequestHelper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.IO;
using System.Net;

namespace SchoolClient.Utils.HttpServer
{
public class ZRequestHelper
{
private HttpListenerRequest request;
private string basepath = "";

public ZRequestHelper(HttpListenerRequest request, string basepath)
{
this.request = request;
this.basepath = basepath;
}

public delegate void ExecutingDispatch(FileStream fs);

public void DispatchResources(ExecutingDispatch action)
{
string rawUrl = request.RawUrl;
string rawPath = rawUrl.Replace("/", Path.DirectorySeparatorChar + "");
//这里对应请求其他类型资源,如图片,文本等
string filePath = string.Format(@"{0}{1}", basepath, rawPath);
//默认文档为index.html
if (rawUrl.Length == 1)
{
filePath = string.Format(@"{0}/index.html", basepath);
}

filePath = filePath.Replace(Path.DirectorySeparatorChar + "" + Path.DirectorySeparatorChar, Path.DirectorySeparatorChar + "");

try
{
if (File.Exists(filePath))
{
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);

action?.Invoke(fs);
}
}
catch { return; }
}
}
}

响应(ZResponseHelper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using System;
using System.IO;
using System.Net;

namespace SchoolClient.Utils.HttpServer
{
public class ZResponseHelper
{
private HttpListenerResponse response;

public ZResponseHelper(HttpListenerResponse response)
{
this.response = response;
outputStream = response.OutputStream;
}

public Stream outputStream { get; set; }

public class FileObject
{
public FileStream fs;
public byte[] buffer;
}

public void WriteToClient(FileStream fs)
{
response.StatusCode = 200;
byte[] buffer = new byte[1024];
FileObject obj = new FileObject() { fs = fs, buffer = buffer };
fs.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(EndWrite), obj);
}

private void EndWrite(IAsyncResult ar)
{
FileObject obj = ar.AsyncState as FileObject;
try
{
int num = obj.fs.EndRead(ar);
outputStream.Write(obj.buffer, 0, num);
if (num < 1)
{
obj.fs.Close(); //关闭文件流
outputStream.Close();//关闭输出流,如果不关闭,浏览器将一直在等待状态
obj.fs.Dispose();
outputStream.Dispose();
return;
}
obj.fs.BeginRead(obj.buffer, 0, obj.buffer.Length, new AsyncCallback(EndWrite), obj);
}
catch (Exception)
{
obj.fs?.Close();
outputStream?.Close();
obj.fs?.Dispose();
outputStream?.Dispose();
}
}
}
}

调用

项目根目录创建文件夹wwwroot

属性=>生成事件=>生成前事件命令行中添加

1
xcopy /Y /i /e $(ProjectDir)\wwwroot $(TargetDir)\wwwroot

项目中启动

1
2
3
4
5
6
7
8
9
10
11
12
13
public static string basePath = AppDomain.CurrentDomain.BaseDirectory;
public static string wwwrootPath = $"{basePath}wwwroot\\";
public void startHttpServer()
{
new Thread(
o =>
{
new ZServerHelper().Setup(wwwrootPath, 8899);
}
)
{ IsBackground = true }
.Start();
}