Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

是不是对于ConnectionHandler,虽然底层是websocket,但是只能当成是流? #3

Open
heroboy opened this issue May 4, 2023 · 5 comments

Comments

@heroboy
Copy link

heroboy commented May 4, 2023

  1. 不区分websocket的string和buffer
  2. 不区分websocket的帧
public override async Task OnConnectedAsync(ConnectionContext connection)
{
    var input = connection.Transport.Input;
    try
    {
	    while (true)
	    {
		    var result = await input.ReadAsync(connection.ConnectionClosed);
		    if (result.IsCanceled) { break; }
		    var buffer = result.Buffer;
		    logger.LogInformation($"read buffer of length {buffer.Length}");
		    foreach (var mem in buffer)
		    {
			    await connection.Transport.Output.WriteAsync(mem);
		    }
		    input.AdvanceTo(buffer.End);
	    }
    }
    catch(Exception e)
    {
	    logger.LogInformation(e, "some exception");
    }
}

我试着用客户端发了1mb的message,服务器分了好多次收到。
无论发字符串还是二进制数据,收到的都是字符串。

@xljiulang
Copy link
Owner

如果你想让流量基于websocket传输,那不能直接在kestrel层使用YourConnectionHandler,而应该在asp.net层的websocket路由里使用YourConnectionHandler,此时会表现出以下现象:

1、ConnectionContext里面的ITransferFormatFeature.ActiveFormat默认为TransferFormat.Text,你可以根据实际是否修改为TransferFormat.Binary;
2、ConnectionContext的Transport对象,实际为websocket的负载数据的读取或写入,负载数据,当作流看待;

app.MapConnectionHandler<YourConnectionHandler>("/xxx_over_websocket", options =>
{
    options.WebSockets.SubProtocolSelector = ...
});

@xljiulang
Copy link
Owner

你可以把websocket帧理解为tcp帧,websocket帧里的payload理解为tcp帧的payload,多个websocket帧的payload串起来,就是websocket流,与tcp流是一个意思。

@heroboy
Copy link
Author

heroboy commented May 31, 2023

其实我不想说帧,你的文档里写了帧所以我用了帧。我想说的是packet或message(因为js的WebSocket是onmessage和message事件)。

我好奇的是ConnectionHandler既然没能暴露出WebSocket所有功能,难道是为了它的底层还能替换成tcp?但是替换成tcp,明显MapConnectionHandler(path)的path怎么办。

我也不能理解UseConnectionHandler<...>()的数据从哪里开始(既然是connection,input读的第一个字节为什么不能是从tcp那层开始的"GET /path ......",或是http body开始)。没有找到文档。

这种高级的功能都封装起来了,服务器容易写了,反正不用关心底层到底怎么传输的。但是,还是要写通信的另一端的啊。

@xljiulang
Copy link
Owner

首先我们了解网络数据包,其实是分层的,比如http over tls over tcp over ip(v4 or v6),就是我们常见的https,http over tcp over ip,是我们常见的http。在web框架里,其实我们也不关系 http over 啥,只关心如果解析http和响应http。

同理,kestrel的中使用中间件来处理这些分层,往往一层就是一个中间件,而不是一个中间件就干完所有层的活。ConnectionHandler是最后一个中间件,如果是处理http,则对应aps.netcore的WebApplcation概念。

假设我们自定义了 hxxp 协议,同时已经实现了HxxpConnectionHandler来处理这个协议,在使用不同的传输层来传输hxxp,会有不同的处理方式:

hxxp over tcp
这是最简单的方式,由于kestrel默认是基于tcp的,所以我们只需要在ListenOptions UseConnectionHandler<HxxpConnectionHandler>()

hxxp over websocket over tcp

  1. ListenOptions使用Websocket中间件
  2. ListenOptions使用HxxpConnectionHandler

关于Websocket中间件,我们可以自己实现,也可以直接使用asp.netcore层已实现的websocket

hxxp over websocket over tls over tcp

  1. ListenOptions使用Tls中间件
  2. ListenOptions使用Websocket中间件
  3. ListenOptions使用HxxpConnectionHandler

上些这些例子,可以很好的证明,使用分层机制,HxxpConnectionHandler的职责单一,可复用强。
回到ConnectionContext的Transport对象,其本质是一个双工的Stream,每过一层中间件,Transport对象会被替换。

如果我们使用Stream的思维来理解,也很方便:

var  tcpSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
var tcpStream = new  NetworkStream(tcpSocket); // tcp负载数据流
var tlsStream = new SslStream(tcpStream); // tls负载数据流
var websocketStream = new WebSocketStream(tlsStream); // websocket负载数据流

var hxxpBytes = new byte[1024];
websocketStream.ReadAsync(hxxpBytes );

@heroboy
Copy link
Author

heroboy commented May 31, 2023

写的很清楚,谢谢。

SignalR可以做,因为客户端的库提供了。
但是如果服务器端做了一个hxxp,做的时候完全不用关心over哪一个。但是写客户端的时候还是要知道over了哪一个。要么就是像aspnetcore那样在造一层ConnectionHandler那样的东西,然后再找个固定的地方查询一下当前over了哪一个,自动选择一下。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants