码迷,mamicode.com
首页 > 编程语言 > 详细

TCP协议-GO语言

时间:2021-01-22 11:52:12      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:std   protocol   命令   拆分   nil   UNC   cep   else   listener   

0,server服务端——常规版本

package main

//server服务端

import (
	"fmt"
	"net"
)

func main() {
	//01,启动服务		net.Listen
	listener, err1 := net.Listen("tcp", "127.0.0.1:20000") //net.Listen网络.监听
	if err1 != nil {
		fmt.Println("12.0.0.1:20000 连接错误!!!")
		return
	}

	//02,监听.接收连接	listener.Accept(),用 for循环建立多个请求
	for {
		conn, err2 := listener.Accept() //listener.Accept收听.接收
		if err2 != nil {
			fmt.Println("accept 接受请求错误!!!")
			return
		}

		//03,读取信息 连接对象.Read([]byte存放信息),发送客户端
		go conn_(conn)
	}
}

//--------------------------使用函数------------------------------
func conn_(conn net.Conn) {
	var tmp [128]byte
	for {
		n, err := conn.Read(tmp[:]) //用for 循环 conn对象可以一直保持接收
		if err != nil {
			fmt.Println("客户端读取错误!!!!")
			return
		}
		fmt.Println(string(tmp[:n]))
	}
}

  1,client客户端——常规版本

package main

//client客户端

import (
	"fmt"
	"net"

	//"bufio"
	//"os"
	//"strings"
)

func main() {
	//01,与server端建立连接
	conn, err1 := net.Dial("tcp", "127.0.0.1:20000") //net.Dial网络.拨号
	if err1 != nil {
		fmt.Println("dial 127.0.0.1:20000 failed,err")
		return
	}
	//02,发送数据
	//var msg string

	/*用法1		用命令行参数os.Args发出
	if len(os.Args) < 2 {
		msg = "hello wangye!!!"
	} else {
		msg = os.Args[1]
	}
	conn.Write([]byte(msg))
	*/

	/*用法2		循环发出 input 用户数据 > 弊端无法过滤空格输出
	for {
		fmt.Print("请老大发言:")
		fmt.Scanln(&msg)
		if msg == "exit" {
			break
		}
		conn.Write([]byte(msg))
	}*/

	/*用法3		用bufio.NewReader()
	for {
		reader := bufio.NewReader(os.Stdin) //从标准输入生成读对象
		fmt.Print("请老大发言:")
		text, _ := reader.ReadString(‘\n‘) //读到换行
		text = strings.TrimSpace(text)     //trim space去除空格

		conn.Write([]byte(text))
	}*/

	for i := 0; i < 20; i++ {
		msg := "黑鲁,黑鲁,how are you?"
		b, err := proto.Encode(msg)
		if err != nil {
			fmt.Println("Encode failed,err:", err)
		}
		conn.Write(b)
	}
	//03,关闭程序
	defer conn.Close()
}

//--------------------------使用函数------------------------------

  上面 for 循环发送消息,速度快,存在粘包

//出现"粘包"的关键在于接收方不确定数据包大小,可以将数据封包和拆包。
//封包: 封包就是给一段数据加包头,过滤非法包时封包会加入”包尾“内容
//包头长度固定,且储存包体长度,根据包头长度固定和包体长度的变量就能确定拆分出完整数据包
//自己定义一个协议,比如数据包前4个字节为包头,储存的是发送的数据长度
自写一个protocol包,调用里面的 Encode()编码, DEcode()解码
0,粘包方案
package proto

import (
	"bufio"
	"bytes"
	"encoding/binary" //编码/二进制
)

//01,先用 binary.Write() 将消息打包成 int32 4个字节,小端排列对象
//02,再用 binary.Read() 将消息按照4字节,小端读取(1次读标准长度来避免粘包)

//Encode 将消息编码
func Encode(message string) ([]byte, error) {

	//读取长度,转换成int32类型(占4个字节)
	length := int32(len(message))

	//定义一个字节类型的缓冲对象,具有Read 和Write 方法的包
	pkg := new(bytes.Buffer)

	//调用二进制写入函数,把pck包对象,按照传入int32类型的长度,小端方式排列
	err := binary.Write(pkg, binary.LittleEndian, length)
	if err != nil {
		//如果对象不为空,返回空,和对象
		return nil, err
	}

	//小端方式写入类容
	err = binary.Write(pkg, binary.LittleEndian, []byte(message))
	if err != nil {
		return nil, err
	}

	//把Bytes.Buffer对象 转换成Bytes()对象返回
	return pkg.Bytes(), nil
}

//Decode 将消息解码
func Decode(reader *bufio.Reader) (string, error) {

	//读取消息长度
	lengthByte, _ := reader.Peek(4)           //读取4个字节的数据
	lengthBuff := bytes.NewBuffer(lengthByte) //创建字节对象
	var length int32

	//把字节对象按照 小端 读取长度放入length中,然后判断 01返回err是否为空 02类容有长度
	err := binary.Read(lengthBuff, binary.LittleEndian, &length)
	if err != nil {
		return "", err
	}

	//Buffered返回缓冲中现有的可读取的字节数
	if int32(reader.Buffered()) < length+4 {
		return "", err
	}

	//读取真正的消息
	//定义一个数组,设定长度
	pack := make([]byte, int(4+length))
	_, err = reader.Read(pack)
	if err != nil {
		return "", err
	}
	return string(pack[4:]), nil
}

  1,客户端代码——粘包解决版

package main

//server服务端

import (
	"bufio"
	"fmt"
	"io"
	"net"
)

func main() {
	//01,启动服务		net.Listen
	listener, err1 := net.Listen("tcp", "127.0.0.1:20000") //net.Listen网络.监听
	if err1 != nil {
		fmt.Println("12.0.0.1:20000 连接错误!!!")
		return
	}

	//02,接受连接	listener.Accept(),用 for循环建立多个请求
	for {
		conn, err2 := listener.Accept() //listener.Accept收听.接受
		if err2 != nil {
			fmt.Println("accept 接受请求错误!!!")
			return
		}

		//03,读取信息 连接对象.Read([]byte存放信息),发送客户端
		go conn_(conn)
	}
}

//--------------------------使用函数------------------------------
func conn_(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	for {
		msg, err := proto.Decode(reader)
		if err == io.EOF {
			return
		}
		if err != nil {
			fmt.Println("Decode failed,err:", err)
			return
		}
		fmt.Println("收到client发来的消息:", msg)
	}
}

  2,client客户端代码——粘包解决版

package main

//client客户端

import (
	"fmt"
	"net"

	//"bufio"
	//"os"
	//"strings"
)

func main() {
	//01,与server端建立连接
	conn, err1 := net.Dial("tcp", "127.0.0.1:20000") //net.Dial网络.拨号
	if err1 != nil {
		fmt.Println("dial 127.0.0.1:20000 failed,err")
		return
	}
	//02,发送数据
	//var msg string

	/*用法1		用命令行参数os.Args发出
	if len(os.Args) < 2 {
		msg = "hello wangye!!!"
	} else {
		msg = os.Args[1]
	}
	conn.Write([]byte(msg))
	*/

	/*用法2		循环发出 input 用户数据 > 弊端无法过滤空格输出
	for {
		fmt.Print("请老大发言:")
		fmt.Scanln(&msg)
		if msg == "exit" {
			break
		}
		conn.Write([]byte(msg))
	}*/

	/*用法3		用bufio.NewReader()
	for {
		reader := bufio.NewReader(os.Stdin) //从标准输入生成读对象
		fmt.Print("请老大发言:")
		text, _ := reader.ReadString(‘\n‘) //读到换行
		text = strings.TrimSpace(text)     //trim space去除空格

		conn.Write([]byte(text))
	}*/

	for i := 0; i < 20; i++ {
		msg := "黑鲁,黑鲁,how are you?"
		b, err := proto.Encode(msg)
		if err != nil {
			fmt.Println("Encode failed,err:", err)
		}
		conn.Write(b)
	}
	//03,关闭程序
	defer conn.Close()
}

//--------------------------使用函数------------------------------

  

Encode

TCP协议-GO语言

标签:std   protocol   命令   拆分   nil   UNC   cep   else   listener   

原文地址:https://www.cnblogs.com/shijieziben/p/14307744.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!