長連接的處理方式是:
Server端收到Client端發來的信息之後,開始心跳計時,在設定時間內如果收到Client發來的消息,則重置計時器,否則計時結束斷開連接。
Client端,處理方式是:用time.NewTicker創建一個定時器,每間隔一秒發送下當前時間到服務器。
服務端代碼如下:
- package main
- import (
- "fmt"
- "os"
- "net"
- "log"
- "github.com/mxi4oyu/MoonSocket/protocol"
- "time"
- )
- //定義CheckError方法,避免寫太多到 if err!=nil
- func CheckError(err error) {
- if err!=nil{
- fmt.Fprintf(os.Stderr,"Fatal error:%s",err.Error())
- os.Exit(1)
- }
- }
- //自定義log
- func Log(v... interface{}) {
- log.Println(v...)
- }
- func main() {
- server_listener,err:=net.Listen("tcp","localhost:8848")
- CheckError(err)
- defer server_listener.Close()
- Log("Waiting for clients connect")
- for{
- new_conn,err:=server_listener.Accept()
- CheckError(err)
- go ServerMsgHandler(new_conn)
- }
- }
- //服務端消息處理
- func ServerMsgHandler(conn net.Conn) {
- //存儲被截斷的數據
- tmpbuf:=make([] byte,0)
- buf:=make([] byte,1024)
- defer conn.Close()
- //接收解包
- readchan:=make(chan [] byte,16)
- go ReadChan(readchan)
- for{
- n,err:=conn.Read(buf)
- if err!=nil{
- fmt.Println("connection close")
- return
- }
- //解包
- tmpbuf = protocol.Depack(append(tmpbuf,buf[:n]...))
- fmt.Println("client say:",string(tmpbuf))
- Msg:=tmpbuf
- beatch :=make(chan byte)
- //心跳計時,默認30秒
- go HeartBeat(conn,beatch,30)
- //檢測每次Client是否有數據傳來
- go HeartChanHandler(Msg,beatch)
- }
- }
- //處理心跳,根據HeartChanHandler判斷Client是否在設定時間內發來信息
- func HeartBeat(conn net.Conn,heartChan chan byte,timeout int) {
- select {
- case hc:=<-heartChan:
- Log("<-heartChan:",string(hc))
- conn.SetDeadline(time.Now().Add(time.Duration(timeout)*time.Second))
- break
- case <-time.After(time.Second*30):
- Log("timeout")
- conn.Close()
- }
- }
- //處理心跳channel
- func HeartChanHandler( n [] byte,beatch chan byte) {
- for _,v:=range n{
- beatch<-v
- }
- close(beatch)
- }
- //從channell中讀取數據
- func ReadChan(readchan chan [] byte) {
- for{
- select {
- case data:=<-readchan:
- Log(string(data))
- }
- }
- }
客戶端代碼如下:
- package main
- import (
- "fmt"
- "os"
- "net"
- "strconv"
- "time"
- "github.com/mxi4oyu/MoonSocket/protocol"
- )
- //定義CheckError方法,避免寫太多到 if err!=nil
- func CheckError(err error) {
- if err!=nil{
- fmt.Fprintf(os.Stderr,"Fatal error:%s",err.Error())
- os.Exit(1)
- }
- }
- func main() {
- if len(os.Args) !=2 {
- fmt.Fprintf(os.Stderr,"Usage:%s IP:Port\n",os.Args[0])
- os.Exit(1)
- }
- //動態傳入服務端IP和端口號
- service:=os.Args[1]
- tcpAddr,err:=net.ResolveTCPAddr("tcp4",service)
- CheckError(err)
- conn,err:=net.DialTCP("tcp",nil,tcpAddr)
- CheckError(err)
- ch:=make(chan int,100)
- ticker := time.NewTicker(time.Second)
- defer ticker.Stop()
- //這裏可以加上業務處理代碼,當應用有數據發送時將數據發送到一個通道里(通道只保存數據指針),這裏加上從通道獲取數據指針的處理函數。
- for{
- select {
- case <-ticker.C:
- ch<-1
- go ClientMsgHandler(conn,ch)
- case <-time.After(time.Second*10):
- defer conn.Close()
- fmt.Println("timeout")
- }
- }
- }
- //客戶端消息處理
- func ClientMsgHandler(conn net.Conn,ch chan int) {
- <-ch
- //獲取當前時間
- msg:=time.Now().String()
- SendMsg(conn,msg)
- }
- func GetSession() string{
- gs1:=time.Now().Unix()
- gs2:=strconv.FormatInt(gs1,10)
- return gs2
- }
- func SendMsg(conn net.Conn,msg string) {
- session:=GetSession()
- words := "{\"Session\":"+session +",\"Meta\":\"Monitor\",\"Message\":\""+msg+"\"}"
- conn.Write([] byte(words))
- protocol.Enpack([]byte(words))
- conn.Write(protocol.Enpack([]byte(words)))
- }