-
Notifications
You must be signed in to change notification settings - Fork 46
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
空data支持 #37
Comments
|
Lines 138 to 185 in bf3466b
从逻辑上看没有不允许发送 |
没有开启 我用wireshark抓包试了,当send buffer为空时, 当用其他kcp库向 |
我看到作为依赖的kcp库中有 |
这段代码仅在 |
真没开。 let config =KcpConfig {
mtu: 1400,
nodelay: KcpNoDelayConfig {
nodelay: false,
interval: 40,
resend: 0,
nc: false,
},
wnd_size: (256, 256),
session_expire: std::time::Duration::from_secs(90),
flush_write: true,
flush_acks_input: true,
stream: false,
};
|
https://github.com/Matrix-Zhang/kcp/blob/58e863fcbdf4cdd0df9e9c378a864dd4d0c8f58f/src/kcp.rs#L1246-L1249 |
至少会往 |
打断点看了,空数组的时候走不到那段代码, |
翻了tokio源码终于找到了 数组为空时不会调用 那我估计这是难修了,除非多一层封装先把 segment 放进去再调用 但是 |
read() 返回 0 是肯定不可以的,返回 0 表示的是 EOF ,不能去破坏这个约定 。要么就去另外搞一个函数去读,不用 AsyncRead trait |
那目前的实现如果不打算改的话还是在文档或者example里标记一下比较好。。。 比如 |
不需要的,因为Rust所有的API都是类POSIX标准,read() = 0 就是 EOF ,不需要特意说明 |
解决办法是加个option,如果设置了的话,write 允许写空包,那不要用 write_all 就好了,用 Lines 165 to 171 in bf3466b
|
不过你的业务层代码也直接用 |
还是有个比较奇怪的点,tokio的 let u = UdpSocket::bind("127.0.0.1:6666".parse::<SocketAddr>().unwrap()).await.unwrap();
u.connect("127.0.0.1:5555").await.unwrap();
u.send(&[0u8;0]).await.unwrap(); 也就是说socket可能应该用send/recv,而且发空包本身是一个被支持的操作。 于是我想着tokio_tcp是不是也可以用 网络库的API应该用 |
KCP协议是一种流协议,因此与它最相似的就是 底层用 对于这里提到的问题,是一种基于 KCP 实现的一种特例,它在 |
试了下 let mut t = TcpStream::connect("127.0.0.1:6666").await.unwrap();
t.write_all(&[0u8;0]).await.unwrap();
从而服务端
我也觉得 |
|
学到了。🙏 |
当前就有 Lines 84 to 87 in bf3466b
和 Lines 140 to 143 in bf3466b
|
上面我说过了。。。tokio_tcp的send无论数组是否为空,都不会发出任何包。 |
你是说 |
重新测了下,之前测错了,应该是 let config = KcpConfig::default();
let server_addr = "127.0.0.1:5555".parse::<SocketAddr>().unwrap();
let mut stream = KcpStream::connect(&config, server_addr).await.unwrap();
stream.send(&[0u8; 10]).await.unwrap(); 这里的 不知是我代码哪里写错了还是什么问题? |
在代码中加了一个这样的 test ,没有问题 #[cfg(test)]
mod test {
use crate::KcpListener;
use super::*;
#[tokio::test]
async fn test_stream_echo() {
let config = KcpConfig::default();
let server_addr = "127.0.0.1:5555".parse::<SocketAddr>().unwrap();
let mut listener = KcpListener::bind(config.clone(), server_addr).await.unwrap();
let listener_hdl = tokio::spawn(async move {
loop {
let (mut stream, peer_addr) = listener.accept().await.unwrap();
println!("accepted {}", peer_addr);
tokio::spawn(async move {
let mut buffer = [0u8; 8192];
loop {
match stream.recv(&mut buffer).await {
Ok(n) => {
println!("server recv: {:?}", &buffer[..n]);
stream.send(&buffer[..n]).await.unwrap();
println!("server sent: {:?}", &buffer[..n]);
}
Err(err) => {
println!("recv error: {}", err);
break;
}
}
}
});
}
});
let mut stream = KcpStream::connect(&config, server_addr).await.unwrap();
let test_payload = b"HELLO WORLD";
stream.send(test_payload).await.unwrap();
println!("client sent: {:?}", test_payload);
let mut recv_buffer = [0u8; 1024];
let recv_n = stream.recv(&mut recv_buffer).await.unwrap();
println!("client recv: {:?}", &recv_buffer[..recv_n]);
assert_eq!(recv_n, test_payload.len());
assert_eq!(&recv_buffer[..recv_n], test_payload);
listener_hdl.abort();
}
} 其中会输出
应该是你测试的问题,你直接这么测, |
Line 207 in bf3466b
|
确实是我测试方法不对,加了 tokio::time::sleep(std::time::Duration::from_secs(1)).await; 就能发出去了 这样看来就只剩 |
不过用发一个空的来表示一种特殊的功能,似乎并不是一种稳定靠谱的做法。但凡后面多发一个字节的数据过去,读出来就不是空的 |
但毕竟kcp协议没有禁止。。。 还有个,当 有次随机生成的 |
本身也没规定conv要怎么生成 |
主要是有这样的逻辑是不是在文档或者注释里标一下比较好。。。不然出了bug还挺难复现的。。。 |
|
判断不了。本身读到size=0表示的就是EOF,session超时本身这里的设计是想表示为关闭,关闭就向上层返回EOF表示结束。你这里一定要读到一个size=0的数据,很难做。 |
超时无法返回一个Timeout的Error吗?🥲 |
可以实现,更好的办法是不是应该让你设置成不超时,由你自己来处理 |
你是指像这样↓,自己设置一个 let mut last_received = Utc::now();
tokio::select! {
_ = async {
//最多超时两秒,否则发送队列会被塞爆,send会被阻塞
let ms = (last_received + Duration::seconds(2) - Utc::now()).num_milliseconds();
tokio::time::sleep(
if ms <= 0 {
std::time::Duration::ZERO
} else {
std::time::Duration::from_millis(ms as u64)
}
).await;
} =>{
//session timeout
break;
}
result = stream.recv(&mut buffer) => {
last_received = Utc::now();
//handle buffer
}
} 我个人感觉如果能提供个超时的 在已有 |
不用那么复杂,直接创建一个 实际上可能更好的是: loop {
let n = match tokio::time::timeout(stream.recv(&mut buffer)) {
Ok(Ok(n)) => n,
Ok(Err(err)) => ... // socket error,
Err(..) => ... // timedout
};
} 这样写不是挺优雅的,没必要 select 。
研究了一下写起来可能会有点丑陋, |
我之前没说清楚,用 主世界每15ms更新一次逻辑帧然后通过 (这种模式应该是比较普遍的,我看一些其他issue里的代码也采用了这种模式,比如:#33 let mut last_received = Utc::now();
loop{
tokio::select! {
//接收主世界channel每隔15ms发来的逻辑帧
recieved = world_receiver.recv() => {
//将逻辑帧序列化为二进制
if tokio::time::timeout(std::time::Duration::from_secs(2), stream.send(&response)).await.is_err(){
//逻辑帧的send只能超时两秒
break;
}
}
_ = async {
//recv最多等待两秒,否则发送队列会被塞爆,send会被阻塞
let ms = (last_received + Duration::seconds(2) - Utc::now()).num_milliseconds();
tokio::time::sleep(
if ms <= 0 {
std::time::Duration::ZERO
} else {
std::time::Duration::from_millis(ms as u64)
}
).await;
} =>{
//session timeout
break;
}
//这里如果使用tokio::time::timeout会永远触发不了timeout
//因为会被主世界的逻辑帧抢先
result = stream.recv(&mut buffer) => {
last_received = Utc::now();
//handle buffer
}
}
} 一共需要手动处理两个超时
在这种情况下,给
|
当客户端发送一个空的数据包时:
服务端的
read
会卡住,或者说,忽略这个空数据包这导致一些兼容性问题。
以及某些kcp库的client在connect时会发送一个空数据包作为握手。
是否有办法接受空数据包?
The text was updated successfully, but these errors were encountered: