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)
![]() | 1 Chemist 2017-12-12 17:08:00 +08:00 老哥, 你把 WebSocket 玩出了 HTTP 的感觉. WebSocket 本来就是长连接, 干嘛要发心跳包? 如果连接断开都会触发客户端和服务端的 onClose 事件, 你处理好这个事件不就行了吗? |
![]() | 2 wtbhk 2017-12-12 17:16:24 +08:00 @kyuuseiryuu 心跳还是要发的,说不定中间链路断了 |
3 morethansean 2017-12-12 17:18:38 +08:00 @kyuuseiryuu 建议补一下网络知识 23333 |
![]() | 4 suikator 2017-12-12 17:19:11 +08:00 via Android @kyuuseiryuu 为了维持 NAT 还是需要的吧 |
![]() | 5 lianyue 2017-12-12 17:37:52 +08:00 WebSocket 自带心跳包啊 |
![]() | 6 mentalidade OP @lianyue 求教,我找了好久,这是抄的官网 demo 的 |
7 que01 2017-12-12 17:47:30 +08:00 最近在学 go 这段代码我居然能看懂了 哈哈 |
![]() | 8 mentalidade OP @que01 ,因为我也是新学 go |
![]() | 9 bhagavad 2017-12-12 17:50:38 +08:00 ![]() @suikator 说的正解,一方面为了 NAT 需要,另一方面是业务需求。 在中间网络断掉后( client 网络断了、链路上的某个路由器断了) TCP 链接两端是不会收到任何通知的,所以这个时候是根本不会触发 @kyuuseiryuu 所说的 onClose 事件的。这个时候就需要依靠心跳去监听,如果发现对方掉线后就触发类似 onClose 的事件(不管是 client 还是 server ),所以业务的心跳是必须的(当然也可以通过自定义 tcp 的 keepalive 来实现)。 |
10 kaneg 2017-12-12 18:39:18 +08:00 via iPhone ![]() 1. 维持链路连接,同楼上各位的观点 2. 及时感知连接断开。如果对端端由某种原因断开连接而没有被网络层感知到,心跳包能及时感知到这种情况,从而来后续清理或者重连。 |
![]() | 11 Chemist 2017-12-12 18:43:02 +08:00 @morethansean #3 求教. |
![]() | 12 Kilerd 2017-12-12 19:16:16 +08:00 1、ping pong 是必须的,如果你传输的消息量少,而且想保持长连接 2、websocket 不自带心跳, 你可能是 socket.io 的受害者 |
13 jswh 2017-12-12 19:21:35 +08:00 如果是我写,我就让客户端来 ping server,一般心跳维持都是客户端报告我还活着吧。这样你就记录一个上次 ping 时间,超时断开就好了。 |
![]() | 14 mentalidade OP @jswh 那和我的疑惑差不多,因为我是服务端定时推送,如果客户端断开,那么消息直接推送失败而断开。不过官网给的几个例子是使用 ping 的。自己也有 deadline, https://github.com/gorilla/websocket/issues/52 |
![]() | 15 pathbox 2017-12-12 20:58:37 +08:00 via iPhone go 原包不带心跳,gorilla 的 websocket 有心跳方法。你自己实现一个也是可以的。 一般建议 client 发心跳 别让服务端发。 |
![]() | 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)) |