websocket 的心跳请求 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
mentalidade
V2EX    程序员

websocket 的心跳请求

  •  
  •   mentalidade 2017-12-12 16:58:31 +08:00 12651 次点击
    这是一个创建于 2861 天前的主题,其中的信息可能已经有所发展或是发生改变。
    func writer(ws *websocket.Conn) { pingTicker := time.NewTicker(time.Second * 1) fileTicker := time.NewTicker(time.Second * 1) defer func() { pingTicker.Stop() fileTicker.Stop() ws.Close() }() for { select { case <-fileTicker.C: ws.SetWriteDeadline(time.Now().Add(time.Second * 2)) fmt.Println("------>hello") if err := ws.WriteMessage(websocket.TextMessage, []byte("hello")); err != nil { fmt.Println("send msg err:", err) return } case <-pingTicker.C: ws.SetWriteDeadline(time.Now().Add(time.Second * 2)) fmt.Println("---->send ping") if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { fmt.Println("send ping err:", err) return } } } } func reader(ws *websocket.Conn) { ws.SetReadLimit(512) ws.SetReadDeadline(time.Now().Add(time.Second * 2)) ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(time.Second * 2)); return nil }) defer ws.Close() for { t, msg, err := ws.ReadMessage() fmt.Println("server recieve t:", t) fmt.Println("server recieve body:", string(msg)) if err != nil { if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { fmt.Println("error:", err) } return } time.Sleep(time.Second *5) } } 

    在 main 函数中,writer 函数必须加关键字 go,开启两个定时器,一个定时向客户端推送消息,一个定时 ping 客户端检测是否连接正常。如果断开显示send ping err: write tcp 127.0.0.1:3000->127.0.0.1:62228: write: broken pipe。但是感觉这个 ping 的定时器没啥用啊,如果客户端断开,WriteMessage 这个方法就会报错退出,那这个 ping 效果和它一样的,请问还有必要吗?

    go writer(conn) go reader(conn) 
    20 条回复    2017-12-13 13:42:09 +08:00
    Chemist
        1
    Chemist  
       2017-12-12 17:08:00 +08:00
    老哥, 你把 WebSocket 玩出了 HTTP 的感觉.

    WebSocket 本来就是长连接, 干嘛要发心跳包?
    如果连接断开都会触发客户端和服务端的 onClose 事件, 你处理好这个事件不就行了吗?
    wtbhk
        2
    wtbhk  
       2017-12-12 17:16:24 +08:00
    @kyuuseiryuu 心跳还是要发的,说不定中间链路断了
    morethansean
        3
    morethansean  
       2017-12-12 17:18:38 +08:00
    @kyuuseiryuu 建议补一下网络知识 23333
    suikator
        4
    suikator  
       2017-12-12 17:19:11 +08:00 via Android
    @kyuuseiryuu 为了维持 NAT 还是需要的吧
    lianyue
        5
    lianyue  
       2017-12-12 17:37:52 +08:00
    WebSocket 自带心跳包啊
    mentalidade
        6
    mentalidade  
    OP
       2017-12-12 17:42:55 +08:00
    @lianyue 求教,我找了好久,这是抄的官网 demo 的
    que01
        7
    que01  
       2017-12-12 17:47:30 +08:00
    最近在学 go 这段代码我居然能看懂了 哈哈
    mentalidade
        8
    mentalidade  
    OP
       2017-12-12 17:48:38 +08:00
    @que01 ,因为我也是新学 go
    bhagavad
        9
    bhagavad  
       2017-12-12 17:50:38 +08:00   2
    @suikator 说的正解,一方面为了 NAT 需要,另一方面是业务需求。

    在中间网络断掉后( client 网络断了、链路上的某个路由器断了) TCP 链接两端是不会收到任何通知的,所以这个时候是根本不会触发 @kyuuseiryuu 所说的 onClose 事件的。这个时候就需要依靠心跳去监听,如果发现对方掉线后就触发类似 onClose 的事件(不管是 client 还是 server ),所以业务的心跳是必须的(当然也可以通过自定义 tcp 的 keepalive 来实现)。
    kaneg
        10
    kaneg  
       2017-12-12 18:39:18 +08:00 via iPhone   1
    1. 维持链路连接,同楼上各位的观点
    2. 及时感知连接断开。如果对端端由某种原因断开连接而没有被网络层感知到,心跳包能及时感知到这种情况,从而来后续清理或者重连。
    Chemist
        11
    Chemist  
       2017-12-12 18:43:02 +08:00
    @morethansean #3 求教.
    Kilerd
        12
    Kilerd  
       2017-12-12 19:16:16 +08:00
    1、ping pong 是必须的,如果你传输的消息量少,而且想保持长连接
    2、websocket 不自带心跳, 你可能是 socket.io 的受害者
    jswh
        13
    jswh  
       2017-12-12 19:21:35 +08:00
    如果是我写,我就让客户端来 ping server,一般心跳维持都是客户端报告我还活着吧。这样你就记录一个上次 ping 时间,超时断开就好了。
    mentalidade
        14
    mentalidade  
    OP
       2017-12-12 20:13:37 +08:00
    @jswh 那和我的疑惑差不多,因为我是服务端定时推送,如果客户端断开,那么消息直接推送失败而断开。不过官网给的几个例子是使用 ping 的。自己也有 deadline, https://github.com/gorilla/websocket/issues/52
    pathbox
        15
    pathbox  
       2017-12-12 20:58:37 +08:00 via iPhone
    go 原包不带心跳,gorilla 的 websocket 有心跳方法。你自己实现一个也是可以的。 一般建议 client 发心跳 别让服务端发。
    TangMonk
        16
    TangMonk  
       2017-12-12 21:14:58 +08:00 via Android
    @Kilerd websocket 的 pingpong 就是用来实现 heartbeat 的
    Kilerd
        17
    Kilerd  
       2017-12-12 21:31:43 +08:00
    @jswh 客户端发 ping 不是通用做法吗?

    client: hey, 我还活着,别挂电话。
    server: 嗯,好的
    client: hey,我还活着
    server: 好的
    client: hey....
    server:噢
    client: emmmmm... 是我
    server: emmmmmmmmmmm
    client: 还是我
    server: ..................


    .........

    server: 那货终于走了。 (啪,挂电话(on_close))
    gamexg
        18
    gamexg  
       2017-12-12 23:25:11 +08:00 via Android
    @Kilerd 我上次为了收集客户网络延迟做成服务端主动发包来统计 pong 回应耗时,不过的确是客户端主动发 ping 简单。
    jswh
        19
    jswh  
       2017-12-13 10:41:49 +08:00
    @Kilerd 我的意思就是客户端发 ping 呀。楼主的代码是服务端在 ping
    mrnull
        20
    mrnull  
       2017-12-13 13:42:09 +08:00
    2 ping, 3 pong, 4 msg,看看各种 engine.io 的实现
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     897 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 19:04 PVG 03:04 LAX 12:04 JFK 15:04
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86