[Go language] A network protocol processing framework for support multiplexing of online games servers

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed. Introduction:This paper describes a network protocol processing framework which is implemented in go language, which is suitable for the go language concurrency model, and provides the framework code implementation. The author uses this framework for protocol processing in online game servers, but it can also be used in other areas.
Application background:In the design of network game server, it is common to encounter protocol multiplexing scenarios. For example, there are multiple TCP connections between the login server and the player client, and there is a 1:1 TCP connection between the login server and the game server 1:n. The approximate process for players to log in to the game is this:
    1. Player Connection Login Server
    2. The login server requests player data from the database
    3. The login server obtains the player data, forwards the player data to the game server for loading, including creating the player object, etc.
    4. After the login server obtains a successful load response, notifies the player that the client can enter the game world
In 3 and 4, because the login server and the game server usually have only one TCP connection, all player data is transmitted through this connection, so it is necessary to distinguish which player's data from the protocol package. This distinction can usually be based on the role name of the player, but it can be more generic and differentiated by a numeric ID, which separates the distribution processing of the protocol package from the content of the game logic in the protocol package.
Protocol Description:Usually online Games network protocol is the form of the message, even if the underlying is the use of TCP, there will be some way to split the data into a message (referred to herein as Protocol package). Therefore, this article is also based on this hypothesis, but for the specific protocol package format, this article does not have a special limitation, only requires that the protocol package can accommodate a 32-byte ID. The processing of the protocol package can be divided into the following two types. Other more complex sessions can be composed of the following two types.
    1. Send a packet and wait for a response. For example, the login server waits for the game server to load the player data notification.
    2. Send a packet, no response is required. For example, when the game server loads the player data, it sends the result notification to the login server.

Frame Description:The go language is a programming language that supports high concurrency, and it supports high concurrency in a number of lightweight goroutine concurrent executions. The operation in each goroutine is basically synchronous blocking, which can greatly simplify the program logic, make the code clear and easy to read and maintainable. Based on this, the calling interface of the framework implemented in this paper is also used in synchronous mode.
  1. If a protocol packet needs to wait for a response, it blocks the wait on the calling function. The signature of this call is:
    func (P *connection) Query (data[]byte) ([]byte, error)
    Note: The control of data is forwarded to the framework, so the contents of data cannot be modified after a function call.
  2. If sending a protocol package is a response to a protocol package that is received, it is called:
    func (P *connection) Reply (query, answer []byte) error
    Note: The control of theanswer is forwarded to the framework, so the contents of the answer cannot be modified after the function call.
  3. If a protocol package does not require a response, it calls the Send function directly:
    func (P *connection) Write (data []byte) error
    Note: The control of data is forwarded to the framework, so the contents of data cannot be modified after a function call.
  4. The interface that the caller needs to implement:
    • Socket. Used to send and receive protocol packets. is basically net. Tcpconn is a simple package that adds a protocol packet length to the head.
    • DataHandler. For protocol processing, a protocol package that is not returned by query is distributed to this interface for processing.
    • ErrorHandler. For error handling. This interface is called when a wire break occurs.
    • Identityhandler. Used to read and set the session ID.
5. Notes on the safety of Goroutine:
The function implementations of ErrorHandler and DataHandler cannot be called directly (*connection). Close, otherwise it will cause a deadlock.

export types, functions, and interfaces:
type Connectionfunc NewConnection(conn Socket, maxcount int, dh DataHandler, ih IdentityHandler, eh ErrorHandler) *Connectionfunc (p *Connection) Start()func (p *Connection) Close()func (p *Connection) Query(data []byte) (res []byte, err error)func (p *Connection) Reply(query, answer []byte) errorfunc (p *Connection) Write(data []byte) errortype Socket interface {Read() ([]byte, error)Write([]byte) errorClose()}type DataHandler interface {Process([]byte)}type ErrorHandler interface {OnError(error)}type IdentityHandler interface {GetIdentity([]byte) uint32SetIdentity([]byte, uint32)}
The complete code implementation:
Package Multiplexerimport ("Errors" "Sync" "Sync/atomic") var (err_exit = errors. New ("Exit")) type Socket interface {Read () ([]byte, error) Write ([]byte) Errorclose ()}type DataHandler interface {Process ([]byte)} Type ErrorHandler Interface {OnError (error)}type Identityhandler interface {getidentity ([]byte) uint32setidentity ([] BYTE, uint32)}type Connection struct {conn SOCKETWG sync. Waitgroupmutex sync. Mutexapplicants Map[uint32]chan []bytechexit Chan Boolchsend Chan []bytechch Chan Chan []bytedh Data]  Handlerih Identityhandlereh errorhandleridentity uint32}func newconnection (conn Socket, Maxcount int, dh DataHandler, ih identityhandler, eh ErrorHandler) *connection {count: = Maxcountif Count < 1024x768 {count = 1024}chch: = Make (Chan Chan []byte, Count] for I: = 0; I < count; i++ {chch <-make (chan []byte, 1)}return &connection{conn:conn,applicants:make (Map[uint32]chan []byte], Coun T), Chsend:make (Chan []byte, Count), Chexit:make (chan bool), Chch:chch,dh:dh,ih:ih,eh:eh,}}func (P *connection) Start () {P.wg.add (2) go func () {defer p.wg.done () P.recv ()} () go func () {defer p.wg.done () P.send ()} ()}func (P *connection) Close () {Close (P.chexit) p.conn.close () p.wg.wait ()}func (P *connection) Query (data []byte) (res []byte, err error) {var ch chan []b Yteselect {case <-p.chexit:return nil, err_exitcase ch = <-p.chch:defer func () {p.chch <-ch} ()}id: = P.newidenti Ty () p.ih.setidentity (data, id) p.addapplicant (ID, ch) defer func () {if err! = Nil {p.popapplicant (ID)}} () If Err: = P.write ( data); Err! = Nil {return nil, err}select {case <-p.chexit:return nil, err_exitcase res = <-ch:break}return res, nil}func ( P *connection) Reply (query, answer []byte) error {//put back the ' identity attached to the Queryid: = P.ih.getidentity (que RY) p.ih.setidentity (answer, ID) return P.write (Answer)}func (P *connection) Write (data []byte) error {select {case <-p . Chexit:return Err_exiTcase p.chsend <-data:break}return nil}func (P *connection) send () {for {select {case <-p.chexit:returncase data: = <-p.chsend:if p.conn.write (data)! = nil {return}}}}func (P *connection) recv () (err error) {defer func () {if err! = NI L {select {case <-p.chexit:err = Nildefault:p.eh.onerror (Err)}}} () for {select {case <-p.chexit:return Nildefault: Break}data, err: = P.conn.read () if err! = Nil {return err}if id: = p.ih.getidentity (data); ID > 0 {ch, OK: = p.popapplicant (id) if OK {ch <-datacontinue}}p.dh.process (data)}return Nil}func (P *connection) NE Widentity () UInt32 {return atomic. AddUint32 (&p.identity, 1)}func (P *connection) addapplicant (Identity UInt32, ch Chan []byte) {P.mutex.lock () defer P.mutex.unlock () p.applicants[identity] = Ch}func (P *connection) popapplicant (Identity UInt32) (Chan []byte, BOOL) { P.mutex.lock () defer p.mutex.unlock () ch, OK: = p.applicants[identity]if!ok {return nil, false}delete (p.applicants, Identity) return CH, true}
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.