创建工程文件
mkdir tcpServer tcpClient
cd tcpServer && go mod init tcpServer && touch server.go
cd ../tcpClient && go mod init tcpClient && touch client.go
服务端
// server.go
package main
import(
"net"
"fmt"
"bufio"
)
func main(){
// get listener
listener, err := net.Listen("tcp", ":8080")
if err != nil{
fmt.Println("listen failed: ")
panic(err)
}
fmt.Println("tcp server running at 127.0.0.1:8080...")
for {
// get connection
conn, err := listener.Accept()
if err != nil{
fmt.Println("accept failed: ")
panic(err)
}
process(conn)
}
}
func process(conn net.Conn){
defer conn.Close()
for {
reader := bufio.NewReader(conn)
var buf [128]byte
n, err := reader.Read(buf[:])
if err != nil{
fmt.Println("read from client failed:")
panic(err)
}
recvStr := string(buf[:n])
fmt.Println("receive from client: ", recvStr)
conn.Write([]byte(recvStr + " from server"))
}
}
$ go run server.go
tcp server running at 127.0.0.1:8080...
客户端
// client.go
package main
import(
"os"
"fmt"
"net"
"bufio"
"strings"
)
func main(){
// create conn to server :8080
conn, err := net.Dial("tcp", ":8080")
if err != nil{
fmt.Println("connnect to server failed: ")
panic(err)
}
defer conn.Close()
inputReader := bufio.NewReader(os.Stdin)
for {
input, _ := inputReader.ReadString('\n')
input_data := strings.Trim(input, "\r\n")
if strings.ToUpper(input_data) == "Q"{
return
}
// send data to server
_, err := conn.Write([]byte(input_data))
if err != nil{
fmt.Println("send data to server failed: ", err)
return
}
// receive data from server
buf := [512]byte{}
n, err := conn.Read(buf[:])
if err != nil{
fmt.Println("receive data from server failed: ", err)
return
}
fmt.Println(string(buf[:n]))
}
}
$ go run client.go
connect to tcp server 0.0.0.0:8080...
测试
$ go run client.go
connect to tcp server 0.0.0.0:8080...
ping
ping from server
yes
yes from server
ok
ok from server
$ go run server.go
tcp server running at 127.0.0.1:8080...
receive from client: ping
receive from client: yes
receive from client: ok
粘包
// server.go
package main
import (
"bufio"
"fmt"
"net"
// "io"
)
func main(){
listener, err := net.Listen("tcp", ":8080")
if err!=nil{
panic(err)
}
fmt.Println("server running at :8080...")
for {
conn, err := listener.Accept()
if err!=nil{
panic(err)
}
go process(conn)
}
}
func process(conn net.Conn){
defer conn.Close()
reader := bufio.NewReader(conn)
var buf [1024]byte
for{
n, err := reader.Read(buf[:])
if err!=nil{
panic(err)
}
recvStr := string(buf[:n])
fmt.Println("receive from client: ", recvStr)
}
}
// client.go
package main
import(
"net"
"fmt"
)
func main(){
conn, err := net.Dial("tcp", ":8080")
if err!=nil{
panic(err)
}
defer conn.Close()
fmt.Println("connect to tcp server :8080...")
for i:=0;i<20;i++{
msg := "how are you"
conn.Write([]byte(msg))
}
}
$ go run server.go
server running at :8080...
receive from client: how are youhow are youhow are youhow are youhow are you
receive from client: how are youhow are youhow are youhow are youhow are youhow are youhow are youhow are youhow are youhow are youhow are youhow are youhow are youhow are youhow are you
理想情况下 服务端 应该打印 20 次 receive from client: how are you
但是实际却发生了粘包现象,有些其他发送次序中的数据跟前面的粘连到了一起
解决粘包的方法:
出现”粘包”的关键在于接收方不确定将要传输的数据包的大小
因此我们可以对数据包加一个协议头和一个数据包长度
封包:就是给客户端一段发送的数据加上包头,包头部分的长度是固定的,并且它存储了包体的长度
根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完整的数据包