闽公网安备 35020302035485号
3.应该由应用层来维护消息和消息的边界,即需要一个应用层协议,比如HTTP
3.自定义协议,将消息分为消息头和消息体,消息头中包含表示消息总长度
package main
// 堆代码 duidaima.com
import (
"log"
"net"
"strings"
)
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8888")
if err != nil {
panic(err)
}
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil {
panic(err)
}
for {
data := make([]byte, 10)
_, err := conn.Read(data)
if err != nil {
log.Printf("%s\n", err.Error())
break
}
receive := string(data)
log.Printf("receive msg: %s\n", receive)
send := []byte(strings.ToUpper(receive))
_, err = conn.Write(send)
if err != nil {
log.Printf("send msg failed, error: %s\n", err.Error())
}
log.Printf("send msg: %s\n", receive)
}
}
}
简单说一下这段代码,有点socket编程的基础的话应该很容易理解,基本上都是Listen -> Accept -> Read这个套路。有些人一下子就看出来这个服务有点“问题”,它是同步阻塞的,也就意味着这个服务同一时间只能处理一个连接请求,其实解决这个问题也很简单,得益于Go协程的强大,我们只需要开启一个协程单独处理每一个连接就行了。不过这不是今天的主题,有兴趣的童鞋可以自行研究。dui@daima:~$ telnet 127.0.0.1 8888 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. 111111 111111 123456 123456当你按回车键的时候telnet会在消息后面自动追加”\r\n“换行符并发送消息!
5.然后重置buffer,把分隔符之后的内容追加到buff,重复第2步
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8888")
if err != nil {
panic(err)
}
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil {
panic(err)
}
reader := bufio.NewReader(conn)
for {
slice, err := reader.ReadSlice('\n')
if err != nil {
continue
}
fmt.Printf("%s", slice)
}
}
}
2、Client端:package main
import (
"log"
"net"
"strconv"
"testing"
"time"
)
func Test(t *testing.T) {
conn, err := net.Dial("tcp", "127.0.0.1:8888")
if err != nil {
log.Println("dial error:", err)
return
}
defer conn.Close()
i := 0
for {
var err error
_, err = conn.Write([]byte(strconv.Itoa(i) + " => 77777\n"))
_, err = conn.Write([]byte(strconv.Itoa(i) + " => 88888\n"))
_, err = conn.Write([]byte(strconv.Itoa(i) + " => 555555555555555555555555555555555555555555\n"))
if err != nil {
panic(err)
}
time.Sleep(time.Second * 1)
_, err = conn.Write([]byte(strconv.Itoa(i) + " => 123456\n"))
_, err = conn.Write([]byte(strconv.Itoa(i) + " => 123456\n"))
if err != nil {
panic(err)
}
time.Sleep(time.Second * 1)
_, err = conn.Write([]byte(strconv.Itoa(i) + " => 9999999\n"))
_, err = conn.Write([]byte(strconv.Itoa(i) + " => 0000000000000000000000000000000000000000000\n"))
if err != nil {
panic(err)
}
i++
}
}
如果要说缺点,这种方式主要存在2点,第一点是分隔符的选择问题,如果需要传输的消息包含分隔符,那就需要提前做转义处理。第二点就是性能问题,如果消息体特别大,每次查找分隔符的位置的话肯定会有一点消耗。package main
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"net"
)
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8888")
if err != nil {
panic(err)
}
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil {
panic(err)
}
reader := bufio.NewReader(conn)
for {
//前4个字节表示数据长度
peek, err := reader.Peek(4)
if err != nil {
continue
}
buffer := bytes.NewBuffer(peek)
//读取数据长度
var length int32
err = binary.Read(buffer, binary.BigEndian, &length)
if err != nil {
continue
}
//Buffered 返回缓存中未读取的数据的长度,如果缓存区的数据小于总长度,则意味着数据不完整
if int32(reader.Buffered()) < length+4 {
continue
}
//从缓存区读取大小为数据长度的数据
data := make([]byte, length+4)
_, err = reader.Read(data)
if err != nil {
continue
}
fmt.Printf("receive data: %s\n", data[4:])
}
}
}
2、Client端:package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"net"
"testing"
"time"
)
func Test(t *testing.T) {
conn, err := net.Dial("tcp", "127.0.0.1:8888")
if err != nil {
log.Println("dial error:", err)
return
}
defer conn.Close()
for {
data, _ := Encode("123456789")
_, err := conn.Write(data)
data, _ = Encode("888888888")
_, err = conn.Write(data)
time.Sleep(time.Second * 1)
data, _ = Encode("777777777")
_, err = conn.Write(data)
data, _ = Encode("123456789")
_, err = conn.Write(data)
time.Sleep(time.Second * 1)
fmt.Println(err)
}
}
func Encode(message string) ([]byte, error) {
// 读取消息的长度
var length = int32(len(message))
var pkg = new(bytes.Buffer)
// 写入消息头
err := binary.Write(pkg, binary.BigEndian, length)
if err != nil {
return nil, err
}
// 写入消息实体
err = binary.Write(pkg, binary.BigEndian, []byte(message))
if err != nil {
return nil, err
}
return pkg.Bytes(), nil
}
七.总结