Closed raintean closed 4 years ago
这个应该没有, 我记得他是基于google的netstack实现的. 不过我更看好作者的这个实现, 轻量, 内存可控性好.
如果暂时没法处理数据,比如连接的发送缓存满了,可以给 lwip 返回一个错误 https://github.com/eycorsican/go-tun2socks/blob/eebb391cf216724da69c9594f9ed46fbce958661/core/tcp_callback_export.go#L96
但 go 现在的连接是同步写,所以还要在 handler 里实现多一级缓存,考虑实际上连接的写操作很少会有阻塞,我觉得没什么必要。
在使用过程中确实会出现这样的情况, 比如上传走的是代理路线, 如果代理服务器不稳定. 那么按这个情况来说, 是会阻塞直连, 或者其他代理路线的上传(包括下载). 而且在网络环境较差的情况下(这个项目用来做啥,大家众所周知,所以网络环境....)特别明显.
目前我的解决方案是:
type tcpConn struct {
sync.Mutex
pcb *C.struct_tcp_pcb
handler TCPConnHandler
remoteAddr *net.TCPAddr
localAddr *net.TCPAddr
connKeyArg unsafe.Pointer
connKey uint32
canWrite *sync.Cond // Condition variable to implement TCP backpressure.
state tcpConnState
sndPipeReader *io.PipeReader //换成带缓存的实现
sndPipeWriter *io.PipeWriter //换成带缓存的实现
closeOnce sync.Once
closeErr error
}
将pipe出来的 sndPipeReader
, sndPipeWriter
更换成带缓存的实现.
其次将
func (conn *tcpConn) Receive(data []byte) error {
if err := conn.receiveCheck(); err != nil {
return err
}
n, err := conn.sndPipeWriter.Write(data)
if err != nil {
return NewLWIPError(LWIP_ERR_CLSD)
}
C.tcp_recved(conn.pcb, C.u16_t(n)) //删除这行
return NewLWIPError(LWIP_ERR_OK)
}
中的 C.tcp_recved(conn.pcb, C.u16_t(n))
删掉. 因为就算无阻塞写入了缓存pipe中, 也并不意味着数据已经接收. 如果这个时候tcp_recved 那么lwip还是会疯狂发数据, 造成缓存疯狂增长(在来不及Read的情况下).
最后在Read方法中:
func (conn *tcpConn) Read(data []byte) (int, error) {
// xxxxxxxx
lwipMutex.Lock()
C.tcp_recved(conn.pcb, C.u16_t(n)) //在真正read的时候,告诉lwip,数据已经接收
lwipMutex.Unlock()
return n, err
}
调用 tcp_recved
, 表示数据已经进入到转发那边去了, 让转发的背压, 能够很好的传递给 lwip 层.
个人认为, 这就是一个因为cgo的实现, 造成的阻塞世界和非阻塞世界思想不匹配的问题.
另外我觉得在应用的层面去实现多一级缓存, 并不清真, 让本身库的职责外泄了. 不优雅.
把缓存放在 core 里也是可以的,但缓存不可能设到无限大,满了后依然要返回错误给 lwip
把缓存放在 core 里也是可以的,但缓存不可能设到无限大,满了后依然要返回错误给 lwip
这不会有缓存扩大的问题. 写入时只是为了不阻塞, 放入缓存, 如果不去调用tcp_recved来更新可用的发送窗口, 实际上, lwip不会再次传入数据了. 除非你在read的时候调用tcp_recved让窗口更新. 所以不需要判断缓存满不满的情况, 因为缓存最大也就是lwip的发送窗口大小. 这边缓存到达最大. 那边就是0了. lwip不会再发数据过来了.
那你实现一下?
那你实现一下?
我实现了, 但是有点问题. 偶尔会出现lwip(tcp.c)中的new_rcv_ann_wnd
错误
#if !LWIP_WND_SCALE
LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
#endif
那你实现一下?
我实现了, 但是有点问题. 偶尔会出现lwip(tcp.c)中的
new_rcv_ann_wnd
错误#if !LWIP_WND_SCALE LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); #endif
能把改过的代码库推上来吗
那你实现一下?
我实现了, 但是有点问题. 偶尔会出现lwip(tcp.c)中的
new_rcv_ann_wnd
错误#if !LWIP_WND_SCALE LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); #endif
能把改过的代码库推上来吗
@eycorsican @wongsyrone 代码已经上来
有个疑问,如果目前代码只把tcp_recved挪到Read里面能不能解决问题呢
有个疑问,如果目前代码只把tcp_recved挪到Read里面能不能解决问题呢
根据lwip的文档中tcp_recved的说明, 以及tcp协议关于发送窗口的规定. 是没有问题.
有个疑问,如果目前代码只把tcp_recved挪到Read里面能不能解决问题呢
根据lwip的文档中tcp_recved的说明, 以及tcp协议关于发送窗口的规定. 是没有问题.
不过目前repo中使用的lwip配置,貌似窗口都是固定大小
这里只是和wnd的scale无关, 影响的是可用的wnd
@raintean @eycorsican 用core的时候遇到这个问题了,Handle过来的net.Conn似乎只能等读写完成才能下一个,这样下游调用的话做不了缓存吧
@raintean @eycorsican 用core的时候遇到这个问题了,Handle过来的net.Conn似乎只能等读写完成才能下一个,这样下游调用的话做不了缓存吧
没太明白你的意思, 可否讲的详细一点
@raintean @eycorsican 用core的时候遇到这个问题了,Handle过来的net.Conn似乎只能等读写完成才能下一个,这样下游调用的话做不了缓存吧
没太明白你的意思, 可否讲的详细一点
情况是这样的,我这里使用go-tun2socks/core的时候,实现了一个core.TCPConnnHandler,里面的Handler会把TAP收到的net.Conn传过来,但是这个时候我还需要去Dial远端服务器才能relay,设置了5s的超时,这个时候这个要是一个请求阻塞了,后面的就都得等5s,不知道有没有办法解决
@Fndroid 你这个问题和该issue没关系, 不过我可以回答一下你. 其实作者在代码里面也写了. 不要让handle被阻塞, 那么问题很明显了. 你handle方法里面 直接开一个协程去处理过来的net.Conn, 让handle能够快速返回 不就OK了嘛.
@raintean 感谢回复。我刚认真测试了一下,我发现的现象是Handle传过来的Conn在Read(copyBuffer)的时候要等很长一段时间,请问这个是什么原因造成的呢?也试过拿到Conn的时候直接Read,但是也需要很长的时间才能EOF
@raintean 感谢回复。我刚认真测试了一下,我发现的现象是Handle传过来的Conn在Read(copyBuffer)的时候要等很长一段时间,请问这个是什么原因造成的呢?也试过拿到Conn的时候直接Read,但是也需要很长的时间才能EOF
可能就是没有数据能read呢? 对不对...
@raintean 最后问一下大佬,这个duplexConn的意义是什么呢?Relay的时候Dst Conn是不是也需要实现这个接口呢,谢谢
@Fndroid 具体不清楚, 字面上的意思是全双工连接. 看接口你能明白, 他是可以单独关闭读取或者写入的, net.Conn的话, 好像是close就全部关闭了. 这个在reply的时候有意义. 比如 A <-> B 直接做pipe, A已经EOF了, 这个时候其实是可以关闭B的写入的. 大概就是这么一个意思. 你不太需要关心这个. 直接net.Conn也能满足了. 这个只是对上下行都做了分开控制.
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days
目前发现一个问题: 在有TCP数据从tun方向过来的时候. 会进到Receive方法
然后再往pipe中写入, 等待对端Read, 然后使用自己的逻辑(直连/Socks等)发送出去.
但是这里存在一个问题. 如果没法及时调用Read方法(比如转发速度太慢等), 会造成Receive方法长时间阻塞在
conn.sndPipeWriter.Write(data)
. 由于lwip是单线程, 所以其对于Receive的调用迟迟无法返回. 造成其他的网络流量, 全部阻塞.目前作者有没有什么好的方案来解决该问题.
个人认为这就是一个流的背压传递问题.