ASP.Net Core HttpUpgradeFeature创建不可用的流

时间:2018-08-10 14:52:35

标签: c# asp.net-core websocket kestrel

我目前的目标是将HTTP连接升级为简单的双向流,就像普通的TCP连接一样,但使用ASP.Net Core。我遵循了Microsoft如何实现WebSockets的方法,但是由于某种原因该流不允许简单的读/写操作,所以我目前仍处于困境。自两天以来,我一直在尝试调试此问题,今天我决定只想创建一个简单的聊天应用程序,以隔离通过流发送字符串的问题。由于某些原因,我遇到了不同的结果,但是它始终无法正常工作:

  • 流以一种方式工作(服务器->客户端),但是来自客户端的数据从未到达服务器
  • 服务器刚刚向客户端发送了零
  • 引发异常,连接已关闭(这是我的聊天应用程序中的问题)

Here,您可以找到Kestrel的UpgradeAsync()的实现。实际上,这看起来很直接而且很容易,但是由于某些原因,ASP.Net Core服务器还设置了诸如Sec-WebSocket-Accept之类的响应标头,即使我没有指定我正在执行websockets。一开始这也是一个问题,因为如果HTTP请求标头不包含Upgrade: websocket(它必须是websocket,也不允许使用其他名称),则该请求不可升级(而IMO则不能升级)有道理,为什么连接仍不能升级?Microsoft是否只想支持websocket?)。您可以找到my test project on GitHub

要进行复制,您需要两个项目: Asp.Net Core Api 2.1(服务器)

using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;

namespace Asp.UpgradeTest
{
    public class TestMiddleware
    {
        private readonly RequestDelegate _next;

        public TestMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var upgradeFeature = context.Features.Get<IHttpUpgradeFeature>();
            if (upgradeFeature?.IsUpgradableRequest == true)
            {
                var stream = await upgradeFeature.UpgradeAsync();

                var buffer = Encoding.UTF8.GetBytes("Hello World");
                await stream.WriteAsync(buffer, 0, buffer.Length);

                await Task.Delay(100000); //just dont end the request
            }

            await _next(context);
        }
    }
}

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.Map("/ws", builder => builder.UseMiddleware<TestMiddleware>());
    }
}

.Net Core 2.1控制台(客户端)

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Asp.UpgradeTest.Client
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var port = 62323;

            var tcpClient = new TcpClient();
            await tcpClient.ConnectAsync("localhost", port);
            var stream = tcpClient.GetStream();


            var data = $@"GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: localhost:{port}
User-Agent: AspSocketTest/1.0
Sec-WebSocket-Key: PG178ygiZKSkeXGGZ6P9KQ==
Sec-WebSocket-Version: 13

";

            var buffer = Encoding.UTF8.GetBytes(data);
            await stream.WriteAsync(buffer, 0, buffer.Length);

            using (var streamReader = new StreamReader(stream, Encoding.UTF8, false, 8192, true))
            {
                var statusLine = await streamReader.ReadLineAsync();
                Console.WriteLine(statusLine); //check if Switching Protocol

                var headers = new Dictionary<string, string>();

                string line;
                while (!string.IsNullOrEmpty(line = await streamReader.ReadLineAsync()))
                {
                    var split = line.Split(new[] { ": " }, 2, StringSplitOptions.None);
                    headers.Add(split[0], split[1]);

                    Console.WriteLine(line);
                }
            }

            buffer = new byte[11];
            await stream.ReadAsync(buffer, 0, 11);

            Console.WriteLine(Encoding.UTF8.GetString(buffer));

            Console.ReadLine();
        }
    }
}

我只是不明白为什么它不起作用以及为什么它会引发此异常:

System.IO.IOException: 'Unable to read data from the transport connection: Eine vorhandene Verbindung wurde vom Remotehost geschlossen.'

我刚刚发现的事情:如果删除Sec-WebSocket-*标头,则不会引发该异常,但是它仍然不起作用。对我来说,就像黑魔法一样,为什么websocket在这里甚至很重要?我只想升级此连接,我不想使用websockets做任何事情。我想要自己的协议。

0 个答案:

没有答案