V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
wdmx007

[GO] net 包读取数据问题,如何触发 Conn 的可读事件

  •  
  •   wdmx007 · Oct 29, 2019 · 5081 views
    This topic created in 2375 days ago, the information mentioned may be changed or developed.

    刚学 golang ,最近在写一个代理服务器.使用了自带的 net 库来需要实现数据的中转。
    服务器有这样一个逻辑:当收到目标服务器的数据时,需要转发给客户端。
    但是查了一下资料,net.Conn.Read 方法可以读取来自目标服务器的数据,但是每次都需要手动调用, 我也不想写死循环或者 time.Sleep 之类的轮询。

    自带的 net 库虽然底层是多路复用的封装, 但是没有暴露像 netty 一样的事件回调,也没有 java NIO 的可读事件通知,请问该怎么处理这种情况呢?需要依赖第三方包或者自己底层重新写一套? 求各位大佬指教。 [哭]

    18 replies    2019-10-29 20:24:52 +08:00
    icexin
        1
    icexin  
       Oct 29, 2019   ❤️ 1
    Read 是阻塞调用,没有数据不会返回的。
    wdmx007
        2
    wdmx007  
    OP
       Oct 29, 2019
    @icexin 对啊,但是我需要在有可读数据的第一时间转发到客户端,轮询调用 Read 是可以解决的,但是太难看了。请问有其他办法吗?
    misaka19000
        3
    misaka19000  
       Oct 29, 2019
    自己写个回调不就行了
    1314258
        4
    1314258  
       Oct 29, 2019 via iPhone
    @wdmx007 就用 go 轮询
    icexin
        5
    icexin  
       Oct 29, 2019   ❤️ 1
    @wdmx007 有数据 Read 就立马返回,你再转发,没有数据就阻塞等待,有什么问题吗?
    wdmx007
        6
    wdmx007  
    OP
       Oct 29, 2019
    @misaka19000 请问在哪里注册回调呢? 大概找了一下,不知道找哪里设置。net.Conn 接口没看到回调注册啊。
    wdmx007
        7
    wdmx007  
    OP
       Oct 29, 2019
    @icexin 谢谢,刚学 go,对这个不太熟。如果没有数据调用 Read 会阻塞等待的话,直接找协程里面写死循环就可以了。我去试试。
    misaka19000
        8
    misaka19000  
       Oct 29, 2019   ❤️ 1
    每个连接开一个协程,之后

    callback = function (data) {

    }

    for {
    data = net.Read()
    callback(data)
    }

    搞定,这就是回调
    ScepterZ
        9
    ScepterZ  
       Oct 29, 2019
    按理说阻塞不是比回调好理解多了么……这就是 go 的卖点啊
    wdmx007
        10
    wdmx007  
    OP
       Oct 29, 2019 via Android
    @ScepterZ 主要是我理解错了,以为没数据的时候 read 会直接返回,所以我就以为需要不停的调用查看是否有数据。实际上如果是阻塞到有数据来了才返回的话,就很好理解了。
    reus
        11
    reus  
       Oct 29, 2019   ❤️ 1
    可以实现啊,自己调 epoll 就行

    不过看你回复,原来不知道 Read 是阻塞调用,那可以认为你不懂基本的网络读写了,更不用说 epoll

    能力不够的时候,“不想写”,“太难看“这种话,是没有资格说的
    whoami9894
        12
    whoami9894  
       Oct 29, 2019 via Android
    syscall.SetNonblock 然后 select 轮询?不知道 go 的范式是不是这样做
    wdmx007
        13
    wdmx007  
    OP
       Oct 29, 2019 via Android
    @reus 谢谢回复。因为没认真研究过 socket,只写过 java nio,epoll 还是知道的,不然我也不会说什么可读事件通知这种话了😂
    zunceng
        14
    zunceng  
       Oct 29, 2019   ❤️ 1
    严重怀疑楼上几位是不是写过 Golang

    golang 里面的 tcp proxy 基本上就是 go 两个函数 各自把一个 socket 读通道的数据写到另一个 scoket 的写通道上

    参考
    https://github.com/kahlys/proxy/blob/master/proxy.go#L74
    https://github.com/google/tcpproxy/blob/master/tcpproxy.go#L353
    wdmx007
        15
    wdmx007  
    OP
       Oct 29, 2019 via Android
    @zunceng 我需要把目标服务器数据进行加密后转发的。不过本质的思路确实如你所说。
    zunceng
        16
    zunceng  
       Oct 29, 2019   ❤️ 1
    @wdmx007 你先实现一个加解密的 pipe ( reader + writer )

    把 proxy 上在 io.Copy 替换成这个 pipe 就可以了

    func proxy ( src. dst net.Conn ) error {
    errCh := make(chan error, 1)

    go func () {
    errCh<- io.Copy(src, dst) // TODO: replace with your pipe
    }

    go func () {
    errCh<- io.Copy(dst, src) // TODO: replace with your pipe
    }

    return <-errCh
    }
    zunceng
        17
    zunceng  
       Oct 29, 2019
    抱歉 语法错+错误处理有问题 意思到了吧
    useben
        18
    useben  
       Oct 29, 2019
    人家就是把异步封装成同步的写法给你,你还要去找异步的。。。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2453 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 57ms · UTC 06:02 · PVG 14:02 · LAX 23:02 · JFK 02:02
    ♥ Do have faith in what you're doing.