这里我们需要实现的功能有以下
在本章中,我们需要实现的修改Server方法中的,链接处理,修改一堆代码
go func() {
//获取一个TCP的Addr
addr,err := net.ResolveTCPAddr(this.IPVersion,fmt.Sprintf("%s:%d",this.IP,this.Port))
if err!=nil{
fmt.Println("resolve tcp addr error:",err)
}
//监听路由的地址
listen,err := net.ListenTCP(this.IPVersion,addr)
if err!=nil{
fmt.Println("Listen",this.IPVersion,"err",err)
return
}
fmt.Println("start zinx server succ",this.Name,"succ,Listening..")
var cid uint32
cid = 0
//阻塞的等待客户端的链接,处理业务
for {
conn,err := listen.AcceptTCP()
if err!=nil {
fmt.Println("Accept err",err)
continue
}
//客户端已经建立链接,做一些业务,因为比较简单,所以就回写吧
dealConn := NewConnection(conn,cid,CallBackToClient)
cid++
//启动当前业务
go dealConn.Start()
}
}()
如果所示,我们需要一个定义一下我们的Connection基本接口
package ziface
import "net"
//定义链接模块的抽象层
type IConnection interface {
//启动链接 让当前的链接业务开始工作
Start()
//停止链接
Stop()
//获取当前链接socket conn
GetTCPConnection() *net.TCPConn
//获取链接ID
GetConnID() uint32
//获取客户的TCP IP port
RemoteAddr() net.Addr
//发送数据,将数据发送远程的客户端
Send(data []byte)
}
//定义一个处理业务的方法
type HandleFunc func(*net.TCPConn,[]byte,int) error
和一个实现链接消息处理的对象
package znet
import (
"fmt"
"net"
"zinx/ziface"
)
/**
链接模块
*/
type Connection struct {
//当前链接Socket TCP
Conn *net.TCPConn
//链接的ID
ConnID uint32
//当前链接的状态
isClosed bool
//当前链接的处理方法
handleAPI ziface.HandleFunc
//退出channel
ExitChan chan bool
}
/**
初始化方法
*/
func NewConnection(conn *net.TCPConn,connID uint32,callback ziface.HandleFunc) *Connection {
c:= &Connection{
Conn:conn,
ConnID:connID,
handleAPI:callback,
isClosed:false,
ExitChan:make(chan bool,1),
}
return c
}
/**
链接的读业务方法
*/
func (c *Connection) StartReader() {
fmt.Println("Reader Goroutine is running...")
defer fmt.Println("c.connID=",c.ConnID," Reader is exit,remote addr is ",c.RemoteAddr().String())
defer c.Stop()
for {
//读取客户端的数据到buf中,最大512字节
buf :=make([]byte,512)
cnt,err:=c.Conn.Read(buf)
if err!=nil{
fmt.Println("recv buf err",err)
continue
}
//调用当前链接的所绑定的HandleApi
if err:= c.handleAPI(c.Conn,buf,cnt);err!=nil{
fmt.Println("ConnID",c.ConnID," handle is error",err)
break
}
}
}
//启动链接 让当前的链接业务开始工作
func (c *Connection) Start(){
fmt.Println("Conn Start() ... ConnID = ",c.ConnID)
//启动从当前链接的读取数据
go c.StartReader()
//TODO 启动从当前链接写数据的业务
}
//停止链接
func (c *Connection) Stop(){
//如果当前链接已经关闭
if c.isClosed == true {
return
}
c.isClosed = true
//关闭socket链接
c.Conn.Close()
//回收资源
close(c.ExitChan)
}
//获取当前链接socket conn
func (c *Connection) GetTCPConnection() *net.TCPConn{
return c.Conn
}
//获取链接ID
func (c *Connection) GetConnID() uint32{
return c.ConnID
}
//获取客户的TCP IP port
func (c *Connection) RemoteAddr() net.Addr{
return c.Conn.RemoteAddr()
}
//发送数据,将数据发送远程的客户端
func (c *Connection) Send(data []byte) error {
return nil
}
最后我们需要回到Server.go 中修改成以下代码
package znet
import (
"errors"
"fmt"
"net"
"zinx/ziface"
)
// IServer的接口实现,定义一个Server的服务器模块
type Server struct {
//服务器名称
Name string
//服务器ip版本
IPVersion string
//服务器监听ip
IP string
//端口
Port int
}
func (this *Server) Start() {
fmt.Printf("[Start] string Listenner at IP: %s,Prot %d,is start \n",this.IP,this.Port)
//可以不阻塞
go func() {
//获取一个TCP的Addr
addr,err := net.ResolveTCPAddr(this.IPVersion,fmt.Sprintf("%s:%d",this.IP,this.Port))
if err!=nil{
fmt.Println("resolve tcp addr error:",err)
}
//监听路由的地址
listen,err := net.ListenTCP(this.IPVersion,addr)
if err!=nil{
fmt.Println("Listen",this.IPVersion,"err",err)
return
}
fmt.Println("start zinx server succ",this.Name,"succ,Listening..")
var cid uint32
cid = 0
//阻塞的等待客户端的链接,处理业务
for {
conn,err := listen.AcceptTCP()
if err!=nil {
fmt.Println("Accept err",err)
continue
}
//客户端已经建立链接,做一些业务,因为比较简单,所以就回写吧
dealConn := NewConnection(conn,cid,CallBackToClient)
cid++
//启动当前业务
go dealConn.Start()
}
}()
}
func CallBackToClient (conn *net.TCPConn,data []byte,cnt int) error {
//回显的业务
fmt.Println("[Conn Handle] CallbackToClient...")
if _,err:=conn.Write(data[:cnt]);err!=nil{
return errors.New("CallBackToClient error")
}
return nil
}
func (this *Server) Stop() {
//TODO 将以西服务资源,回收
}
func (this *Server) Serve() {
//启动服务
this.Start()
//TODO 启动服务后做一些额外的业务功能
//阻塞状态
select {
}
}
/**
构造函数
*/
func NewServer(name string) ziface.IServer {
s:= &Server{
Name:name,
IPVersion:"tcp4",
IP:"0.0.0.0",
Port:8999,
}
return s
}