Matrix-Zhang / tokio_kcp

A Kcp implementation for tokio
MIT License
176 stars 44 forks source link

send队列长度超过128时只阻塞不报错 #38

Open TheCGDF opened 3 months ago

TheCGDF commented 3 months ago

在没有接收方的时候,一直发送数据包

    let server_addr = "127.0.0.1:5555".parse::<SocketAddr>().unwrap(); //没有程序监听这个端口
    let mut stream = KcpStream::connect(&config, server_addr).await.unwrap();
    let mut s = 1;
    loop {
        println!("sending{s}");
        stream.send(&buffer).await.unwrap();
        println!("sent{s}");
        s += 1;
        tokio::time::sleep(std::time::Duration::from_millis(1)).await;
    }

输出:

...
sending127
sent127
sending128
sent128
sending129

发送第129个的时候send会阻塞住,而不是返回error,而且即便sessiontimeout到了也不会解除阻塞。

同时也没有找到地方可以修改这个队列上限,或者查询当前队列长度。

这里不太清楚为啥用异步sendUdpSocketsend倒是同步的。

游戏服务器逻辑帧60帧的情况下,客户端网络卡顿两秒钟(2x60=120个包)服务端的发送队列就会被塞满并阻塞。


是否可以考虑提供一个同步的send_sync函数?队列满了就直接返回报错。

或者sendsession超时后返回一个报错?

再或者提供一个查询发送队列的长度的函数,允许调用者在send前先检查一下队列是否已满?

如果可以的话还希望可以考虑下在KcpConfig里提供这个队列长度上限的配置。

zonyitoo commented 3 months ago

这里不太清楚为啥用异步send,UdpSocket的send倒是同步的。

KCP的设计 send 就是异步的,真实IO只会在updateflush时才会产生。

发送第129个的时候send会阻塞住,而不是返回error,而且即便sessiontimeout到了也不会解除阻塞。

https://github.com/Matrix-Zhang/tokio_kcp/blob/db5b613fe2e3866c02e6e611363a7faa0ac85008/src/skcp.rs#L148-L167

就是因为发送队列满了所以发不出去了。行为是合理的,用TCP如果对面一直不收,本地的socket buffer满了也是一样卡住。

是否可以考虑提供一个同步的send_sync函数?队列满了就直接返回报错。

那应该叫 try_send ,如果满了报错 EWOULDBLOCK

或者sendsession超时后返回一个报错?

Client Stream 实际上根本不会超时,服务端的 Stream 才会有超时。Client 端的 Session 只会在 KcpStream 实例析构时才会触发关闭逻辑,会唤醒所有等待在 send 的 Task

如果可以的话还希望可以考虑下在KcpConfig里提供这个队列长度上限的配置。

可以试下 https://github.com/Matrix-Zhang/tokio_kcp/blob/db5b613fe2e3866c02e6e611363a7faa0ac85008/src/config.rs#L68-L69

TheCGDF commented 3 months ago

try_send也挺好的,不知实现起来难不难🥲

wnd_size试了不会影响队列的配置,我试了(256, 256)(25600, 25600),都是只能缓存128个

TheCGDF commented 3 months ago

Client Stream 实际上根本不会超时

这个之前确实不知道😢,因为用的另一个c#的kcp库的客户端倒是会超时,超时了send会报错🤯,导致我以为rust这边差不多

我以为超时了tokio_kcpClient Stream也会自动关闭套接字。。。

zonyitoo commented 3 months ago

你可以加个超时,用 tokio::time::timeout 包在 send 调用上,这样就会超时了

TheCGDF commented 3 months ago

真实IO只会在update和flush时才会产生

我可能哪里没理解对,我知道只有updateflush时才会IO,所以我的理解send不是可以立即返回吗?send又没有等待updateflush

zonyitoo commented 3 months ago

KCP的send 会不做任何限制地往snd_queue塞数据,最终会塞爆。因此在本项目如果已经积压超过上限,会主动等待。

TheCGDF commented 3 months ago

Client 端的 Session 只会在 KcpStream 实例析构时才会触发关闭逻辑

这个是不是会导致内存泄漏?

服务端每秒钟向客户端发送60个逻辑帧,每次客户端离线退出时,服务端的send队列会残留128个包。

而服务端的KcpStream是同一个一直运行着,不会产生析构,那是不是意味着这send队列里的包永远不会被释放?

我现在服务端这边内存泄漏非常严重,感觉会不会可能和这个有关系。。。。🤯🤯🤯

当我没说,一直运行着的是KcpSocketKcpStream是会析构的,我再找找自己哪里搞出内存泄漏了。。。