内容简介:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lengyuezuixue/article/details/79235850
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lengyuezuixue/article/details/79235850
在多客户端同时访问服务器的工作模式下,首先要保证服务器的运行正常。因此,Server和Client建立通讯后,确保连接的及时断开就非常重要。否则,多个客户端长时间占用着连接不关闭,是非常可怕的服务器资源浪费。会使得服务器可服务的客户端数量大幅度减少。
因此,针对短链接和长连接,根据业务的需求,配套不同的处理机制。
短连接
一般建立完连接,就立刻传输数据。传输完数据,连接就关闭。服务端根据需要,设定连接的时长。超过时间长度,就算客户端超时。立刻关闭连接。
长连接
建立连接后,传输数据,然后要保持连接,然后再次传输数据。直到连接关闭。
socket读写可以通过 SetDeadline、SetReadDeadline、SetWriteDeadline设置阻塞的时间。
func (*IPConn) SetDeadline func (c *IPConn) SetDeadline(t time.Time) error func (*IPConn) SetReadDeadline func (c *IPConn) SetReadDeadline(t time.Time) error func (*IPConn) SetWriteDeadline func (c *IPConn) SetWriteDeadline(t time.Time) error
如果做短连接,直接在Server端的连接上设置SetReadDeadline。当你设置的时限到达,无论客户端是否还在继续传递消息,服务端都不会再接收。并且已经关闭连接。
func main() { server := ":7373" netListen, err := net.Listen("tcp", server) if err != nil{ Log("connect error: ", err) os.Exit(1) } Log("Waiting for Client ...") for{ conn, err := netListen.Accept() if err != nil{ Log(conn.RemoteAddr().String(), "Fatal error: ", err) continue } //设置短连接(10秒) conn.SetReadDeadline(time.Now().Add(time.Duration(10)*time.Second)) Log(conn.RemoteAddr().String(), "connect success!") ... } }
这就可以了。在这段代码中,每当10秒中的时限一道,连接就终止了。
根据业务需要,客户端可能需要长时间保持连接。但是服务端不能无限制的保持。这就需要一个机制,如果超过某个时间长度,服务端没有获得客户端的数据,就判定客户端已经不需要连接了(比如客户端挂掉了)。
做到这个,需要一个心跳机制。在限定的时间内,客户端给服务端发送一个指定的消息,以便服务端知道客户端还活着。
func sender(conn *net.TCPConn) { for i := 0; i < 10; i++{ words := strconv.Itoa(i)+" Hello I'm MyHeartbeat Client." msg, err := conn.Write([]byte(words)) if err != nil { Log(conn.RemoteAddr().String(), "Fatal error: ", err) os.Exit(1) } Log("服务端接收了", msg) time.Sleep(2 * time.Second) } for i := 0; i < 2 ; i++ { time.Sleep(12 * time.Second) } for i := 0; i < 10; i++{ words := strconv.Itoa(i)+" Hi I'm MyHeartbeat Client." msg, err := conn.Write([]byte(words)) if err != nil { Log(conn.RemoteAddr().String(), "Fatal error: ", err) os.Exit(1) } Log("服务端接收了", msg) time.Sleep(2 * time.Second) } }
这段客户端代码,实现了两个相同的信息发送频率给服务端。两个频率中间,我们让运行休息了12秒。然后,我们在服务端的对应机制是这样的。
func HeartBeating(conn net.Conn, bytes chan byte, timeout int) { select { case fk := <- bytes: Log(conn.RemoteAddr().String(), "心跳:第", string(fk), "times") conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) break case <- time.After(5 * time.Second): Log("conn dead now") conn.Close() } }
每次接收到心跳数据就 SetDeadline 延长一个时间段 timeout。如果没有接到心跳数据,5秒后连接关闭。
服务端完整代码示例
/** * MyHeartbeatServer * @Author: Jian Junbo * @Email: junbojian@qq.com * @Create: 2017/9/16 14:02 * Copyright (c) 2017 Jian Junbo All rights reserved. * * Description: */ package main import ( "net" "fmt" "os" "time" ) func main() { server := ":7373" netListen, err := net.Listen("tcp", server) if err != nil{ Log("connect error: ", err) os.Exit(1) } Log("Waiting for Client ...") for{ conn, err := netListen.Accept() if err != nil{ Log(conn.RemoteAddr().String(), "Fatal error: ", err) continue } //设置短连接(10秒) conn.SetReadDeadline(time.Now().Add(time.Duration(10)*time.Second)) Log(conn.RemoteAddr().String(), "connect success!") go handleConnection(conn) } } func handleConnection(conn net.Conn) { buffer := make([]byte, 1024) for { n, err := conn.Read(buffer) if err != nil { Log(conn.RemoteAddr().String(), " Fatal error: ", err) return } Data := buffer[:n] message := make(chan byte) //心跳计时 go HeartBeating(conn, message, 4) //检测每次是否有数据传入 go GravelChannel(Data, message) Log(time.Now().Format("2006-01-02 15:04:05.0000000"), conn.RemoteAddr().String(), string(buffer[:n])) } defer conn.Close() } func GravelChannel(bytes []byte, mess chan byte) { for _, v := range bytes{ mess <- v } close(mess) } func HeartBeating(conn net.Conn, bytes chan byte, timeout int) { select { case fk := <- bytes: Log(conn.RemoteAddr().String(), "心跳:第", string(fk), "times") conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) break case <- time.After(5 * time.Second): Log("conn dead now") conn.Close() } } func Log(v ...interface{}) { fmt.Println(v...) return }
客户端完整代码示例
/** * MyHeartbeatClient * @Author: Jian Junbo * @Email: junbojian@qq.com * @Create: 2017/9/16 14:21 * Copyright (c) 2017 Jian Junbo All rights reserved. * * Description: */ package main import ( "net" "fmt" "os" "strconv" "time" ) func main() { server := "127.0.0.1:7373" tcpAddr, err := net.ResolveTCPAddr("tcp4",server) if err != nil{ Log(os.Stderr,"Fatal error:",err.Error()) os.Exit(1) } conn, err := net.DialTCP("tcp",nil,tcpAddr) if err != nil{ Log("Fatal error:",err.Error()) os.Exit(1) } Log(conn.RemoteAddr().String(), "connection succcess!") sender(conn) Log("send over") } func sender(conn *net.TCPConn) { for i := 0; i < 10; i++{ words := strconv.Itoa(i)+" Hello I'm MyHeartbeat Client." msg, err := conn.Write([]byte(words)) if err != nil { Log(conn.RemoteAddr().String(), "Fatal error: ", err) os.Exit(1) } Log("服务端接收了", msg) time.Sleep(2 * time.Second) } for i := 0; i < 2 ; i++ { time.Sleep(12 * time.Second) } for i := 0; i < 10; i++{ words := strconv.Itoa(i)+" Hi I'm MyHeartbeat Client." msg, err := conn.Write([]byte(words)) if err != nil { Log(conn.RemoteAddr().String(), "Fatal error: ", err) os.Exit(1) } Log("服务端接收了", msg) time.Sleep(2 * time.Second) } } func Log(v ...interface{}) { fmt.Println(v...) return }
参考:
https://www.yuque.com/docs/share/ef732d9e-f488-4e7e-8d64-43a1c18872ea
以上所述就是小编给大家介绍的《Golang 心跳的实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python语言程序设计
[美]梁勇(Lang Y. D.) / 李娜 / 机械工业出版社 / 2015-4 / 79.00元
本书采用“问题驱动”、“基础先行”和“实例和实践相结合”的方式,讲述如何使用Python语言进行程序设计。本书首先介绍Python程序设计的基本概念,接着介绍面向对象程序设计方法,最后介绍算法与数据结构方面的内容。为了帮助学生更好地掌握相关知识,本书每章都包括以下模块:学习目标,引言,关键点,检查点,问题,本章总结,测试题,编程题,注意、提示和警告。 本书可以作为高等院校计算机及相关专业Py......一起来看看 《Python语言程序设计》 这本书的介绍吧!