tcp客户端与服务端

TCP客户端和服务端

服务端

package main

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

func processCon(conn net.Conn) {
	defer conn.Close()
	// 3. 与客户端通信
	var tmp [128]byte
	reader := bufio.NewReader(os.Stdin) //抓取终端输入
	for {
		n, err := conn.Read(tmp[:]) //传入一个固定大小的数组,然后去获取内容,n表示获取的内容长度
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Println("读取失败:", err)
		}
		fmt.Println(string(tmp[:n])) //转换为字符串
		fmt.Println("请回复:")
		msg, _ := reader.ReadString('\n') //获取输入信息按照\n分割
		msg = strings.TrimSpace(msg)
		if msg == "exit" {
			break
		}
		conn.Write([]byte(msg)) //写入conn里面,也就是发送给对端
	}
}
func runNet() {
	var tcpAddr = net.TCPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: 8080,
	}

	// 1.本地端口启动
	tcpListen, err := net.ListenTCP("tcp", &tcpAddr)
	if err != nil {
		fmt.Println("监听端口err:", err)
	}
	defer tcpListen.Close()
	// 2.等待别人跟我连接
	for {
		conn, err := tcpListen.Accept() //获取连接的对象
		if err != nil {
			fmt.Println("连接失败: ", err)
		}
		go processCon(conn) //开启线程去接收和发送信息
	}
}

func main() {
	runNet()
}

客户端

package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
)

func runConn() {
	// 1.连接对端端口
	conn, err := net.Dial("tcp", "127.0.0.1:8080") //建立连接
	if err != nil {
		fmt.Println("监听端口err:", err)
	}
	defer conn.Close()
	// 2.发送消息,然后循环读信息
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Println("请说话:")
		msg, _ := reader.ReadString('\n') //获取输入信息
		msg = strings.TrimSpace(msg)
		if msg == "exit" {
			break
		}
		conn.Write([]byte(msg)) //写入输入信息
	}
}

func main() {
	runConn()
}

TCP黏包解决

服务端

package main

import (
	"bufio"
	"fmt"
	"io"
	"net"
	proto "oldbody.com/day8/07nianbao_jiejue/protocol"
)

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	for {
		recvStr, err := proto.Decode(reader) //直接调用这个包来解决
		if err == io.EOF {
			return
		}
		if err != nil {
			fmt.Println("decode failed,err:", err)
			return
		}
		fmt.Println("收到client发来的数据:", recvStr)
	}
}

func main() {
	listen, err := net.Listen("tcp", "127.0.0.1:30000")
	if err != nil {
		fmt.Println("listen failed, err:", err)
		return
	}
	defer listen.Close()
	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("accept failed, err:", err)
			continue
		}
		go process(conn)
	}
}

客户端

package main

import (
	"fmt"
	"net"

	proto "oldbody.com/day8/07nianbao_jiejue/protocol"
)

// 黏包 client
func main() {
	conn, err := net.Dial("tcp", "127.0.0.1:30000")
	if err != nil {
		fmt.Println("dial failed, err", err)
		return
	}
	defer conn.Close()
	for i := 0; i < 20; i++ {
		msg := `Hello, Hello. How are you?`
		// 调用协议编码数据
		b, err := proto.Encode(msg)
		if err != nil {
			fmt.Println("encode failed,err:", err)
			return
		}
		conn.Write(b)
		// time.Sleep(time.Second)
	}
}

协议

package proto

import (
	"bufio"
	"bytes"
	"encoding/binary"
)

// Encode 将消息编码
func Encode(message string) ([]byte, error) {
	// 读取消息的长度,转换成int32类型(占4个字节)
	var length = int32(len(message))
	var pkg = new(bytes.Buffer)
	// 写入消息头
	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
	}
	return pkg.Bytes(), nil
}

// Decode 解码消息
func Decode(reader *bufio.Reader) (string, error) {
	// 读取消息的长度
	lengthByte, _ := reader.Peek(4) // 读取前4个字节的数据
	lengthBuff := bytes.NewBuffer(lengthByte)
	var length int32
	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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值