leaf开源服务器第四节-分析源码实现模拟TCP客户端

来源:互联网 发布:詹姆斯生涯总数据 编辑:程序博客网 时间:2024/06/11 13:11
leaf开源游戏服务器源码
leaf开源服务器第一节-分析项目结构
leaf开源服务器第二节-分析之配置文件说明及服务器运行
leaf开源服务器第三节-分析TCP消息通信之增加Glog日志(1)

      大家好,我是Golang语言社区(WwW.Golang.Ltd)的站长,今天继续来给大家分析leaf游戏服务器源码,来实现模拟客户端;

这节我们主要是分析源码实现模拟客户端,因为leaf的作者已经把客户端的结构及收发函数已经实现,所以我们参照源码实现模拟

客户端是很简单的


原作者实现的客户端代码级逻辑处理函数

8.png 


找到了原作者的客户端实现的逻辑处理及基本结构后,直接copy代码,运行就可以;模拟客户单代码如下
  1. package main

  2. import (
  3.         "glog-master" // 此包:Golang语言社区资源站下载,www.Golang.MoM
  4.         "log"
  5.         "math"
  6.         "net"
  7.         "sync"
  8.         "time"
  9. )

  10. func init() {

  11.         // 初始化 日志系统
  12.         flag.Set("alsologtostderr", "true") // 日志写入文件的同时,输出到stderr
  13.         flag.Set("log_dir", "./log")        // 日志文件保存目录
  14.         flag.Set("v", "3")                  // 配置V输出的等级。
  15.         flag.Parse()

  16.         return
  17. }

  18. func main() {
  19.         // 调用函数

  20.         return
  21. }

  22. //-----------------------------------------------------------------------------

  23. type ConnSet map[net.Conn]struct{}

  24. type TCPConn struct {
  25.         sync.Mutex
  26.         conn      net.Conn
  27.         writeChan chan []byte
  28.         closeFlag bool
  29.         msgParser *MsgParser
  30. }

  31. // --------------
  32. // | len | data |
  33. // --------------
  34. // 数据结构信息
  35. type MsgParser struct {
  36.         lenMsgLen    int
  37.         minMsgLen    uint32
  38.         maxMsgLen    uint32
  39.         littleEndian bool
  40. }

  41. // 接口信息
  42. type Agent interface {
  43.         Run()
  44.         OnClose()
  45. }

  46. // 客户端结构
  47. type TCPClient struct {
  48.         sync.Mutex
  49.         Addr            string
  50.         ConnNum         int
  51.         ConnectInterval time.Duration
  52.         PendingWriteNum int
  53.         AutoReconnect   bool
  54.         NewAgent        func(*TCPConn) Agent
  55.         conns           ConnSet
  56.         wg              sync.WaitGroup
  57.         closeFlag       bool

  58.         // msg parser
  59.         LenMsgLen    int
  60.         MinMsgLen    uint32
  61.         MaxMsgLen    uint32
  62.         LittleEndian bool
  63.         msgParser    *MsgParser
  64. }

  65. func NewMsgParser() *MsgParser {
  66.         p := new(MsgParser)
  67.         p.lenMsgLen = 2
  68.         p.minMsgLen = 1
  69.         p.maxMsgLen = 4096
  70.         p.littleEndian = false

  71.         return p
  72. }

  73. func (client *TCPClient) Start() {
  74.         client.init()

  75.         for i := 0; i < client.ConnNum; i++ {
  76.                 client.wg.Add(1)
  77.                 go client.connect()
  78.         }
  79. }

  80. func (client *TCPClient) init() {
  81.         client.Lock()
  82.         defer client.Unlock()

  83.         if client.ConnNum <= 0 {
  84.                 client.ConnNum = 1
  85.                 glog.Info("invalid ConnNum, reset to %v", client.ConnNum)
  86.         }
  87.         if client.ConnectInterval <= 0 {
  88.                 client.ConnectInterval = 3 * time.Second
  89.                 glog.Info("invalid ConnectInterval, reset to %v", client.ConnectInterval)
  90.         }
  91.         if client.PendingWriteNum <= 0 {
  92.                 client.PendingWriteNum = 100
  93.                 glog.Info("invalid PendingWriteNum, reset to %v", client.PendingWriteNum)
  94.         }
  95.         if client.NewAgent == nil {
  96.                 log.Fatal("NewAgent must not be nil")
  97.         }
  98.         if client.conns != nil {
  99.                 log.Fatal("client is running")
  100.         }

  101.         client.conns = make(ConnSet)
  102.         client.closeFlag = false

  103.         // msg parser
  104.         msgParser := NewMsgParser()
  105.         msgParser.SetMsgLen(client.LenMsgLen, client.MinMsgLen, client.MaxMsgLen)
  106.         msgParser.SetByteOrder(client.LittleEndian)
  107.         client.msgParser = msgParser
  108. }

  109. // It's dangerous to call the method on reading or writing
  110. func (p *MsgParser) SetByteOrder(littleEndian bool) {
  111.         p.littleEndian = littleEndian
  112. }

  113. // It's dangerous to call the method on reading or writing
  114. func (p *MsgParser) SetMsgLen(lenMsgLen int, minMsgLen uint32, maxMsgLen uint32) {
  115.         if lenMsgLen == 1 || lenMsgLen == 2 || lenMsgLen == 4 {
  116.                 p.lenMsgLen = lenMsgLen
  117.         }
  118.         if minMsgLen != 0 {
  119.                 p.minMsgLen = minMsgLen
  120.         }
  121.         if maxMsgLen != 0 {
  122.                 p.maxMsgLen = maxMsgLen
  123.         }

  124.         var max uint32
  125.         switch p.lenMsgLen {
  126.         case 1:
  127.                 max = math.MaxUint8
  128.         case 2:
  129.                 max = math.MaxUint16
  130.         case 4:
  131.                 max = math.MaxUint32
  132.         }
  133.         if p.minMsgLen > max {
  134.                 p.minMsgLen = max
  135.         }
  136.         if p.maxMsgLen > max {
  137.                 p.maxMsgLen = max
  138.         }
  139. }

  140. func (client *TCPClient) dial() net.Conn {
  141.         for {
  142.                 conn, err := net.Dial("tcp", client.Addr)
  143.                 if err == nil || client.closeFlag {
  144.                         return conn
  145.                 }

  146.                 glog.Info("connect to %v error: %v", client.Addr, err)
  147.                 time.Sleep(client.ConnectInterval)
  148.                 continue
  149.         }
  150. }

  151. func newTCPConn(conn net.Conn, pendingWriteNum int, msgParser *MsgParser) *TCPConn {
  152.         tcpConn := new(TCPConn)
  153.         tcpConn.conn = conn
  154.         tcpConn.writeChan = make(chan []byte, pendingWriteNum)
  155.         tcpConn.msgParser = msgParser

  156.         go func() {
  157.                 for b := range tcpConn.writeChan {
  158.                         if b == nil {
  159.                                 break
  160.                         }

  161.                         _, err := conn.Write(b)
  162.                         if err != nil {
  163.                                 break
  164.                         }
  165.                 }

  166.                 conn.Close()
  167.                 tcpConn.Lock()
  168.                 tcpConn.closeFlag = true
  169.                 tcpConn.Unlock()
  170.         }()

  171.         return tcpConn
  172. }

  173. func (client *TCPClient) connect() {
  174.         defer client.wg.Done()

  175. reconnect:
  176.         conn := client.dial()
  177.         if conn == nil {
  178.                 return
  179.         }

  180.         client.Lock()
  181.         if client.closeFlag {
  182.                 client.Unlock()
  183.                 conn.Close()
  184.                 return
  185.         }
  186.         client.conns[conn] = struct{}{}
  187.         client.Unlock()

  188.         tcpConn := newTCPConn(conn, client.PendingWriteNum, client.msgParser)
  189.         agent := client.NewAgent(tcpConn)
  190.         agent.Run()

  191.         // cleanup
  192.         tcpConn.Close()
  193.         client.Lock()
  194.         delete(client.conns, conn)
  195.         client.Unlock()
  196.         agent.OnClose()

  197.         if client.AutoReconnect {
  198.                 time.Sleep(client.ConnectInterval)
  199.                 goto reconnect
  200.         }
  201. }

  202. func (tcpConn *TCPConn) Close() {
  203.         tcpConn.Lock()
  204.         defer tcpConn.Unlock()
  205.         if tcpConn.closeFlag {
  206.                 return
  207.         }

  208.         tcpConn.doWrite(nil)
  209.         tcpConn.closeFlag = true
  210. }

  211. func (tcpConn *TCPConn) doWrite(b []byte) {
  212.         if len(tcpConn.writeChan) == cap(tcpConn.writeChan) {
  213.                 glog.Info("close conn: channel full")
  214.                 tcpConn.doDestroy()
  215.                 return
  216.         }

  217.         tcpConn.writeChan <- b
  218. }

  219. func (tcpConn *TCPConn) doDestroy() {
  220.         tcpConn.conn.(*net.TCPConn).SetLinger(0)
  221.         tcpConn.conn.Close()

  222.         if !tcpConn.closeFlag {
  223.                 close(tcpConn.writeChan)
  224.                 tcpConn.closeFlag = true
  225.         }
  226. }

  227. func (client *TCPClient) Close() {
  228.         client.Lock()
  229.         client.closeFlag = true
  230.         for conn := range client.conns {
  231.                 conn.Close()
  232.         }
  233.         client.conns = nil
  234.         client.Unlock()

  235.         client.wg.Wait()
  236. }
复制代码
模拟客户端代码,简单的修改;增加了第三方日志glog库(ps:日志库可以去GITHUB或者去我们社区资源站www.Golang.MoM)
9.png 

我们的模拟客户端就写好了,后面我们会在这个代码的基础上进行模拟消息的发送;


最后给大家总结下,
1 开源框架的目录结构我们首先要熟悉下,原作者肯定比我们使用者考虑的方面多;所以我们多数会找到;所以首先相信原作者
2 每个人开发都有自己的风格,不一定拘泥一个定式;所以大家可以按照自己的实现方式去实现逻辑;写了代码越多你才会越精炼;切忌纸上谈兵。
3 做项目尽量把简单的事情做”复杂“了,这个对后面有益无害。

公众账号:Golang语言社区
社区微博:Golang语言社区
社区网址:www.Golang.Ltd
社区资源:www.Golang.MoM
社区直播:www.huya.com/golang
社区教育:www.NewTon.TV

我是彬哥,下节见。
阅读全文
0 0
原创粉丝点击