IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 基于Golang TCP 开发网络游戏 CLI四川麻将 - 2.Tcp通讯 -> 正文阅读

[网络协议]基于Golang TCP 开发网络游戏 CLI四川麻将 - 2.Tcp通讯

项目地址

https://github.com/mangenotwork/CLI-Sichuan-Mahjong

数据传输结构定义

这里使用gob序列化数据

// entity.go

type TransfeData struct {
	Cmd enum.Command  // 指令
	Timestamp int64
	Token string // 识别客户端身份
	Data interface{} // 传输的数据
	Message string // 传输消息
	Code int // 传输code
}

func (t *TransfeData) Byte() []byte {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(t)
	if err != nil {
		log.Fatal("encode error:", err)
	}
	return buf.Bytes()
}

func NewTransfeData(cmd enum.Command, token string, data interface{}) []byte {
	tra := &TransfeData{
		Cmd: cmd,
		Timestamp: time.Now().Unix(),
		Token: token,
		Data: data,
	}
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(tra)
	if err != nil {
		log.Fatal("encode error:", err)
	}
	return buf.Bytes()
}

func TransfeDataDecoder(data []byte) *TransfeData {
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	tra := &TransfeData{}
	err := dec.Decode(&tra)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	return tra
}

Server 端

// tcp
type TcpServer struct {
	Listener   *net.TCPListener
	HawkServer *net.TCPAddr
}

// 运行服务
func Run() {
	//类似于初始化套接字,绑定端口
	hawkServer, err := net.ResolveTCPAddr("tcp", "0.0.0.0:14444")
	if err != nil {
		panic(err)
	}

	//侦听
	listen, err := net.ListenTCP("tcp", hawkServer)
	utils.PanicErr(err)

	//关闭
	defer listen.Close()

	tcpServer := &TcpServer{
		Listener:   listen,
		HawkServer: hawkServer,
	}
	log.Println("start Master TCP server successful.")

	//接收请求
	for {

		//来自客户端的连接
		conn, err := tcpServer.Listener.Accept()
		if err != nil {
			log.Println("[连接失败]:", err.Error())
			continue
		}
		log.Println("[连接成功]: ", conn.RemoteAddr().String(), conn)

		go func(conn net.Conn){
			// 最大接收1024个字节
			recv := make([]byte, 1024)
			
			// 设置读取超时
			err = conn.SetReadDeadline(time.Now().Add(60*time.Second)) // timeout
			if err != nil {
				log.Println("setReadDeadline failed:", err)
			}
			for {
				// 接收数据
				n, err := conn.Read(recv)
				if err != nil{
					// 处理断开连接
					if err == io.EOF {
						log.Println(conn.RemoteAddr().String(), " 断开了连接!")
						conn.Close()
						return
					}
				}
				// 打印接收的数据
				if n > 0 && n < 1025 {
					data := entity.TransfeDataDecoder(recv[:n])
					log.Println(data)
				}
				
			}
		}(conn)
	}
}

Client端

// 重连chan
var RConn = make(chan bool)

type TcpClient struct {
	Connection *net.TCPConn
	HawkServer *net.TCPAddr
	StopChan   chan struct{} // 停止 chan
	CmdChan chan *entity.TransfeData // 来自Server数据传输chan
}

func (c *TcpClient) Send(b []byte) (int, error) {
	return c.Connection.Write(b)
}

func (c *TcpClient) Read(b []byte) (int, error) {
	return c.Connection.Read(b)
}

func (c *TcpClient) Addr() string {
	return c.Connection.RemoteAddr().String()
}

func (c *TcpClient) Close(){
	c.Connection.Close()
	RConn <- true
}

func Run(){

	//用于重连
Reconnection:
	host := "192.168.0.9:14444"
	hawkServer, err := net.ResolveTCPAddr("tcp", host)
	if err != nil {
		log.Printf("hawk server [%s] resolve error: [%s]", host, err.Error())
		time.Sleep(1 * time.Second)	//连接失败后1秒重连
		goto Reconnection
	}

	//连接服务器
	connection, err := net.DialTCP("tcp", nil, hawkServer)
	if err != nil {
		log.Printf("connect to hawk server error: [%s]", err.Error())
		time.Sleep(1 * time.Second)
		goto Reconnection
	}
	log.Println("[连接成功] 连接服务器成功")

	//创建客户端实例
	client := &TcpClient{
		Connection: connection,
		HawkServer: hawkServer,
		StopChan:   make(chan struct{}),
		CmdChan: make(chan *entity.TransfeData),
	}

	//启动接收
	go func(conn *models.TcpClient){
		for{
			recv := make([]byte, 1024)
			for {
				n, err := conn.Connection.Read(recv)
				if err != nil{
					if err == io.EOF {
						log.Println(conn.Addr(), " 断开了连接!")
						conn.Close()
						return
					}
				}
				if n > 0 && n < 1025 {
					conn.CmdChan <- entity.TransfeDataDecoder(recv[:n])
				}
			}
		}
	}(client)

	// 发送心跳
	go func(conn *models.TcpClient){
		i := 0
		heartBeatTick := time.Tick(10 * time.Second)
		for {
			select {
			case <-heartBeatTick:
				heartBeat := entity.NewTransfeData(enum.HeartPacket, "", i)
				if _, err := conn.Send(heartBeat); err != nil {
					models.RConn <- true
					return
				}
				i++
			case <-conn.StopChan:
				return
			}
		}
	}(client)
	
	// 接收来自Server端的数据
	go func(conn *TcpClient){
		for{
			select{
				case data := <- c.CmdChan:
					log.Println(data)
			}
		}
	}(conn)

	// 处理重连信号
	for {
		select {
		case a := <- models.RConn:
			log.Println("global.RConn = ", a)
			goto Reconnection
		}
	}

	//等待退出
	<-client.StopChan

}

上一篇 1.选型与结构定义

https://blog.csdn.net/Man_ge/article/details/120204207

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-09-11 19:10:26  更:2021-09-11 19:10:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:25:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码