github.com/rolandhe/saber@v0.0.4/nfour/simplex/srv.go (about)

     1  // rpc abstraction basing nfour
     2  // Copyright 2023 The saber Authors. All rights reserved.
     3  
     4  // Package simplex 单路模式的服务端实现,类似于http1.1, 大量客户端但每个客户请求较少的场景。每个连接上request/response是同步模式,每个请求必须得到响应以后才能发送另一个请求。
     5  // 用于兼容一些老的场景,因此该模式下没有提供客户端组件。
     6  package simplex
     7  
     8  import (
     9  	"github.com/rolandhe/saber/nfour"
    10  	"github.com/rolandhe/saber/utils/bytutil"
    11  	"net"
    12  	"strconv"
    13  	"time"
    14  )
    15  
    16  // Startup 启动一个单路服务端,在单路模式下,每个客户端连接对应服务端由一个goroutine服务,且服务是同步模式,每个请求必须被处理且收到响应后才能发送下一个请求,类似http1.1。
    17  //
    18  // port 服务监听的端口
    19  // conf.concurrent 指定了最大并发数
    20  func Startup(port int, conf *nfour.SrvConf) {
    21  	ln, err := net.Listen("tcp", ":"+strconv.Itoa(port))
    22  	if err != nil {
    23  		// handle error
    24  		nfour.NFourLogger.InfoLn(err)
    25  		return
    26  	}
    27  	nfour.NFourLogger.Info("listen tcp port %d,and next to accept\n", port)
    28  	for {
    29  		conn, err := ln.Accept()
    30  		if err != nil {
    31  			// handle error
    32  			nfour.NFourLogger.InfoLn(err)
    33  			return
    34  		}
    35  		go handleConnection(conn, conf)
    36  	}
    37  }
    38  
    39  func handleConnection(conn net.Conn, conf *nfour.SrvConf) {
    40  	nfour.NFourLogger.DebugLn("start to read header info...")
    41  	header := make([]byte, nfour.PayLoadLenBufLength)
    42  	for {
    43  		conn.SetReadDeadline(time.Now().Add(conf.IdleTimeout))
    44  		err := nfour.InternalReadPayload(conn, header, nfour.PayLoadLenBufLength, true)
    45  		if err != nil {
    46  			releaseConn(conn)
    47  			break
    48  		}
    49  		l, _ := bytutil.ToInt32(header[:nfour.PayLoadLenBufLength])
    50  		bodyBuff := make([]byte, l, l)
    51  		conn.SetReadDeadline(time.Now().Add(conf.ReadTimeout))
    52  		err = nfour.InternalReadPayload(conn, bodyBuff, int(l), false)
    53  		if err != nil {
    54  			releaseConn(conn)
    55  			break
    56  		}
    57  
    58  		if !conf.GetConcurrent().AcquireTimeout(conf.SemaWaitTime) {
    59  			if !writeCore(conf.ErrHandle(nfour.ExceedConcurrentError), conn, conf.WriteTimeout) {
    60  				releaseConn(conn)
    61  				break
    62  			}
    63  			continue
    64  		}
    65  		ok := doBiz(bodyBuff, conn, conf)
    66  		conf.GetConcurrent().Release()
    67  		if !ok {
    68  			releaseConn(conn)
    69  			break
    70  		}
    71  	}
    72  }
    73  
    74  func doBiz(bodyBuff []byte, conn net.Conn, conf *nfour.SrvConf) bool {
    75  	task := &nfour.Task{PayLoad: bodyBuff}
    76  	resBody, err := conf.Working(task)
    77  
    78  	if err != nil {
    79  		resBody = conf.ErrHandle(err)
    80  	}
    81  	return writeCore(resBody, conn, conf.WriteTimeout)
    82  }
    83  
    84  func releaseConn(conn net.Conn) {
    85  	if err := conn.Close(); err != nil {
    86  		nfour.NFourLogger.InfoLn(err)
    87  	}
    88  }
    89  
    90  func writeCore(res []byte, conn net.Conn, timeout time.Duration) bool {
    91  	conn.SetWriteDeadline(time.Now().Add(timeout))
    92  
    93  	plen := len(res)
    94  	payload := make([]byte, plen+nfour.PayLoadLenBufLength)
    95  	copy(payload, bytutil.Int32ToBytes(int32(plen)))
    96  	copy(payload[nfour.PayLoadLenBufLength:], res)
    97  
    98  	n, err := conn.Write(payload)
    99  	if err != nil {
   100  		conn.Close()
   101  		nfour.NFourLogger.InfoLn(err)
   102  		return false
   103  	}
   104  	nfour.NFourLogger.Debug("write data:%d, expect:%d\n", n, plen+nfour.PayLoadLenBufLength)
   105  	return true
   106  }