我目前的目标是将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做任何事情。我想要自己的协议。