GO語(yǔ)言實(shí)現(xiàn)TCP服務(wù)器的示例代碼
interface/tcp/Handler.go
type Handler interface { Handle(ctx context.Context, conn net.Conn) Close() error }
Handler:業(yè)務(wù)邏輯的處理接口
Handle(ctx context.Context, conn net.Conn) 處理連接
tcp/server.go
type Config struct { Address string } func ListenAndServeWithSignal(cfg *Config, handler tcp.Handler) error { closeChan := make(chan struct{}) listen, err := net.Listen("tcp", cfg.Address) if err != nil { return err } logger.Info("start listen") ListenAndServe(listen, handler, closeChan) return nil } func ListenAndServe(listener net.Listener, handler tcp.Handler, closeChan <-chan struct{}) { ctx := context.Background() var waitDone sync.WaitGroup for true { conn, err := listener.Accept() if err != nil { break } logger.Info("accept link") waitDone.Add(1) go func() { defer func() { waitDone.Done() }() handler.Handler(ctx, conn) }() } waitDone.Wait() }
Config:?jiǎn)?dòng)tcp服務(wù)器的配置
Address:監(jiān)聽(tīng)地址
ListenAndServe:ctx是上下文,可以傳遞一些參數(shù)。死循環(huán)中接收到新連接時(shí),讓一個(gè)協(xié)程去處理連接
如果listener.Accept()出錯(cuò)了就會(huì)break跳出來(lái),這時(shí)候需要等待已經(jīng)服務(wù)的客戶(hù)端退出。使用WaitGroup等待客服端退出
func ListenAndServe(listener net.Listener, handler tcp.Handler, closeChan <-chan struct{}) { go func() { <-closeChan logger.Info("shutting down...") _ = listener.Close() _ = handler.Close() }() defer func() { _ = listener.Close() _ = handler.Close() }() ...... }
listener和handler在退出的時(shí)候需要關(guān)掉。如果用戶(hù)直接kill掉了程序,我們也需要關(guān)掉listener和handler,這時(shí)候要使用closeChan,一旦接收到關(guān)閉信號(hào),就執(zhí)行關(guān)閉邏輯
func ListenAndServeWithSignal(cfg *Config, handler tcp.Handler) error { closeChan := make(chan struct{}) sigCh := make(chan os.Signal) signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) go func() { sig := <-sigCh switch sig { case syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: closeChan <- struct{}{} } }() listen, err := net.Listen("tcp", cfg.Address) if err != nil { return err } logger.Info("start listen") ListenAndServe(listen, handler, closeChan) return nil }
當(dāng)系統(tǒng)對(duì)程序發(fā)送信號(hào)時(shí),sigCh會(huì)接收到信號(hào)
tcp/echo.go
type EchoHandler struct { activeConn sync.Map closing atomic.Boolean }
EchoHandler:
- activeConn:記錄連接
- closing:是否正在關(guān)閉,有并發(fā)競(jìng)爭(zhēng),使用atomic.Boolean
type EchoClient struct { Conn net.Conn Waiting wait.Wait } func (c *EchoClient) Close() error { c.Waiting.WaitWithTimeout(10 * time.Second) _ = c.Conn.Close() return nil }
EchoClient:一個(gè)客戶(hù)端就是一個(gè)連接。Close方法關(guān)閉客戶(hù)端連接,超時(shí)時(shí)間設(shè)置為10s
func MakeHandler() *EchoHandler { return &EchoHandler{} } func (h *EchoHandler) Handle(ctx context.Context, conn net.Conn) { // 連接正在關(guān)閉,不接收新連接 if h.closing.Get() { _ = conn.Close() } client := &EchoClient{ Conn: conn, } h.activeConn.Store(client, struct{}{}) reader := bufio.NewReader(conn) for { msg, err := reader.ReadString('\n') if err != nil { if err == io.EOF { logger.Info("connection close") h.activeConn.Delete(client) } else { logger.Warn(err) } return } // 正在處理業(yè)務(wù),不要關(guān)掉 client.Waiting.Add(1) // 將數(shù)據(jù)原封不動(dòng)寫(xiě)回去,測(cè)試 b := []byte(msg) _, _ = conn.Write(b) client.Waiting.Done() } } func (h *EchoHandler) Close() error { logger.Info("handler shutting down...") h.closing.Set(true) h.activeConn.Range(func(key interface{}, val interface{}) bool { client := key.(*EchoClient) _ = client.Close() return true }) return nil }
MakeEchoHandler:創(chuàng)建EchoHandler
Handle:處理客戶(hù)端的連接。
1.連接正在關(guān)閉時(shí),不接收新連接
2.存儲(chǔ)新連接,value用空結(jié)構(gòu)體
3.使用緩存區(qū)接收用戶(hù)發(fā)來(lái)的數(shù)據(jù),使用\n作為結(jié)束的標(biāo)志
Close:將所有客戶(hù)端連接關(guān)掉
main.go
const configFile string = "redis.conf" var defaultProperties = &config.ServerProperties{ Bind: "0.0.0.0", Port: 6379, } func fileExists(filename string) bool { info, err := os.Stat(filename) return err == nil && !info.IsDir() } func main() { logger.Setup(&logger.Settings{ Path: "logs", Name: "godis", Ext: "log", TimeFormat: "2022-02-02", }) if fileExists(configFile) { config.SetupConfig(configFile) } else { config.Properties = defaultProperties } err := tcp.ListenAndServeWithSignal( &tcp.Config{ Address: fmt.Sprintf("%s:%d", config.Properties.Bind, config.Properties.Port), }, EchoHandler.MakeHandler()) if err != nil { logger.Error(err) } }
到此這篇關(guān)于GO語(yǔ)言實(shí)現(xiàn)TCP服務(wù)器的示例代碼的文章就介紹到這了,更多相關(guān)GO TCP服務(wù)器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語(yǔ)言實(shí)現(xiàn)順序存儲(chǔ)的棧
這篇文章主要介紹了go語(yǔ)言實(shí)現(xiàn)順序存儲(chǔ)的棧,實(shí)例分析了Go語(yǔ)言實(shí)現(xiàn)順序存儲(chǔ)的棧的原理與各種常見(jiàn)的操作技巧,需要的朋友可以參考下2015-03-03go-zero源碼閱讀之布隆過(guò)濾器實(shí)現(xiàn)代碼
布隆過(guò)濾器可以用于檢索一個(gè)元素是否在一個(gè)集合中。它的優(yōu)點(diǎn)是空間效率和查詢(xún)時(shí)間都比一般的算法要好的多,缺點(diǎn)是有一定的誤識(shí)別率和刪除困難,這篇文章主要介紹了go-zero源碼閱讀-布隆過(guò)濾器,需要的朋友可以參考下2023-02-02如何使用大學(xué)教育郵箱下載golang等軟件(推薦)
這篇文章主要介紹了如何使用大學(xué)教育郵箱下載goland等軟件,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09golang常用加密解密算法總結(jié)(AES、DES、RSA、Sha1、MD5)
在項(xiàng)目開(kāi)發(fā)過(guò)程中,當(dāng)操作一些用戶(hù)的隱私信息,本文主要主要介紹了golang常用加密解密算法總結(jié)(AES、DES、RSA、Sha1MD5),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04golang通過(guò)反射設(shè)置結(jié)構(gòu)體變量的值
這篇文章主要介紹了golang通過(guò)反射設(shè)置結(jié)構(gòu)體變量的值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04