上一期我们介绍了使用TCP协议来建立流式传输实现实时的图像显示。我们也简单的提到了HTTP与TCP协议的优缺点。
本期我们简单的介绍一下HTTP并且使用C#制作一个HTTP助手来接收我们的HTTP消息。
HTTP简介 HTTP是超文本传输协议( Hypertext Transfer Protocol )的简称,既然是一种协议,HTTP分为
客户端和服务端 ,顾名思义服务端是为客户端服务的。 服务端不会主动发送请求,但是服务端可以响应客服端发出的请求 。
客户端通过请求报文向服务端发送消息,服务端接收到请求报文后会使用响应报文对客户端进行响应,HTTP规定了请求报文和响应报文的格式。 HT
TP请求报文如下所示 <请求方法>
| <请求路劲 > | <版本 >
—|—|—
<请求内容>….
GET https://pic.qr2c.cn/1.1......
例如这个例子,请求方法是GET,请求路劲是https://pic.qr2c.cn/1.1
HTTP响应报文如下所示 <版本>
| <状态码 > | <原因 >
—|—|—
<响应内容>
HTTP/1.1 200 OK.......
200通常代表着响应成功。
其实关于HTTP还有好多好多内容,我们就简单的介绍一下HTTP的请求和响应机制,接着使用C#编写测试代码。
界面布局
还是简单的布置一下我们的界面,使用一个TextBox来接收客户端发送的请求报文,下面设置一个我们的自定义响应文本。
右边使用Comombox控件来存放电脑的可用IP地址,并且可以选择输入端口号。
设置波形显示的勾选框,方便后期可以从HTTP协议中解析波形。
设置详细信息的勾选框,可以选择显示详细信息还是显示简短的信息。
主要代码 ****
private void LoadLocalIPv4Addresses() { try { // 获取本地主机名 string hostName = Dns.GetHostName(); // 获取主机名对应的IP地址列表 IPAddress[] ipAddresses = Dns.GetHostAddresses(hostName); // 筛选IPv4地址并添加到ComboBox foreach (IPAddress ipAddress in ipAddresses.Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)) { IPAddressChoose.Items.Add(ipAddress.ToString()); }
// 设置默认选择第一个IPv4地址 if (IPAddressChoose.Items.Count > 0) { IPAddressChoose.SelectedIndex = 0; } } catch (Exception ex) { MessageBox.Show($"错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
利用Dns.GetHostName的方法获得主机的可用IP地址,并将地址写入名为IPAddressChoose的Comombox控件中
需要注意的是传入格式的问题IPAddresses是IPAddress类型的数据,我们需要将其转换为IPAddress类型传入IPAddressChoose控件的Text属性中。
private HttpListener httpListener = null; private void CreateHttpListener() { try { httpListener = new HttpListener(); // 获取选择的 IP 地址和端口号 string selectedIpAddress = IPAddressChoose.SelectedItem.ToString(); string selectedPort = PortChoose.Text;
// 创建监听地址 string listenUrl = $"https://pic.qr2c.cn/";
// 创建 HttpListener 实例 httpListener.Prefixes.Add(listenUrl);
// 启动监听器 try { // 启动监听器 httpListener.Start(); IPAddressChoose.Enabled = true; PortChoose.Enabled = true; // 异步处理接收请求 Task.Run(async () => { while (true) { // 等待请求连接 HttpListenerContext context = await httpListener.GetContextAsync();
// 处理请求 HandleHttpRequest(context); } }); } catch (HttpListenerException ex) { // 输出错误信息或采取适当的处理措施 MessageBox.Show("访问端口失败rn如未启动管理员模式请使用管理员模式启动rn检查端口是否被占用可以尝试更换端口"); } } catch (Exception ex) { MessageBox.Show($"Error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
private void HandleHttpRequest(HttpListenerContext context) { // 获取请求对象 HttpListenerRequest request = context.Request;
// 获取请求的 URL string url = request.Url.AbsoluteUri;
// 获取请求的 HTTP 方法(GET、POST 等) string httpMethod = request.HttpMethod;
// 获取请求头 WebHeaderCollection headers = request.Headers as WebHeaderCollection;
// 获取请求的 IP 地址 string ipAddress = context.Request.RemoteEndPoint?.Address?.ToString();
// 读取请求正文 string requestBody; using (System.IO.Stream body = request.InputStream) using (System.IO.StreamReader reader = new System.IO.StreamReader(body, request.ContentEncoding)) { requestBody = reader.ReadToEnd(); }
// 在这里处理请求正文 this.BeginInvoke(new Action(() => { // 显示 IP 地址 if (!ChartShow.Checked) { Recive.AppendText("New Message:rn");
Recive.AppendText($"IP Address: {ipAddress}rn");
// 显示 URL Recive.AppendText($"URL: {url}rn"); } if (MoreDetile.Checked&&!ChartShow.Checked) {
// 显示 HTTP 方法 Recive.AppendText($"HTTP Method: {httpMethod}rn");
// 显示请求头 Recive.AppendText($"Request Headers:n{string.Join("n", headers.AllKeys.Select(key => $"{key}: {headers[key]}"))}rn");
// 显示请求正文 Recive.AppendText($"Request Body:n{requestBody}rn"); } else { Dictionary<string, string> keyValuePairs = ParseJsonRequestBody(requestBody);
// 在这里处理请求正文 this.BeginInvoke(new Action(() => { // 打印键值对 foreach (var kvp in keyValuePairs) { if (double.TryParse(kvp.Value, out double doubleValue)) { if (ChartShow.Checked) { // 在 Chart 中检查是否包含对应键名的 Serial if (chart.Series.Any(series => series.Name == kvp.Key)) { // 如果已经包含,将数据添加到对应的 Serial 中 chart.Series[kvp.Key].Points.AddXY(chart.Series[kvp.Key].Points.Count+1, double.Parse(kvp.Value)); } else { // 如果不存在,新建对应键名的 Serial,并添加数据 Series newSeries = new Series(kvp.Key); Random random = new Random(); Color randomColor = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256));
// 新建 Series,并设置线条的属性
newSeries.Color = randomColor; // 设置颜色 newSeries.BorderWidth = 3; // 设置宽度 newSeries.ChartType = SeriesChartType.Spline; newSeries.Points.AddY(double.Parse(kvp.Value)); chart.Series.Add(newSeries); } } } Recive.AppendText($"{kvp.Key}: {kvp.Value}rn"); }
})); }
}));
// 构建响应内容 string responseString = Recall.Text; byte[] buffer = Encoding.UTF8.GetBytes(responseString);
// 发送响应 HttpListenerResponse response = context.Response; response.ContentLength64 = buffer.Length; System.IO.Stream output = response.OutputStream; output.Write(buffer, 0, buffer.Length); output.Close(); }
接着初始化一个监听实例,根据我们选择的IP地址以及端口号创建HTTP监听,并通过异步的方式处理接收到的数据,将数据打印或者从中解析出波形显示在外面的页面上。
** 效果展示 **
使用HTTP客户端工具来测试接收代码。
可以看到无论是接收数据,还是数据解析都是成功的,之后就是需要我们不停的完善代码。