[Zinx tutorial catalogue]
Zinx source code: https://github.com/aceld/zinx
Zinx Develops API Documents
Zinx Chapter 1 - Introduction
Chapter 2 of Zinx - First Understanding of Zinx Framework
Zinx Chapter 3 - Basic Routing Module
Zinx Chapter 4 - Global Configuration
Zinx Chapter 5 - Message Encapsulation
Zinx Chapter 6 - Multi-Routing Patterns
Zinx Chapter VII: A Reading-Writing Separation Model
Chapter 8 of Zinx - Message Queuing and Multitasks
Chapter 9 of Zinx - Link Management
Chapter 10 of Zinx - Connection Property Settings
[In the serials... ]
[Zinx application case-MMO multiplayer online game]
(1) Case introduction
(2)AOI Interest Point Algorithms
(3) protocol buffer of data transmission protocol
(4)Proto3 Protocol Definition
(5) Building projects and putting users online
(6) World Chat
(7) On-line location information synchronization
(8) Mobile Location and AOI Broadcasting
(9) Players offline
[In the serials... ]
Second, First Understanding of Zinx Framework
Here's a look at Zinx's simplest Server prototype.
1. Zinx-V0.1-Basic Server
In order to better see the Zinx framework, Zinx first builds Zinx's two most basic modules, ziface and znet.
Ziface is mainly an abstract layer interface class that stores all modules of Zinx framework. The most basic of Zinx framework is the service class interface iserver, which is defined in ziface module.
znet module is the realization of network-related functions in zinx framework. All network-related modules are defined in znet module.
1.1 Zinx-V0.1 Code Implementation
A Create zinx framework
Create zinx folder under $GOPATH/src
B Create ziface and znet modules
Create ziface and znet folders under zinx/, so that the current file path is as follows:
└── zinx ├── ziface │ └── └── znet ├──
C creates the service module abstraction layer iserver.go under ziface.
zinx/ziface/iserver.go
package ziface //Define Server Interface type IServer interface{ //Start Server Method Start() //Stop Server Method Stop() //Opening Business Service Method Serve() }
D implements service module server.go under znet
package znet import ( "fmt" "net" "time" "zinx/ziface" ) //iServer interface implementation, defining a Server service class type Server struct { //The name of the server Name string //tcp4 or other IPVersion string //IP address of service binding IP string //Ports for service binding Port int } //============== Implementation of all interface methods in ziface.IServer======== //Open Network Services func (s *Server) Start() { fmt.Printf("[START] Server listenner at IP: %s, Port %d, is starting\n", s.IP, s.Port) //Open a go to do service-side Linster business go func() { //1 Get a TCP Adder addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port)) if err != nil { fmt.Println("resolve tcp addr err: ", err) return } //2 Monitor Server Address listenner, err:= net.ListenTCP(s.IPVersion, addr) if err != nil { fmt.Println("listen", s.IPVersion, "err", err) return } //Has been monitored successfully fmt.Println("start Zinx server ", s.Name, " succ, now listenning...") //3 Start server Network Connection Service for { //3.1 Blocking Waiting for Client to Establish Connection Request conn, err := listenner.AcceptTCP() if err != nil { fmt.Println("Accept err ", err) continue } //3.2 TODO Server.Start() sets the server's maximum connection control and closes the new connection if it exceeds the maximum connection. //3.3 TODO Server.Start() Business method for handling the new connection request, at which point handler and conn should be bound //Let's do a maximum 512 bytes echo service for the time being. go func () { //Continuous loops to retrieve data from the client for { buf := make([]byte, 512) cnt, err := conn.Read(buf) if err != nil { fmt.Println("recv buf err ", err) continue } //Echo display if _, err := conn.Write(buf[:cnt]); err !=nil { fmt.Println("write back buf err ", err) continue } } }() } }() } func (s *Server) Stop() { fmt.Println("[STOP] Zinx server , name " , s.Name) //TODO Server. Stop () also stops or cleans up other connection information or other information that needs to be cleaned up. } func (s *Server) Serve() { s.Start() //Does TODO Server.Serve() have to deal with other things when starting the service? //Block, otherwise the main Go will quit and listener's go will quit for { time.Sleep(10*time.Second) } } /* Create a server handle */ func NewServer (name string) ziface.IServer { s:= &Server { Name :name, IPVersion:"tcp4", IP:"0.0.0.0", Port:7777, } return s }
Well, we have completed the basic prototype of Zinx-V0.1, although it is only a basic write-back client data (we will customize the client business method later), then we should test whether our current zinx-V0.1 can be used.
1.2 Zinx Framework Unit Test Sample
In theory, we should be able to import the zinx framework now, then write a server program, and then write a client program for testing, but we can do unit testing through the unit Test function of Go.
Create zinx/znet/server_test.go
package znet import ( "fmt" "net" "testing" "time" ) /* Analog Client */ func ClientTest() { fmt.Println("Client Test ... start") //Three seconds later, test requests are initiated to give the server the opportunity to open the service time.Sleep(3 * time.Second) conn,err := net.Dial("tcp", "127.0.0.1:7777") if err != nil { fmt.Println("client start err, exit!") return } for { _, err := conn.Write([]byte("hello ZINX")) if err !=nil { fmt.Println("write error err ", err) return } buf :=make([]byte, 512) cnt, err := conn.Read(buf) if err != nil { fmt.Println("read buf error ") return } fmt.Printf(" server call back : %s, cnt = %d\n", buf, cnt) time.Sleep(1*time.Second) } } //Test function of Server module func TestServer(t *testing.T) { /* Server-side testing */ //1 Create a server handle s s := NewServer("[zinx V0.1]") /* Client Testing */ go ClientTest() //2 Open Services s.Serve() }
Execution under zinx/znet
$ go test
The results of implementation are as follows:
[START] Server listenner at IP: 0.0.0.0, Port 7777, is starting Client Test ... start listen tcp4 err listen tcp4 0.0.0.0:7777: bind: address already in use server call back : hello ZINX, cnt = 6 server call back : hello ZINX, cnt = 6 server call back : hello ZINX, cnt = 6 server call back : hello ZINX, cnt = 6
Explain that our zinx framework is already available.
1.3 Use Zinx-V0.1 to complete the application
Of course, if go test feels like a hassle, then we can write two applications based entirely on zinx, Server. go, Client. go.
Server.go
package main import ( "zinx/znet" ) //Test function of Server module func main() { //1 Create a server handle s s := znet.NewServer("[zinx V0.1]") //2 Open Services s.Serve() }
Start Server.go
go run Server.go
Client.go
package main import ( "fmt" "net" "time" ) func main() { fmt.Println("Client Test ... start") //Three seconds later, test requests are initiated to give the server the opportunity to open the service time.Sleep(3 * time.Second) conn,err := net.Dial("tcp", "127.0.0.1:7777") if err != nil { fmt.Println("client start err, exit!") return } for { _, err := conn.Write([]byte("hahaha")) if err !=nil { fmt.Println("write error err ", err) return } buf :=make([]byte, 512) cnt, err := conn.Read(buf) if err != nil { fmt.Println("read buf error ") return } fmt.Printf(" server call back : %s, cnt = %d\n", buf, cnt) time.Sleep(1*time.Second) } }
Start Client.go for testing
go run Client.go
2.Zinx-V0.2-Simple Connection Encapsulation and Business Binding
Version V0.1 has already implemented a basic Server framework. Now we need to encapsulate the different services processed by client links and different client links. Of course, we need to build the architecture first.
Now create an interface file iconnection.go under ziface, which belongs to the link. Of course, we put its implementation file in connection.go under znet.
2.1 Zinx-V0.2 Code Implementation
A ziface creates iconnection.go
zinx/ziface/iconnection.go
package ziface import "net" //Define the connection interface type IConnection interface { //Start the connection to make the current connection work Start() //Stop Connection, End Current Connection State M Stop() //Get the original socket TCPConn from the current connection GetTCPConnection() *net.TCPConn //Get the current connection ID GetConnID() uint32 //Get the address information of the remote client RemoteAddr() net.Addr //Send data directly to remote TCP clients Send(data []byte) error //Send the data to the buffer queue and write it to the client through go, which reads the data from the buffer queue. SendBuff(data []byte) error } //Define a unified interface for handling linked services type HandFunc func(*net.TCPConn, []byte, int) error
Some basic methods of this interface, code annotations have been introduced very clearly, here we briefly describe a HandFunc function type, which is the function interface of all conn links in dealing with business. The first parameter is the socket native link, the second parameter is the data requested by the client, and the third parameter is the data length requested by the client. In this way, if we want to specify a conn's processing business, we just need to define a function of HandFunc type and then bind to the link.
B znet Creates iconnection.go
zinx/znet/connection.go
package znet import ( "fmt" "net" "zinx/ziface" ) type Connection struct { //Currently connected socket TCP sockets Conn *net.TCPConn //The ID of the current connection can also be called Session ID, which is globally unique. ConnID uint32 //Closed state of current connection isClosed bool //Processing method api for this connection handleAPI ziface.HandFunc //Inform the channel that the link has exited/stopped ExitBuffChan chan bool } //Method of creating connections func NewConntion(conn *net.TCPConn, connID uint32, callback_api ziface.HandFunc) *Connection{ c := &Connection{ Conn: conn, ConnID: connID, isClosed: false, handleAPI: callback_api, ExitBuffChan: make(chan bool, 1), } return c } /* Goroutine Processing conn Reading Data */ func (c *Connection) StartReader() { fmt.Println("Reader Goroutine is running") defer fmt.Println(c.RemoteAddr().String(), " conn reader exit!") defer c.Stop() for { //Read our biggest data into buf buf := make([]byte, 512) cnt, err := c.Conn.Read(buf) if err != nil { fmt.Println("recv buf err ", err) c.ExitBuffChan <- true continue } //Call the current link business (where the current conn's bound handle method is executed) if err := c.handleAPI(c.Conn, buf, cnt); err !=nil { fmt.Println("connID ", c.ConnID, " handle is error") c.ExitBuffChan <- true return } } } //Start the connection to make the current connection work func (c *Connection) Start() { //Open the request service to process the link after reading the client data go c.StartReader() for { select { case <- c.ExitBuffChan: //Get the exit message, no more blocking return } } } //Stop Connection, End Current Connection State M func (c *Connection) Stop() { //1. If the current link is closed if c.isClosed == true { return } c.isClosed = true //TODO Connection Stop() If the user registers the shutdown callback service for the link, the call should be displayed at this point // Close socket links c.Conn.Close() //Notify the business that reads data from the buffer queue that the link has been closed c.ExitBuffChan <- true //Close all pipes of the link close(c.ExitBuffChan) } //Get the original socket TCPConn from the current connection func (c *Connection) GetTCPConnection() *net.TCPConn { return c.Conn } //Get the current connection ID func (c *Connection) GetConnID() uint32{ return c.ConnID } //Get the address information of the remote client func (c *Connection) RemoteAddr() net.Addr { return c.Conn.RemoteAddr() } //Send data directly to remote TCP clients func (c *Connection) Send(data []byte) error { return nil } //Send the data to the buffer queue and write it to the client through go, which reads the data from the buffer queue. func (c *Connection) SendBuff(data []byte) error { return nil }
C. Re-corrects the connection business for conn in Server.go
zinx/znet/server.go
package znet import ( "errors" "fmt" "net" "time" "zinx/ziface" ) //iServer interface implementation, defining a Server service class type Server struct { //The name of the server Name string //tcp4 or other IPVersion string //IP address of service binding IP string //Ports for service binding Port int } //============== Define handle api for the current client link=========== func CallBackToClient(conn *net.TCPConn, data []byte, cnt int) error { //Explicit business fmt.Println("[Conn Handle] CallBackToClient ... ") if _, err := conn.Write(data[:cnt]); err !=nil { fmt.Println("write back buf err ", err) return errors.New("CallBackToClient error") } return nil } //============== Implementation of all interface methods in ziface.IServer======== //Open Network Services func (s *Server) Start() { fmt.Printf("[START] Server listenner at IP: %s, Port %d, is starting\n", s.IP, s.Port) //Open a go to do service-side Linster business go func() { //1 Get a TCP Adder addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port)) if err != nil { fmt.Println("resolve tcp addr err: ", err) return } //2 Monitor Server Address listenner, err:= net.ListenTCP(s.IPVersion, addr) if err != nil { fmt.Println("listen", s.IPVersion, "err", err) return } //Has been monitored successfully fmt.Println("start Zinx server ", s.Name, " succ, now listenning...") //TODO server.go should have an automatic ID generation method var cid uint32 cid = 0 //3 Start server Network Connection Service for { //3.1 Blocking Waiting for Client to Establish Connection Request conn, err := listenner.AcceptTCP() if err != nil { fmt.Println("Accept err ", err) continue } //3.2 TODO Server.Start() sets the server's maximum connection control and closes the new connection if it exceeds the maximum connection. //3.3 Business methods for handling this new connection request, where handler and conn are bound dealConn := NewConntion(conn, cid, CallBackToClient) cid ++ //3.4 Start Processing Business for Current Links go dealConn.Start() } }() } func (s *Server) Stop() { fmt.Println("[STOP] Zinx server , name " , s.Name) //TODO Server. Stop () also stops or cleans up other connection information or other information that needs to be cleaned up. } func (s *Server) Serve() { s.Start() //Does TODO Server.Serve() have to deal with other things when starting the service? //Block, otherwise the main Go will quit and listener's go will quit for { time.Sleep(10*time.Second) } } /* Create a server handle */ func NewServer (name string) ziface.IServer { s:= &Server { Name :name, IPVersion:"tcp4", IP:"0.0.0.0", Port:7777, } return s }
CallBackToClient is our handle method to bind the conn object of the current client. Of course, it is now the echo service of the server-side compulsory binding. We will enrich the framework later so that the user can customize the specified handle.
In the start() method, we mainly make the following modifications:
//3.3 Business methods for handling this new connection request, where handler and conn are bound dealConn := NewConntion(conn, cid, CallBackToClient) cid ++ //3.4 Start Processing Business for Current Links go dealConn.Start()
Okay, now that we have bound connection and handle, let's test whether Zinx-V0.2 framework can be used.
2.2 Complete the application using Zinx-V0.2
In fact, the external interface of Zinx framework has not changed at present, so the test of V0.1 is still valid.
Server.go
package main import ( "zinx/znet" ) //Test function of Server module func main() { //1 Create a server handle s s := znet.NewServer("[zinx V0.1]") //2 Open Services s.Serve() }
Start Server.go
go run Server.go
Client.go
package main import ( "fmt" "net" "time" ) func main() { fmt.Println("Client Test ... start") //Three seconds later, test requests are initiated to give the server the opportunity to open the service time.Sleep(3 * time.Second) conn,err := net.Dial("tcp", "127.0.0.1:7777") if err != nil { fmt.Println("client start err, exit!") return } for { _, err := conn.Write([]byte("hahaha")) if err !=nil { fmt.Println("write error err ", err) return } buf :=make([]byte, 512) cnt, err := conn.Read(buf) if err != nil { fmt.Println("read buf error ") return } fmt.Printf(" server call back : %s, cnt = %d\n", buf, cnt) time.Sleep(1*time.Second) } }
Start Client.go for testing
go run Client.go
Now we've simply started the prototype of Zinx, but it's still a long way from our real framework. Next, let's improve the Zinx framework.
### About the author:
Author: Aceld (Liu Danbing)
Brief Book Number: IT No Cliff
mail: danbing.at@gmail.com
github: https://github.com/aceld
Original book gitbook: http://legacy.gitbook.com/@aceld
Original Statement: Do not reproduce without permission of the author, or reproduce, please indicate the source!