使用 Golang 构建一个简单的 TCP 服务

使用 Golang 构建一个简单的 TCP 服务

Golang 原生的协程使其在处理高并发方面很有优势,下面使用 Go 来简单实现个支持并发的 TCP 服务器

首先实现客户端

首先定义针对客户端的结构体,包含连接、地址和一个用来接收停止信号的 chan

type client struct {
	connection *net.Conn
	addr       *net.TCPAddr
	stopChan   chan struct{}
}

然后为其定义数据处理方法,handler

func (c *client) handler() {
	defer c.connection.Close()
	//根据连接初始化 reader
	reader := bufio.NewReader(c.connection)
	//阻塞,读取数据
	for {
		buf := make([]byte, 1024)
		//n 为读取到的数据长度
		n, err := reader.Read(buf)
		if err != nil {
			if err == io.EOF {
				//如果是 EOF 代表已经失去客户端连接,在这里直接释放这个 client
				close(c.stopChan)
				break
			}
			golog.Errorf("read error: %v", err)
			continue
		}
		bytes := buf[:n]
		golog.Infof("recv: %s", bytes)
		//直接将发送过来的数据回写
		_, err = c.connection.Write(bytes)
		if err != nil {
			golog.Errorf("write error: %v", err)
			continue
		}
	}
}

完整代码如下

package main

import (
	"bufio"
	"io"
	"net"

	"github.com/kataras/golog"
)

type client struct {
	connection *net.TCPConn
	addr       *net.TCPAddr
	stopChan   chan struct{}
}

func (c *client) handler() {
	defer c.connection.Close()
	//根据连接初始化 reader
	reader := bufio.NewReader(c.connection)
	//阻塞,读取数据
	for {
		buf := make([]byte, 1024)
		//n 为读取到的数据长度
		n, err := reader.Read(buf)
		if err != nil {
			if err == io.EOF {
				//如果是 EOF 代表连接断开,在这里直接释放这个 client
				close(c.stopChan)
				break
			}
			golog.Errorf("read error: %v", err)
			continue
		}
		bytes := buf[:n]
		golog.Infof("recv: %s", bytes)
		//直接将发送过来的数据回写
		_, err = c.connection.Write(bytes)
		if err != nil {
			golog.Errorf("write error: %v", err)
			continue
		}
	}
}

func main() {
	//建立 TCP 连接
	addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8000")
	if err != nil {
		golog.Fatalf("resolve addr error: %v", err)
	}

	conn, err := net.DialTCP("tcp", nil, addr)
	if err != nil {
		golog.Fatalf("dial tcp error: %v", err)
	}

	client := &client{
		connection: conn,
		addr:       addr,
		stopChan:   make(chan struct{}),
	}

	//开启协程处理
	go client.handler()

	//开启 channel 等待,当 chan 被关闭时退出
	<-client.stopChan
}

实现服务端

封装服务端结构体,包含 listener 和 addr

type server struct {
	listener *net.TCPListener
	addr     *net.TCPAddr
}

定义连接处理方法

//处理方法
func handle(conn net.Conn) {
	defer conn.Close()
	golog.Infof("new client: %s", conn.RemoteAddr().String())
	reader := bufio.NewReader(conn)
	for {
		buf := make([]byte, 1024)
		n, err := reader.Read(buf)
		if err != nil {
			if err == io.EOF {
				golog.Errorf("client closed: %s", conn.RemoteAddr().String())
				return
			}
			continue
		}
		//回写
		conn.Write(buf[:n])
	}
}

建立连接,完整代码如下

package main

import (
	"bufio"
	"io"
	"net"

	"github.com/kataras/golog"
)

type server struct {
	listener *net.TCPListener
	addr     *net.TCPAddr
}

//处理方法
func handle(conn net.Conn) {
	//此处可定义状态机,处理连接的不同状态和数据包结构
	defer conn.Close()
	golog.Infof("new client: %s", conn.RemoteAddr().String())
	reader := bufio.NewReader(conn)
	for {
		buf := make([]byte, 1024)
		n, err := reader.Read(buf)
		if err != nil {
			if err == io.EOF {
				golog.Errorf("client closed: %s", conn.RemoteAddr().String())
				return
			}
			continue
		}
		//回写
		conn.Write(buf[:n])
	}
}

func main() {
	//建立连接
	addr, err := net.ResolveTCPAddr("tcp", ":8000")
	if err != nil {
		golog.Fatalf("resolve addr error: %v", err)
	}
	listener, err := net.ListenTCP("tcp", addr)
	if err != nil {
		golog.Fatalf("listen error: %v", err)
	}
	defer listener.Close()

	//构造 server 对象
	s := &server{
		listener: listener,
		addr:     addr,
	}

	for {
		conn, err := s.listener.Accept()
		if err != nil {
			continue
		}
		//进入协程处理该连接
		go handle(conn)
	}
}