Golang Socket

378 阅读1分钟

注意

  • 如果客户端一直向服务器发送数据,而服务器不接收的话,会有缓冲上限,达到上限会阻塞。
  • 服务器一直向客户端发送数据,而客户端不接收的话,会有缓冲上限,达到上限会阻塞。

粘包问题

  • 接收大小 < 发送数据大小 会出现粘包
  • 发送数据频率高会出现粘包

解决粘包

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(),
   }
}