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

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

Open
TheCGDF opened this issue Jul 15, 2024 · 7 comments
Open

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

TheCGDF opened this issue Jul 15, 2024 · 7 comments

Comments

@TheCGDF
Copy link

TheCGDF commented Jul 15, 2024

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

    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
Copy link
Collaborator

zonyitoo commented Jul 16, 2024

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

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

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

tokio_kcp/src/skcp.rs

Lines 148 to 167 in db5b613

if self.sent_first
&& (self.kcp.wait_snd() >= self.kcp.snd_wnd() as usize
|| self.kcp.wait_snd() >= self.kcp.rmt_wnd() as usize
|| self.kcp.waiting_conv())
{
trace!(
"[SEND] waitsnd={} sndwnd={} rmtwnd={} excceeded or waiting conv={}",
self.kcp.wait_snd(),
self.kcp.snd_wnd(),
self.kcp.rmt_wnd(),
self.kcp.waiting_conv()
);
if let Some(waker) = self.pending_sender.replace(cx.waker().clone()) {
if !cx.waker().will_wake(&waker) {
waker.wake();
}
}
return Poll::Pending;
}

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

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

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

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

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

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

可以试下

tokio_kcp/src/config.rs

Lines 68 to 69 in db5b613

/// Send window size
pub wnd_size: (u16, u16),

@TheCGDF
Copy link
Author

TheCGDF commented Jul 16, 2024

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

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

@TheCGDF
Copy link
Author

TheCGDF commented Jul 16, 2024

Client Stream 实际上根本不会超时

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

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

@zonyitoo
Copy link
Collaborator

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

@TheCGDF
Copy link
Author

TheCGDF commented Jul 16, 2024

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

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

@zonyitoo
Copy link
Collaborator

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

@TheCGDF
Copy link
Author

TheCGDF commented Jul 16, 2024

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

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

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

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

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

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

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