xtaci / smux

A Stream Multiplexing Library for golang with least memory usage(TDMA)
MIT License
1.31k stars 196 forks source link

一个stream不读数据,另一端stream一直写,直到缓冲满。这个时候绑定在conn上的Session将不能执行任何指令。 #85

Open yddeng opened 3 years ago

yddeng commented 3 years ago
// recvLoop keeps on reading from underlying connection if tokens are available
 recvLoop() 
    for {
        for atomic.LoadInt32(&s.bucket) <= 0 && !s.IsClosed() {
            select {
            case <-s.bucketNotify:
            case <-s.die:
                return
            }
        }

由这段代码可知,当s.bucket小于0时,将不能从tcp缓冲拿数据。 session 将不能响应 对端的任何指令。包括关闭那个已经写超时的stream。 除非从stream中读数据,腾出缓冲空间

yddeng commented 3 years ago

如果设计将指令(cmdSYN,cmdFIN,cmdNOP)与数据接收指令(cmdPSH) 分开接收,可解决关闭那个已经写超时的stream,并回收缓存空间。

但是, 经测试,如果 s.bucket 满后,不再从tcp 中读数据,又会导致tcp的缓冲区满。要拿到关闭指令任然需要将之前的数据取出。

yddeng commented 3 years ago

一个待商榷的方案,用两层buffer。

write:从stream的写wbuffer中取最多 1024个字节。经session 编码发送到对端,对端 接收到完整包后将其投递到对应stream。若对端 stream buffer不够,响应buffer满,本端尝试重发。

read:直接从 stream 上读取数据。

什么时候尝试重发?假设在stream上设置一个标记,对端write步骤触发本端buffer满时,本端设置标记。本端read步骤时,通告对端可以继续发送了。

上面的 buffer 大小应可以自己调节,避免多次拆包。

fly3366 commented 1 year ago

lgtm, 为了整体的 SLO,并且保证其它 stream 的最佳性能,应该直接放弃慢的 stream frame,并 RST 对应 Stream。