Golang实现redis系列-(2)基础的TCP

思路

redis服务器是tcp连接,这里我们先写一个基础的TCP服务器,而redis的hanler具体实现今后再写,这里仅实现了监听信号关闭服务,以及优雅的关闭(清理已经有的连接)

至于为什么只写了一个handler.Close(),是因为设计打算把所有的conn放在一个map里面,handler.Close()遍历map关闭所有连接

测试单元只是简单的写写,本篇博文重点是基础的TCP以及优雅的关闭

代码

package TCP

import (
	"Gotosp/pkg/log"
	"context"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

type Handler interface {
          
   
	Handle(ctx context.Context, conn net.Conn)
	Close()
}
type ServerConfig struct {
          
   
	Address string
}

func StartServer(cfg *ServerConfig, handler Handler) error {
          
   
	ctx, cancel := context.WithCancel(context.Background())
	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) //监听这些信号
	go func() {
          
   
		<-sigCh
		cancel() //收到退出信号,告诉下流开始退出
		signal.Stop(sigCh)
		close(sigCh)
	}()
	listener, err := net.Listen("tcp4", cfg.Address)
	if err != nil {
          
   
		return err
	}
	ListenAndServeWithSignal(ctx, listener, handler)
	return nil
}
func ListenAndServeWithSignal(ctx context.Context, listener net.Listener, handler Handler) {
          
   
	wg := sync.WaitGroup{
          
   }
	//收到退出通知,开始退出
	go func() {
          
   
		<-ctx.Done()
		listener.Close()
		handler.Close()
	}()
	//意外panic导致程序退出
	defer func() {
          
   
		listener.Close()
		handler.Close()
	}()

	for {
          
   
		conn, err := listener.Accept()
		if err != nil {
          
   
			log.Error("Accept err", err)
			break
		}
		wg.Add(1)
		go func() {
          
   
			defer func() {
          
   
				wg.Done()
			}()
			handler.Handle(ctx, conn)
		}()
	}
	wg.Wait()
}
package TCP

import (
	"Gotosp/pkg/log"
	"bufio"
	"context"
	"io"
	"net"
	"sync"
	"testing"
)

type echo struct {
          
   
	activeConn sync.Map
	conn       net.Conn
}

func (e *echo) Handle(ctx context.Context, conn net.Conn) {
          
   
	e.conn = conn
	e.activeConn.Store(e.conn, struct{
          
   }{
          
   })
	reader := bufio.NewReader(conn)
	for {
          
   
		msg, err := reader.ReadString(
)
		if err != nil {
          
   
			if err != io.EOF {
          
   
				log.Error("read err", err)
			}
			e.activeConn.Delete(e.conn)
			conn.Close()
			return
		}
		b := []byte(msg)
		conn.Write(b)
	}
}

func (e *echo) Close() {
          
   
	e.activeConn.Range(func(key interface{
          
   }, val interface{
          
   }) bool {
          
   
		client := key.(*echo)
		client.conn.Close()
		return true
	})
}
func MakeEcho() *echo {
          
   
	return &echo{
          
   }
}

func TestStartServer(t *testing.T) {
          
   
	cfg := &ServerConfig{
          
   Address: "127.0.0.1:6872"}
	err := StartServer(cfg, MakeEcho())
	if err != nil {
          
   
		log.Fatal("start server err", err)
	}
}
经验分享 程序员 微信小程序 职场和发展