注意:
- 如果客户端一直向服务器发送数据,而服务器不接收的话,会有缓冲上限,达到上限会阻塞。
- 服务器一直向客户端发送数据,而客户端不接收的话,会有缓冲上限,达到上限会阻塞。
粘包问题
- 接收大小 < 发送数据大小 会出现粘包
- 发送数据频率高会出现粘包
解决粘包
msg = "wanghaha"
- 先发送固定长度(4字节)的数据(包头),内容为消息的长度
包头=int32(len(msg))
- 再发送包体
包体 = msg
一个服务端服务多个客户端
server
package main
import (
"net"
"strings"
)
import "fmt"
// 持续接收消息
func recvMsg(conn net.Conn) {
for {
data := make([]byte, 1024)
n, err := conn.Read(data)
// 接收消息失败,退出循环,断开连接
if err != nil {
fmt.Println("接收消息失败...")
break
}
// 空消息,退出循环,断开连接
if n == 0 {
break
}
fmt.Printf("收到消息: %s\n", data[:n])
// 将接收到的消息转大写再发送回去
msg := string(data[:n])
_, err = conn.Write([]byte(strings.ToUpper(msg)))
if err != nil {
fmt.Println("消息发送失败...")
break
}
}
_ = conn.Close()
fmt.Println("close", conn.RemoteAddr())
}
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8090")
if err != nil {
panic(err)
}
fmt.Println("开始监听...")
for {
conn, err := listen.Accept()
if err != nil {
panic(err)
}
// 新开一个goroutine持续接收消息
go recvMsg(conn)
}
}
client
package main
import (
"bufio"
"net"
"os"
"fmt"
)
// 持续接收消息
func recvMsg(conn net.Conn) {
for {
data := make([]byte, 1024)
n, err := conn.Read(data)
if err != nil {
fmt.Println("接收消息失败...")
break
}
if n == 0 {
break
}
fmt.Printf("recv msg: %s\n", data[:n])
}
_ = conn.Close()
fmt.Println("断开连接", conn.RemoteAddr())
}
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8090")
if err != nil {
panic(err)
}
// 新开一个goroutine去持续接收消息
go recvMsg(conn)
for {
fmt.Print("msg:")
reader := bufio.NewReader(os.Stdin)
lineBytes, _, _ := reader.ReadLine()
// 发送消息
_, err := conn.Write(lineBytes)
if err != nil {
fmt.Println("发送消息失败...")
os.Exit(1)
}
}
}
服务器转发消息,实现多个client之间通信
server
package main
import (
"net"
"strings"
)
import "fmt"
// 持续接收消息
func recvMsg(conn net.Conn, userList *[]net.Conn) {
for {
data := make([]byte, 1024)
n, err := conn.Read(data)
// 接收消息失败,退出循环,断开连接
if err != nil {
fmt.Println("接收消息失败...")
break
}
// 空消息,退出循环,断开连接
if n == 0 {
break
}
fmt.Printf("收到消息: %s\n", data[:n])
// 消息转发至各个客户端
msg := string(data[:n])
for _, user := range *userList {
_, _ = user.Write([]byte(strings.ToUpper(msg)))
}
}
_ = conn.Close()
fmt.Println("close", conn.RemoteAddr())
}
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8090")
if err != nil {
panic(err)
}
fmt.Println("开始监听...")
userList := []net.Conn{}
for {
conn, err := listen.Accept()
if err != nil {
panic(err)
}
userList = append(userList, conn)
// 新开一个goroutine持续接收消息
go recvMsg(conn, &userList)
}
}
client
package main
import (
"bufio"
"net"
"os"
"fmt"
)
// 持续接收消息
func recvMsg(conn net.Conn) {
for {
data := make([]byte, 1024)
n, err := conn.Read(data)
if err != nil {
fmt.Println("接收消息失败...")
break
}
if n == 0 {
break
}
fmt.Printf("收到消息: %s\n", data[:n])
}
_ = conn.Close()
fmt.Println("断开连接", conn.RemoteAddr())
}
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8090")
if err != nil {
panic(err)
}
// 新开一个goroutine去持续接收消息
go recvMsg(conn)
for {
fmt.Print("msg:")
reader := bufio.NewReader(os.Stdin)
lineBytes, _, _ := reader.ReadLine()
// 发送消息
_, err := conn.Write(lineBytes)
if err != nil {
fmt.Println("发送消息失败...")
os.Exit(1)
}
}
}
端口转发
package main
import (
"net"
)
import "fmt"
func recvMsg(client net.Conn, proxy net.Conn) {
for {
data := make([]byte, 1024)
n, err := client.Read(data)
if n == 0 {
break
}
msg := data[:n]
if err != nil {
fmt.Println("接收消息客户端失败...")
break
}
_, err = proxy.Write(msg)
if err != nil {
fmt.Println("发送数据失败...")
_ = proxy.Close()
}
}
_ = client.Close()
}
func sendMsg(proxy net.Conn, client net.Conn) {
for {
data := make([]byte, 1024)
n, err := proxy.Read(data)
if n == 0 {
break
}
msg := data[:n]
if err != nil {
fmt.Println("接收返回消息失败...")
break
}
_, err = client.Write(msg)
if err != nil {
fmt.Println("发送数据失败...")
_ = client.Close()
}
}
_ = proxy.Close()
}
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8090")
if err != nil {
panic(err)
}
fmt.Println("listen...")
for {
client, err := listen.Accept()
if err != nil {
panic(err)
}
proxy, err := net.Dial("tcp", "127.0.0.1:10000")
if err != nil {
panic(err)
}
go recvMsg(client, proxy)
go sendMsg(proxy, client)
}
}
端口转发简写版
package main
import (
"net"
)
import "fmt"
func handle(recipient net.Conn, sender net.Conn) {
for {
data := make([]byte, 1024)
n, err := recipient.Read(data)
if n == 0 {
break
}
msg := data[:n]
if err != nil {
fmt.Println("接收消息客户端失败...")
break
}
_, err = sender.Write(msg)
if err != nil {
fmt.Println("发送数据失败...")
_ = sender.Close()
}
}
_ = recipient.Close()
}
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8090")
if err != nil {
panic(err)
}
fmt.Println("listen...")
for {
client, err := listen.Accept()
if err != nil {
panic(err)
}
proxy, err := net.Dial("tcp", "127.0.0.1:10000")
if err != nil {
panic(err)
}
go handle(client, proxy)
go handle(proxy, client)
}
}
端口转发简写版3
package main
import (
"io"
"log"
"net"
)
func main() {
address := ":9000"
log.Printf("listen %s\n", address)
listener, err := net.Listen("tcp4", address)
if err != nil {
panic(err)
}
for {
conn, err := listener.Accept()
if err != nil {
conn.Close()
break
}
go handle(conn)
}
}
func handle(conn net.Conn) {
defer conn.Close()
target, err := net.Dial("tcp4", "127.0.0.1:8080")
if err != nil {
return
}
defer target.Close()
go io.Copy(target, conn)
io.Copy(conn, target)
}
解决粘包
server.go
package main
import (
"fmt"
"github.com/gogf/gf/util/gconv"
"log"
"net"
)
func main() {
listener, err := net.Listen("tcp4", ":9000")
if err != nil {
panic(err)
}
for {
conn, err := listener.Accept()
defer conn.Close()
if err != nil {
log.Println(err)
break
}
go Handle(conn)
}
}
func Handle(conn net.Conn) {
for {
// receive msg
header := make([]byte, 4)
hLen, err := conn.Read(header)
if err != nil {
log.Printf("%s close.", conn.RemoteAddr())
conn.Close()
break
}
headerLength := gconv.Int(header[:hLen])
data := make([]byte, gconv.Int(header[:hLen]))
n, err := conn.Read(data)
if err != nil {
log.Printf("%s close.", conn.RemoteAddr())
conn.Close()
break
}
body := string(data[:n])
fmt.Println("receive msg:", headerLength, body)
// send data
conn.Write(gconv.Bytes(int32(len(body))))
conn.Write(gconv.Bytes(body))
}
}
client.go
package main
import (
"fmt"
"github.com/gogf/gf/util/gconv"
"net"
)
func main() {
conn, err := net.Dial("tcp4", "127.0.0.1:9000")
if err != nil {
panic(err)
}
for {
// send msg
msg := "wanghaha12313131"
conn.Write(gconv.Bytes(int32(len(msg))))
conn.Write([]byte(msg))
// receive msg
header := make([]byte, 4)
hLen, err := conn.Read(header)
if err != nil {
conn.Close()
break
}
headerLength := gconv.Int(header[:hLen])
data := make([]byte, gconv.Int(header[:hLen]))
n, err := conn.Read(data)
if err != nil {
conn.Close()
break
}
body := string(data[:n])
fmt.Println("receive msg:", headerLength, body)
}
}
client.py
import socket
import struct
client = socket.socket()
client.connect(('127.0.0.1', 9000))
while True:
# send msg
send_msg = "wanghaha"
send_header = struct.pack("i", len(send_msg))
client.send(send_header)
client.send(send_msg.encode())
# receive msg
receive_header = client.recv(4)
data = struct.unpack("i", receive_header)
receive_msg = client.recv(data[0])
print(data[0], receive_msg.decode())
游戏服务器
package main
import (
"fmt"
"github.com/gogf/gf/util/gconv"
"log"
"net"
)
func main() {
address := ":9000"
log.Println("========================")
log.Printf("listen %s\n", address)
log.Println("========================")
listener, err := net.Listen("tcp4", address)
if err != nil {
panic(err)
}
connList := []net.Conn{}
for {
conn, err := listener.Accept()
defer conn.Close()
if err != nil {
log.Println(err)
break
}
connList = append(connList, conn)
go Handle(conn, &connList)
}
}
func Handle(conn net.Conn, connList *[]net.Conn) {
for {
// receive msg
header := make([]byte, 4)
hLen, err := conn.Read(header)
if err != nil {
log.Printf("%s close.", conn.RemoteAddr())
conn.Close()
break
}
headerLength := gconv.Int(header[:hLen])
data := make([]byte, gconv.Int(header[:hLen]))
n, err := conn.Read(data)
if err != nil {
log.Printf("%s close.", conn.RemoteAddr())
conn.Close()
break
}
body := string(data[:n])
//fmt.Println("receive msg:", headerLength, body)
_ = headerLength
fmt.Println(connList)
// 消息转发至各个客户端
for _, user := range *connList {
// send data
user.Write(gconv.Bytes(int32(len(body))))
user.Write(gconv.Bytes(body))
}
}
}
游戏客户端
import threading
import struct
import pygame
from sys import exit
import socket
import json
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 9000))
name = "user1"
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("hello, "+name)
background = pygame.image.load("background.png").convert()
car1 = pygame.image.load("car1.png")
car2 = pygame.image.load("car2.png")
car1 = pygame.transform.scale(car1, (80, 50))
car2 = pygame.transform.scale(car2, (150, 50))
car_x = 0
car_y = 0
car2_x = 0
car2_y = 200
def receive_data():
while True:
try:
global car_x, car_y, car2_x, car2_y
header = client.recv(4)
header_length = struct.unpack("i", header)[0]
json_data = client.recv(header_length).decode()
print(json_data)
body = json.loads(json_data)
print(body)
for item in body:
user_name = item["name"]
x = item["x"]
y = item["y"]
if user_name == "user1":
car_x = x
car_y = y
elif user_name == "user2":
car2_x = x
car2_y = y
except Exception as e:
print(e)
continue
threading.Thread(target=receive_data).start()
def send_data():
data = [
{
"name": "user1",
"x": car_x,
"y": car_y
},
{
"name": "user2",
"x": car2_x,
"y": car2_y
},
]
json_data = json.dumps(data)
header = struct.pack("i", len(json_data))
client.send(header)
client.sendall(json_data.encode())
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.init()
exit()
# screen.blit(background, (0, 0))
screen.fill((0, 0, 0))
screen.blit(car1, (car_x, car_y))
screen.blit(car2, (car2_x, car2_y))
pygame.display.update()
keyboard = pygame.key.get_pressed()
if keyboard[pygame.K_DOWN]:
if name == "user1":
car_y += 1
elif name == "user2":
car2_y += 1
send_data()
elif keyboard[pygame.K_UP]:
if name == "user1":
car_y -= 1
elif name == "user2":
car2_y -= 1
send_data()
elif keyboard[pygame.K_LEFT]:
if name == "user1":
car_x -= 1
elif name == "user2":
car2_x -= 1
send_data()
elif keyboard[pygame.K_RIGHT]:
if name == "user1":
car_x += 1
elif name == "user2":
car2_x += 1
send_data()
封装收发请求函数(解决粘包)
func ReceiveMessage(conn net.Conn) ([]byte, error) {
// receive header
header := make([]byte, 4)
headerLength, err := conn.Read(header)
if err != nil {
return nil, err
}
// receive body
body := make([]byte, gconv.Int(header[:headerLength]))
n, err := conn.Read(body)
if err != nil {
return nil, err
}
return body[:n], nil
}
func SendMessage(conn net.Conn, message []byte) error {
// send header
_, err := conn.Write(gconv.Bytes(int32(len(string(message)))))
if err != nil {
return err
}
// send body
_, err = conn.Write(message)
if err != nil {
return err
}
return nil
}
多人聊天(已处理粘包问题)
server.go
package main
import (
"fmt"
"github.com/gogf/gf/util/gconv"
"log"
"net"
"strings"
)
// 持续接收消息
func recvMsg(conn net.Conn, userList *[]net.Conn) {
for {
message, err := ReceiveMessage(conn)
if err != nil {
log.Println(conn.RemoteAddr(), "close.")
break
}
fmt.Printf("收到消息: %s\n", message)
// 消息转发至各个客户端
msg := string(message)
for _, user := range *userList {
_ = SendMessage(user, gconv.Bytes(strings.ToUpper(msg)))
}
}
_ = conn.Close()
fmt.Println("close", conn.RemoteAddr())
}
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:8090")
if err != nil {
panic(err)
}
fmt.Println("开始监听...")
userList := []net.Conn{}
for {
conn, err := listen.Accept()
if err != nil {
panic(err)
}
userList = append(userList, conn)
// 新开一个goroutine持续接收消息
go recvMsg(conn, &userList)
}
}
func ReceiveMessage(conn net.Conn) ([]byte, error) {
// receive header
header := make([]byte, 4)
headerLength, err := conn.Read(header)
if err != nil {
return nil, err
}
// receive body
body := make([]byte, gconv.Int(header[:headerLength]))
n, err := conn.Read(body)
if err != nil {
return nil, err
}
return body[:n], nil
}
func SendMessage(conn net.Conn, message []byte) error {
// send header
_, err := conn.Write(gconv.Bytes(int32(len(string(message)))))
if err != nil {
return err
}
// send body
_, err = conn.Write(message)
if err != nil {
return err
}
return nil
}
client.go
package main
import (
"bufio"
"fmt"
"github.com/gogf/gf/util/gconv"
"net"
"os"
)
// 持续接收消息
func recvMsg(conn net.Conn) {
for {
message, err := ReceiveMessage(conn)
if err != nil {
break
}
fmt.Printf("收到消息: %s\n", message)
}
_ = conn.Close()
fmt.Println("断开连接", conn.RemoteAddr())
}
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8090")
if err != nil {
panic(err)
}
// 新开一个goroutine去持续接收消息
go recvMsg(conn)
for {
fmt.Print("msg:")
reader := bufio.NewReader(os.Stdin)
lineBytes, _, _ := reader.ReadLine()
// 发送消息
err := SendMessage(conn, lineBytes)
if err != nil {
fmt.Println("发送消息失败...")
os.Exit(1)
}
}
}
func ReceiveMessage(conn net.Conn) ([]byte, error) {
// receive header
header := make([]byte, 4)
headerLength, err := conn.Read(header)
if err != nil {
return nil, err
}
// receive body
body := make([]byte, gconv.Int(header[:headerLength]))
n, err := conn.Read(body)
if err != nil {
return nil, err
}
return body[:n], nil
}
func SendMessage(conn net.Conn, message []byte) error {
// send header
_, err := conn.Write(gconv.Bytes(int32(len(string(message)))))
if err != nil {
return err
}
// send body
_, err = conn.Write(message)
if err != nil {
return err
}
return nil
}
tcpx封包&解包
package main
import (
"fmt"
"github.com/fwhezfwhez/tcpx"
)
func main() {
// pack msg
msg, err := tcpx.PackWithMarshaller(tcpx.Message{
MessageID: 1,
Header: nil,
Body: []byte("hello world"),
}, nil)
if err != nil {
panic(err)
}
fmt.Println("pack msg:", msg)
// unpack msg
var body []byte
dest, err := tcpx.UnpackWithMarshaller(msg, &body, tcpx.JsonMarshaller{})
if err != nil {
panic(err)
}
fmt.Println(string(body))
fmt.Println("MessageID:", dest.MessageID)
fmt.Println("Header:", dest.Header)
fmt.Println("Body:", dest.Body)
}
tcpx自定义封包解包
package main
import (
"encoding/binary"
"encoding/json"
"fmt"
"reflect"
)
func main() {
// pack
msg := Pack(Message{
MessageID: 1,
Header: nil,
Body: []byte("hello world"),
})
fmt.Println("pack msg:", msg)
// unpack
var body []byte
dest := Unpack(msg, &body)
fmt.Println(string(body))
fmt.Println("MessageID:", dest.MessageID)
fmt.Println("Header:", dest.Header)
fmt.Println("Body:", dest.Body)
}
type Message struct {
MessageID int32 `json:"message_id"`
Header map[string]interface{} `json:"header"`
Body interface{} `json:"body"`
}
func Pack(message Message) []byte {
var messageID = message.MessageID
var messageIDBuf = make([]byte, 4)
binary.BigEndian.PutUint32(messageIDBuf, uint32(messageID))
var header []byte
var headerLength = make([]byte, 4)
header, _ = json.Marshal(message.Header)
binary.BigEndian.PutUint32(headerLength, uint32(len(header)))
var bodyLength = make([]byte, 4)
body, _ := json.Marshal(message.Body)
binary.BigEndian.PutUint32(bodyLength, uint32(len(body)))
// messageIDFixLength(4) + headerFixLength(4) + bodyFixedLength(4) + headerLength(14) + bodyLength(19)=45 -> [0 0 0 45]
lenBytes := make([]byte, 4)
length := len(messageIDBuf) + len(headerLength) + len(bodyLength) + len(header) + len(body)
binary.BigEndian.PutUint32(lenBytes, uint32(length))
// length, messageID, headerLength, bodyLength, header, body
content := append(lenBytes, messageIDBuf...)
content = append(content, headerLength...)
content = append(content, bodyLength...)
content = append(content, header...)
content = append(content, body...)
return content
}
func Unpack(buf []byte, dest interface{}) Message {
//length := binary.BigEndian.Uint32(buf[0:4])
// length = 45
messageID := binary.BigEndian.Uint32(buf[4:8])
// messageID = 1
headerLength := binary.BigEndian.Uint32(buf[8:12])
// headerLength = 14
bodyLength := binary.BigEndian.Uint32(buf[12:16])
// bodyLength = 19
var header map[string]interface{}
json.Unmarshal(buf[16:16+headerLength], &header)
json.Unmarshal(buf[16+headerLength:16 + headerLength + bodyLength], &dest)
return Message{
MessageID: int32(messageID),
Header: header,
Body: reflect.Indirect(reflect.ValueOf(dest)).Interface(),
}
}