Go语言的TCP示例:
实现功能:客户端发送"abc",服务端转为大写返回"ABC"
服务端
package main
import (
"net"
"strings"
"fmt"
"bufio"
)
func handle(conn net.Conn) {
defer conn.Close() // 服务端关闭连接
for {
var b [8]byte
/* 用bufio接收,每次只接收8字节,剩下的扔掉。相当于我缓冲区只有8字节,你发来的大于8字节的我就扔了 */
reader := bufio.NewReader(conn)
reader.Read(b[:])
// conn.Read(b[:]) //如果用conn接收,每次接收8字节,剩下的会存在缓冲区中,下一次会接收到上一次没收完的,粘包
ret := strings.ToUpper(string(b[:]))
conn.Write([]byte(ret))
}
}
func main() {
sock, _ := net.Listen("tcp", "127.0.0.1:8000")
for {
conn, _ := sock.Accept() //阻塞1
fmt.Println("来了个连接:", conn)
go handle(conn)
}
}
客户端
package main
import (
"fmt"
"net"
)
func main() {
conn, _ := net.Dial("tcp", "127.0.0.1:8000")
defer conn.Close() // 客户端关闭连接
for {
var msg string
fmt.Printf("请输入:")
fmt.Scanln(&msg)
conn.Write([]byte(msg))
var ret [128]byte
conn.Read(ret[:])
fmt.Println(string(ret[:]))
}
}
在linux环境下Go使用的epoll多路复用IO模型(与goroutine无关,就算服务端两个地方都阻塞也是epoll io),所以Go原生就能支持很高的并发
解决粘包(主要解决接收端的粘包,发送端假设只发一次)
用前4个字节存数据的长度, 剩下的字节存数据
( 大端和小端:数据存取和读取的顺序
16进制数:0x123456 占用3个字节
协议用4字节存数据长度
12 34 56 00 大端,高位在左边
00 56 34 12 小端 ,高位在右边)
// socket_stick/proto/proto.go package proto
import ( "bufio" "bytes" "encoding/binary" )
// Encode 将消息编码 func Encode(message string) ([]byte, error) { // 读取消息的长度,转换成int32类型(占4个字节) var length = int32(len(message)) // 计算msg长度,放入4字节int中 var pkg = new(bytes.Buffer) err := binary.Write(pkg, binary.LittleEndian, length) // 写入msg长度。小端的方式存放 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个字节byte,存放的是数据长度 lengthBuff := bytes.NewBuffer(lengthByte) var length int32 err := binary.Read(lengthBuff, binary.LittleEndian, &length) // 将4个字节的byte,转为int32,放入length,length是数据长度 if err != nil { return "", err } if int32(reader.Buffered()) < length+4 { // 如果reader总长度<4报错 return "", err }
// 读取真正的消息数据 pack := make([]byte, int(4+length)) // 在reader中读取length+4长度的字节(length是数据的长度) _, err = reader.Read(pack) if err != nil { return "", err } return string(pack[4:]), nil }
服务端:
// socket_stick/server2/main.go
func process(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 msg failed, err:", err) return } fmt.Println("收到client发来的数据:", msg) } }
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) } }
客户端:
|
请发表评论