├── .gitignore ├── .vscode └── settings.json ├── README.md ├── bridge ├── bridge.go └── bridge_test.go ├── build_i386.bat ├── build_i386_linux.bat ├── build_linux.bat ├── conf └── gfep.json ├── fep ├── gfep_376.go ├── gfep_698.go └── gfep_nw.go ├── gfep.go ├── go.mod ├── test ├── gterminal.go └── test.go ├── timewriter ├── timewriter.go └── timewriter_test.go ├── utils └── globalobj.go ├── ziface ├── iconnection.go ├── iconnmanager.go ├── imessage.go ├── imsghandler.go ├── iprotocol.go ├── irequest.go ├── irouter.go └── iserver.go ├── zlog ├── stdzlog.go ├── zlogger.go └── zlogger_test.go ├── znet ├── connection.go ├── connmanager.go ├── message.go ├── msghandler.go ├── request.go ├── router.go ├── server.go └── server_test.go └── zptl ├── ptl.go ├── ptl1376_1.go ├── ptl698_45.go ├── ptl_nw.go ├── ptl_nwm.go ├── ptl_test.go ├── ptlchk.go └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | log 2 | *.exe 3 | .idea 4 | gfep 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.testFlags": [ 3 | "-v", 4 | "-count=1" 5 | ] 6 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gfep 2 | 3 | Golang Fep Server for 698.45、376.1、... 4 | -------------------------------------------------------------------------------- /bridge/bridge.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "errors" 5 | "gfep/timewriter" 6 | "gfep/utils" 7 | "gfep/zptl" 8 | "log" 9 | "net" 10 | "runtime/debug" 11 | "sync" 12 | "sync/atomic" 13 | "time" 14 | ) 15 | 16 | var ( 17 | logBridge *log.Logger 18 | ) 19 | 20 | // init bridge 21 | func init() { 22 | logBridge = log.New(&timewriter.TimeWriter{ 23 | Dir: utils.GlobalObject.LogDir, 24 | Compress: true, 25 | ReserveDay: 30, 26 | ModuleName: "bridge", 27 | }, "", log.LstdFlags) 28 | } 29 | 30 | type connStatus int 31 | 32 | const ( 33 | _ connStatus = iota 34 | unConnect //未连接 35 | tcpConnectOK //已连接 36 | loginOK //已登录 37 | exitRequest //请求退出 38 | ) 39 | 40 | // SendMsgHandler 消息回调 41 | type SendMsgHandler func([]byte) 42 | 43 | // Conn 桥接信息 44 | type Conn struct { 45 | sync.Mutex 46 | host string // 桥接主站IP:Port 47 | addr []byte // 终端地址 48 | ptype uint32 // 协议类型 49 | heartCycle time.Duration // 心跳周期 50 | sendMsg SendMsgHandler // 消息回调 51 | cStatus connStatus // 当前在线状态 52 | loginTime time.Time // 登录时间 53 | heartTime time.Time // 心跳时间 54 | heartUnAckCnt int32 // 未确认心跳数量 55 | conn net.Conn // socket 56 | loginAckSig chan struct{} // 登录确认信号 57 | lock sync.Mutex // 登录确认信号锁 58 | isExitRequest bool //是否退出 59 | // chan 60 | } 61 | 62 | // NewConn 新建桥接连接 63 | func NewConn(host string, addr []byte, ptype uint32, heart time.Duration, sendMsg SendMsgHandler) *Conn { 64 | return &Conn{ 65 | host: host, 66 | addr: addr, 67 | ptype: ptype, 68 | heartCycle: heart, 69 | sendMsg: sendMsg, 70 | cStatus: unConnect, 71 | isExitRequest: false, 72 | } 73 | } 74 | 75 | // Start 启动桥接 76 | func (c *Conn) Start() { 77 | go c.serve() 78 | } 79 | 80 | // Stop 终止桥接 81 | func (c *Conn) Stop() { 82 | c.isExitRequest = true 83 | c.cStatus = exitRequest 84 | c.disConnectServer() 85 | } 86 | 87 | // Send 发送报文 88 | func (c *Conn) Send(buf []byte) error { 89 | if c.cStatus != loginOK { 90 | logBridge.Printf("BT(NotLOGIN): % X\n", buf) 91 | return errors.New("not login ok") 92 | } 93 | 94 | if c.conn != nil { 95 | _, err := c.conn.Write(buf) 96 | if err != nil { 97 | logBridge.Printf("BT(ERR): % X\n", buf) 98 | c.disConnectServer() 99 | return err 100 | } 101 | logBridge.Printf("BT: % X\n", buf) 102 | } else { 103 | logBridge.Printf("BT(LOST): % X\n", buf) 104 | } 105 | 106 | return nil 107 | } 108 | 109 | func (c *Conn) connectServer() error { 110 | c.disConnectServer() 111 | conn, err := net.Dial("tcp", c.host) 112 | if err != nil { 113 | return err 114 | } 115 | c.conn = conn 116 | c.cStatus = tcpConnectOK 117 | logBridge.Println("connect " + c.host + " OK") 118 | 119 | go c.recv() 120 | 121 | return nil 122 | } 123 | 124 | func (c *Conn) disConnectServer() { 125 | c.Lock() 126 | if c.conn != nil { 127 | c.logout() 128 | c.cStatus = unConnect 129 | if c.conn != nil { 130 | _ = c.conn.Close() 131 | c.conn = nil 132 | } 133 | c.loginTime = time.Time{} 134 | c.heartTime = time.Time{} 135 | } 136 | c.Unlock() 137 | } 138 | 139 | func (c *Conn) login() error { 140 | var p []byte 141 | 142 | if c.ptype&zptl.PTL_1376_1 != 0 { 143 | p = zptl.Ptl1376_1BuildPacket(0, c.addr) 144 | } else if c.ptype&zptl.PTL_698_45 != 0 { 145 | p = zptl.Ptl698_45BuildPacket(0, c.addr) 146 | } else if c.ptype&zptl.PTL_NW != 0 { 147 | p = zptl.PtlNwBuildPacket(0, c.addr) 148 | } 149 | if len(p) > 0 { 150 | _, err := c.conn.Write(p) 151 | if err != nil { 152 | logBridge.Printf("BL(ERR): % X\n", p) 153 | return err 154 | } 155 | logBridge.Printf("BL: % X\n", p) 156 | } 157 | 158 | c.lock.Lock() 159 | c.loginAckSig = make(chan struct{}) 160 | c.lock.Unlock() 161 | 162 | var err error 163 | select { 164 | case <-time.After(time.Second * 5): 165 | err = errors.New("timeout") 166 | case <-c.loginAckSig: 167 | c.cStatus = loginOK 168 | c.loginTime = time.Now() 169 | } 170 | 171 | c.lock.Lock() 172 | close(c.loginAckSig) 173 | c.loginAckSig = nil 174 | c.lock.Unlock() 175 | 176 | return err 177 | } 178 | 179 | func (c *Conn) heartbeat() error { 180 | var p []byte 181 | 182 | if c.conn == nil { 183 | return errors.New("conn lost") 184 | } 185 | 186 | if c.ptype&zptl.PTL_1376_1 != 0 { 187 | p = zptl.Ptl1376_1BuildPacket(1, c.addr) 188 | } else if c.ptype&zptl.PTL_698_45 != 0 { 189 | p = zptl.Ptl698_45BuildPacket(1, c.addr) 190 | } else if c.ptype&zptl.PTL_NW != 0 { 191 | p = zptl.PtlNwBuildPacket(1, c.addr) 192 | } 193 | if len(p) > 0 { 194 | _, err := c.conn.Write(p) 195 | if err != nil { 196 | logBridge.Printf("BH(ERR): % X\n", p) 197 | return err 198 | } 199 | logBridge.Printf("BH: % X\n", p) 200 | } 201 | return nil 202 | } 203 | 204 | // packetType 判断报文类型 205 | // @retval 0 : 其它报文 206 | // @retval 1 : 登录/心跳/退出确认帧 207 | func (c *Conn) packetType(ptype uint32, p []byte) uint8 { 208 | if ptype&zptl.PTL_1376_1 != 0 { 209 | return 0 210 | } else if ptype&zptl.PTL_698_45 != 0 { 211 | if p[3] == 0x01 { 212 | return 1 213 | } 214 | return 0 215 | } else if ptype&zptl.PTL_NW != 0 { 216 | return 0 217 | } 218 | return 0 219 | } 220 | 221 | func (c *Conn) logout() { 222 | if c.cStatus != loginOK { 223 | return 224 | } 225 | //todo: 发送退出帧 226 | } 227 | 228 | func cbRecvPacket(ptype uint32, data []byte, arg interface{}) { 229 | c, ok := arg.(*Conn) 230 | if ok { 231 | logBridge.Printf("BR: % X\n", data) 232 | //解析报文类型, 若为登录、心跳确认帧直接截获 233 | if c.packetType(ptype, data) == 1 { 234 | if c.cStatus == loginOK { 235 | c.heartTime = time.Now() 236 | atomic.StoreInt32(&c.heartUnAckCnt, 0) 237 | } else { 238 | if c.cStatus == tcpConnectOK { 239 | c.lock.Lock() 240 | if c.loginAckSig != nil { 241 | c.loginAckSig <- struct{}{} 242 | } 243 | c.lock.Unlock() 244 | } 245 | } 246 | } else { 247 | c.sendMsg(data) // 把数据发送给终端 248 | } 249 | } 250 | } 251 | 252 | func (c *Conn) recv() { 253 | defer func() { 254 | if p := recover(); p != nil { 255 | logBridge.Panicf("recv() painc recover! p: %v\n", p) 256 | debug.PrintStack() 257 | } 258 | }() 259 | ptlChk := zptl.NewChkfrm(c.ptype, 1000, cbRecvPacket, c) 260 | rbuf := make([]byte, 2048) 261 | 262 | for { 263 | rlen, err := c.conn.Read(rbuf) 264 | if err != nil { 265 | logBridge.Printf("read err: %s\n", err) 266 | c.disConnectServer() 267 | break 268 | } 269 | // logBridge.Printf("BRAW: % X\n", rbuf[0:rlen]) 270 | ptlChk.Chkfrm(rbuf[0:rlen]) 271 | } 272 | } 273 | 274 | func (c *Conn) serve() { 275 | errCntConnect := 0 //连接错误次数 276 | 277 | for { 278 | <-time.After(time.Second) 279 | 280 | // 1. tcp connect 281 | err := c.connectServer() 282 | if err != nil { 283 | if errCntConnect < 12 { //120s 284 | errCntConnect++ 285 | } 286 | <-time.After(time.Second * time.Duration(errCntConnect*10)) 287 | continue 288 | } 289 | errCntConnect = 0 290 | 291 | // 2. login 292 | err = c.login() 293 | if err != nil { 294 | c.disConnectServer() 295 | <-time.After(time.Second * 10) 296 | continue 297 | } 298 | logBridge.Printf("[% X]login ok", c.addr) 299 | 300 | // 3. hearbeat 301 | atomic.StoreInt32(&c.heartUnAckCnt, 0) 302 | for { 303 | sec := c.heartCycle / time.Second 304 | for ; sec > 0; sec -= 10 { 305 | <-time.After(time.Second * 10) 306 | if c.cStatus != loginOK || c.isExitRequest { 307 | break //掉线后立即重连 308 | } 309 | } 310 | if c.cStatus != loginOK || c.isExitRequest { 311 | break 312 | } 313 | if atomic.AddInt32(&c.heartUnAckCnt, 1) > 3 { 314 | break //3次心跳错误 315 | } 316 | _ = c.heartbeat() 317 | } 318 | 319 | //4. 断开连接 320 | c.disConnectServer() 321 | if c.isExitRequest { 322 | logBridge.Printf("[% X]exit", c.addr) 323 | return 324 | } 325 | logBridge.Printf("[% X]logout", c.addr) 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /bridge/bridge_test.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "fmt" 5 | "gfep/zptl" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func sendMsgHandler(buf []byte) { 11 | fmt.Printf("rx form app: % X\n", buf) 12 | } 13 | 14 | func Test698_tm(t *testing.T) { 15 | c := NewConn("127.0.0.1:8601", []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, zptl.PTL_698_45, time.Minute, sendMsgHandler) 16 | c.Start() 17 | <-time.After(time.Second * 10) 18 | c.Stop() 19 | } 20 | -------------------------------------------------------------------------------- /build_i386.bat: -------------------------------------------------------------------------------- 1 | SET GOARCH=386 2 | SET GOOS=windows 3 | @REM go build -ldflags "-s -w" 4 | go build -------------------------------------------------------------------------------- /build_i386_linux.bat: -------------------------------------------------------------------------------- 1 | SET GOARCH=386 2 | SET GOOS=linux 3 | @REM go build -ldflags "-s -w" 4 | go build -------------------------------------------------------------------------------- /build_linux.bat: -------------------------------------------------------------------------------- 1 | SET GOARCH=amd64 2 | SET GOOS=linux 3 | @REM go build -ldflags "-s -w" 4 | go build -------------------------------------------------------------------------------- /conf/gfep.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name":"gfep", 3 | "Host":"0.0.0.0", 4 | "TCPPort":20083, 5 | "BridgeHost698":"", 6 | "MaxConn":50000, 7 | "WorkerPoolSize":0, 8 | "LogDir": "./log", 9 | "LogFile":"", 10 | "LogDebugClose": true, 11 | "Timeout": 30, 12 | "SupportCompress": false, 13 | "SupportCas": false, 14 | "SupportCasLink": false, 15 | "SupportCommTermianl": true, 16 | "SupportReplyHeart": true, 17 | "SupportReplyReport": true 18 | } 19 | -------------------------------------------------------------------------------- /fep/gfep_376.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | "gfep/utils" 7 | "gfep/ziface" 8 | "gfep/zlog" 9 | "gfep/znet" 10 | "gfep/zptl" 11 | "os" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | "time" 16 | // _ "net/http/pprof" 17 | ) 18 | 19 | type addrConnID struct { 20 | addrStr string 21 | connID uint32 22 | } 23 | 24 | var ( 25 | appList *list.List 26 | appLock sync.RWMutex 27 | tmnList *list.List 28 | tmnLock sync.RWMutex 29 | ) 30 | 31 | const ( 32 | connIdle = 0 33 | connUnknow = 1 34 | connT698 = 2 35 | connT376 = 3 36 | connTNW = 4 37 | connA698 = 5 38 | connA376 = 6 39 | connANW = 7 40 | ) 41 | 42 | // Ptl1376_1Router 376规约路由 43 | type Ptl1376_1Router struct { 44 | znet.BaseRouter 45 | } 46 | 47 | // Handle 376报文处理方法 48 | func (r *Ptl1376_1Router) Handle(request ziface.IRequest) { 49 | conn := request.GetConnection() 50 | if conn.IsStop() { 51 | return 52 | } 53 | connStatus, err := conn.GetProperty("status") 54 | if err != nil { 55 | conn.NeedStop() 56 | return 57 | } 58 | rData := request.GetData() 59 | msaStr := strconv.Itoa(zptl.Ptl1376_1MsaGet(rData)) 60 | tmnStr := zptl.Ptl1376_1AddrStr(zptl.Ptl1376_1AddrGet(rData)) 61 | conn.SetProperty("rtime", time.Now()) //最近报文接收时间 62 | 63 | if zptl.Ptl1376_1GetDir(rData) == 0 { 64 | //from app 65 | if connStatus != connIdle && connStatus != connA376 { 66 | conn.NeedStop() 67 | return 68 | } 69 | conn.SetProperty("status", connA376) 70 | zlog.Debugf("A: % X\n", rData) 71 | isNewApp := true 72 | appLock.Lock() 73 | for e := appList.Front(); e != nil; e = e.Next() { 74 | a, ok := (e.Value).(addrConnID) 75 | if ok && a.connID == conn.GetConnID() { 76 | if a.addrStr != msaStr { 77 | appList.Remove(e) 78 | } else { 79 | isNewApp = false 80 | } 81 | break 82 | } 83 | } 84 | if isNewApp { 85 | appList.PushBack(addrConnID{msaStr, conn.GetConnID()}) 86 | conn.SetProperty("addr", msaStr) 87 | zlog.Debug("后台登录", msaStr, "connID", conn.GetConnID()) 88 | } 89 | appLock.Unlock() 90 | 91 | if zptl.Ptl1376_1GetFrameType(rData) == zptl.ONLINE { 92 | //todo: 处理app Online响应 93 | return 94 | } 95 | 96 | // zlog.Debug("后台登录", msaStr, "读取", tmnStr) 97 | //寻找匹配的终端连接,进行转发 98 | tmnLock.RLock() 99 | for e := tmnList.Front(); e != nil; e = e.Next() { 100 | a, ok := (e.Value).(addrConnID) 101 | //1. 终端地址匹配要转发 102 | //2. 广播/通配地址需要转发 103 | if ok && (a.addrStr == tmnStr || strings.HasSuffix(tmnStr, "AA")) { 104 | // zlog.Debug("后台", msaStr, "转发", tmnStr) 105 | go conn.SendMsgByConnID(a.connID, rData) 106 | } 107 | } 108 | tmnLock.RUnlock() 109 | } else { 110 | //from 终端 111 | if connStatus != connIdle && connStatus != connT376 { 112 | conn.NeedStop() 113 | return 114 | } 115 | conn.SetProperty("status", connT376) 116 | zlog.Debugf("T: % X\n", rData) 117 | switch zptl.Ptl1376_1GetFrameType(rData) { 118 | case zptl.LINK_LOGIN: 119 | if utils.GlobalObject.SupportCasLink { 120 | //todo: 处理级联终端登陆 121 | } 122 | 123 | preTmnStr, err := conn.GetProperty("addr") 124 | if err != nil || preTmnStr != tmnStr { 125 | isNewTmn := true 126 | tmnLock.Lock() 127 | if utils.GlobalObject.SupportCommTermianl != true { 128 | var next *list.Element 129 | for e := tmnList.Front(); e != nil; e = next { 130 | next = e.Next() 131 | a, ok := (e.Value).(addrConnID) 132 | //todo: 尝试比较级联终端 133 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 134 | isNewTmn = false 135 | break 136 | } 137 | if ok && a.addrStr == tmnStr && a.connID != conn.GetConnID() { 138 | zlog.Debug("终端重复登录", tmnStr, "删除", a.connID) 139 | //todo: 清除级联 140 | tmnList.Remove(e) 141 | } 142 | } 143 | } else { 144 | var next *list.Element 145 | for e := tmnList.Front(); e != nil; e = next { 146 | next = e.Next() 147 | a, ok := (e.Value).(addrConnID) 148 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 149 | isNewTmn = false 150 | break 151 | } 152 | if utils.GlobalObject.SupportCasLink != true { 153 | if ok && a.connID == conn.GetConnID() && a.addrStr != tmnStr { 154 | //todo: 有可能是联终端登录 155 | zlog.Debug("终端登录地址发生变更", tmnStr, "删除", a.connID) 156 | tmnList.Remove(e) 157 | } 158 | } 159 | } 160 | } 161 | if isNewTmn { 162 | tmnList.PushBack(addrConnID{tmnStr, conn.GetConnID()}) 163 | zlog.Debug("终端登录", tmnStr, "connID", conn.GetConnID()) 164 | } else { 165 | zlog.Debug("终端重新登录", tmnStr, "connID", conn.GetConnID()) 166 | } 167 | tmnLock.Unlock() 168 | } else { 169 | zlog.Debug("终端重新登录", tmnStr, "connID", conn.GetConnID()) 170 | } 171 | 172 | reply := make([]byte, 128, 128) 173 | len := zptl.Ptl1376_1BuildReplyPacket(rData, reply) 174 | err = conn.SendBuffMsg(reply[0:len]) 175 | if err != nil { 176 | zlog.Error(err) 177 | } else { 178 | conn.SetProperty("ltime", time.Now()) 179 | conn.SetProperty("addr", tmnStr) 180 | zlog.Debugf("L: % X\n", reply[0:len]) 181 | } 182 | return 183 | 184 | case zptl.LINK_HAERTBEAT: 185 | if utils.GlobalObject.SupportReplyHeart { 186 | if connStatus != connT376 { 187 | zlog.Error("终端未登录就发心跳", tmnStr) 188 | conn.NeedStop() 189 | } else { 190 | preTmnStr, err := conn.GetProperty("addr") 191 | if err == nil && preTmnStr == tmnStr { 192 | //todo: 级联心跳时, 需判断级联地址是否存在 193 | zlog.Debug("终端心跳", tmnStr) 194 | conn.SetProperty("htime", time.Now()) //更新心跳时间 195 | reply := make([]byte, 128, 128) 196 | len := zptl.Ptl1376_1BuildReplyPacket(rData, reply) 197 | err := conn.SendBuffMsg(reply[0:len]) 198 | if err != nil { 199 | zlog.Error(err) 200 | } else { 201 | zlog.Debugf("H: % X", reply[0:len]) 202 | } 203 | } else { 204 | zlog.Error("终端登录地址与心跳地址不匹配!", preTmnStr, tmnStr) 205 | conn.NeedStop() 206 | } 207 | } 208 | return 209 | } 210 | break 211 | 212 | case zptl.LINK_EXIT: 213 | if connStatus != connT376 { 214 | zlog.Error("终端未登录就想退出", tmnStr) 215 | } else { 216 | zlog.Debug("终端退出", tmnStr) 217 | reply := make([]byte, 128, 128) 218 | len := zptl.Ptl1376_1BuildReplyPacket(rData, reply) 219 | err := conn.SendMsg(reply[0:len]) 220 | if err != nil { 221 | zlog.Error(err) 222 | } 223 | } 224 | conn.NeedStop() 225 | return 226 | 227 | default: 228 | break 229 | } 230 | //寻找对应APP进行转发 231 | appLock.RLock() 232 | for e := appList.Front(); e != nil; e = e.Next() { 233 | a, ok := (e.Value).(addrConnID) 234 | //1. 终端主动上报msa==0,所有后台都转发 235 | //2. 后台msa为匹配要转发 236 | if ok && (msaStr == "0" || a.addrStr == msaStr) { 237 | go conn.SendMsgByConnID(a.connID, rData) 238 | } 239 | } 240 | appLock.RUnlock() 241 | } 242 | } 243 | 244 | // DoConnectionBegin 创建连接的时候执行 245 | func DoConnectionBegin(conn ziface.IConnection) { 246 | conn.SetProperty("status", connIdle) //默认状态 247 | conn.SetProperty("ctime", time.Now()) //连接时间 248 | } 249 | 250 | // DoConnectionLost 连接断开的时候执行 251 | func DoConnectionLost(conn ziface.IConnection) { 252 | connStatus, err := conn.GetProperty("status") 253 | if err != nil { 254 | panic("connStatus != err") 255 | } 256 | 257 | switch connStatus { 258 | case connT376: 259 | tmnLock.Lock() 260 | var next *list.Element 261 | for e := tmnList.Front(); e != nil; e = next { 262 | next = e.Next() 263 | a, ok := (e.Value).(addrConnID) 264 | if ok && a.connID == conn.GetConnID() { 265 | tmnList.Remove(e) 266 | if utils.GlobalObject.SupportCas != true { 267 | break 268 | } 269 | } 270 | } 271 | tmnLock.Unlock() 272 | break 273 | 274 | case connA376: 275 | appLock.Lock() 276 | var next *list.Element 277 | for e := appList.Front(); e != nil; e = next { 278 | next = e.Next() 279 | a, ok := (e.Value).(addrConnID) 280 | if ok && a.connID == conn.GetConnID() { 281 | appList.Remove(e) 282 | } 283 | } 284 | appLock.Unlock() 285 | break 286 | 287 | default: 288 | break 289 | } 290 | } 291 | 292 | func usrInput() { 293 | helper := `~~~~~~~~~~~~~~~~~~~ 294 | 1. 显示在线终端列表 295 | 2. 显示在线后台列表 296 | 3. 显示版本信息 297 | 4. 设置调试级别 298 | 5. 屏蔽心跳 299 | 6. 剔除终端 300 | 7. 尝试升级 301 | 8. 退出 302 | ~~~~~~~~~~~~~~~~~~~ 303 | :` 304 | var menu int 305 | 306 | for { 307 | menu = 0 308 | fmt.Scanln(&menu) 309 | fmt.Println("Hi you input is", menu) 310 | switch menu { 311 | case 1: 312 | tmnLock.RLock() 313 | var i int 314 | var next *list.Element 315 | for e := tmnList.Front(); e != nil; e = next { 316 | next = e.Next() 317 | t, ok := (e.Value).(addrConnID) 318 | if ok { 319 | fmt.Printf("%d %s, %d\n", i, t.addrStr, t.connID) 320 | i++ 321 | } 322 | } 323 | tmnLock.RUnlock() 324 | case 2: 325 | appLock.RLock() 326 | var i int 327 | var next *list.Element 328 | for e := appList.Front(); e != nil; e = next { 329 | next = e.Next() 330 | a, ok := (e.Value).(addrConnID) 331 | if ok { 332 | fmt.Printf("%d %s, %d\n", i, a.addrStr, a.connID) 333 | i++ 334 | } 335 | } 336 | appLock.RUnlock() 337 | case 4: 338 | fmt.Println("功能未实现!") 339 | case 5: 340 | fmt.Println("功能未实现!") 341 | case 6: 342 | fmt.Println("功能未实现!") 343 | case 7: 344 | fmt.Println("功能未实现!") 345 | case 8: 346 | os.Exit(0) 347 | } 348 | fmt.Printf(helper) 349 | } 350 | } 351 | 352 | func main() { 353 | // runtime.GOMAXPROCS(runtime.NumCPU()) 354 | 355 | // go func() { 356 | // log.Println(http.ListenAndServe("localhost:9999", nil)) 357 | // }() 358 | 359 | // zlog.SetLogFile("./log", "gfep.log") 360 | // zlog.OpenDebug() 361 | // zlog.ResetFlags(zlog.BitDefault | zlog.BitMicroSeconds) 362 | // zlog.CloseDebug() 363 | // go usrInput() 364 | 365 | appList = list.New() 366 | tmnList = list.New() 367 | 368 | //创建一个server句柄 369 | s := znet.NewServer() 370 | 371 | //注册链接hook回调函数 372 | s.SetOnConnStart(DoConnectionBegin) 373 | s.SetOnConnStop(DoConnectionLost) 374 | 375 | //配置路由 376 | s.AddRouter(zptl.PTL_1376_1, &Ptl1376_1Router{}) 377 | 378 | //开启服务 379 | s.Serve() 380 | } 381 | -------------------------------------------------------------------------------- /fep/gfep_698.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | "gfep/utils" 7 | "gfep/ziface" 8 | "gfep/zlog" 9 | "gfep/znet" 10 | "gfep/zptl" 11 | "os" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | "time" 16 | // _ "net/http/pprof" 17 | ) 18 | 19 | type addrConnID struct { 20 | addrStr string 21 | connID uint32 22 | } 23 | 24 | var ( 25 | appList *list.List 26 | appLock sync.RWMutex 27 | tmnList *list.List 28 | tmnLock sync.RWMutex 29 | ) 30 | 31 | const ( 32 | connIdle = 0 33 | connUnknow = 1 34 | connT698 = 2 35 | connT376 = 3 36 | connTNW = 4 37 | connA698 = 5 38 | connA376 = 6 39 | connANW = 7 40 | ) 41 | 42 | // PTL698_45Router 698规约路由 43 | type PTL698_45Router struct { 44 | znet.BaseRouter 45 | } 46 | 47 | // Handle 698报文处理方法 48 | func (r *PTL698_45Router) Handle(request ziface.IRequest) { 49 | conn := request.GetConnection() 50 | if conn.IsStop() { 51 | return 52 | } 53 | connStatus, err := conn.GetProperty("status") 54 | if err != nil { 55 | conn.NeedStop() 56 | return 57 | } 58 | rData := request.GetData() 59 | msaStr := strconv.Itoa(zptl.Ptl698_45MsaGet(rData)) 60 | tmnStr := zptl.Ptl698_45AddrStr(zptl.Ptl698_45AddrGet(rData)) 61 | conn.SetProperty("rtime", time.Now()) //最近报文接收时间 62 | 63 | if zptl.Ptl698_45GetDir(rData) == 0 { 64 | //from app 65 | if connStatus != connIdle && connStatus != connA698 { 66 | conn.NeedStop() 67 | return 68 | } 69 | conn.SetProperty("status", connA698) 70 | zlog.Debugf("A: % X\n", rData) 71 | isNewApp := true 72 | appLock.Lock() 73 | for e := appList.Front(); e != nil; e = e.Next() { 74 | a, ok := (e.Value).(addrConnID) 75 | if ok && a.connID == conn.GetConnID() { 76 | if a.addrStr != msaStr { 77 | appList.Remove(e) 78 | } else { 79 | isNewApp = false 80 | } 81 | break 82 | } 83 | } 84 | if isNewApp { 85 | appList.PushBack(addrConnID{msaStr, conn.GetConnID()}) 86 | conn.SetProperty("addr", msaStr) 87 | zlog.Debug("后台登录", msaStr, "connID", conn.GetConnID()) 88 | } 89 | appLock.Unlock() 90 | 91 | if zptl.Ptl698_45GetFrameType(rData) == zptl.ONLINE { 92 | //todo: 处理app Online响应 93 | return 94 | } 95 | 96 | // zlog.Debug("后台登录", msaStr, "读取", tmnStr) 97 | //寻找匹配的终端连接,进行转发 98 | tmnLock.RLock() 99 | for e := tmnList.Front(); e != nil; e = e.Next() { 100 | a, ok := (e.Value).(addrConnID) 101 | //1. 终端地址匹配要转发 102 | //2. 广播/通配地址需要转发 103 | if ok && (a.addrStr == tmnStr || strings.HasSuffix(tmnStr, "AA")) { 104 | // zlog.Debug("后台", msaStr, "转发", tmnStr) 105 | go conn.SendMsgByConnID(a.connID, rData) 106 | } 107 | } 108 | tmnLock.RUnlock() 109 | } else { 110 | //from 终端 111 | if connStatus != connIdle && connStatus != connT698 { 112 | conn.NeedStop() 113 | return 114 | } 115 | conn.SetProperty("status", connT698) 116 | zlog.Debugf("T: % X\n", rData) 117 | switch zptl.Ptl698_45GetFrameType(rData) { 118 | case zptl.LINK_LOGIN: 119 | if utils.GlobalObject.SupportCasLink { 120 | //todo: 处理级联终端登陆 121 | } 122 | 123 | preTmnStr, err := conn.GetProperty("addr") 124 | if err != nil || preTmnStr != tmnStr { 125 | isNewTmn := true 126 | tmnLock.Lock() 127 | if utils.GlobalObject.SupportCommTermianl != true { 128 | var next *list.Element 129 | for e := tmnList.Front(); e != nil; e = next { 130 | next = e.Next() 131 | a, ok := (e.Value).(addrConnID) 132 | //todo: 尝试比较级联终端 133 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 134 | isNewTmn = false 135 | break 136 | } 137 | if ok && a.addrStr == tmnStr && a.connID != conn.GetConnID() { 138 | zlog.Debug("终端重复登录", tmnStr, "删除", a.connID) 139 | //todo: 清除级联 140 | tmnList.Remove(e) 141 | } 142 | } 143 | } else { 144 | var next *list.Element 145 | for e := tmnList.Front(); e != nil; e = next { 146 | next = e.Next() 147 | a, ok := (e.Value).(addrConnID) 148 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 149 | isNewTmn = false 150 | break 151 | } 152 | if utils.GlobalObject.SupportCasLink != true { 153 | if ok && a.connID == conn.GetConnID() && a.addrStr != tmnStr { 154 | //todo: 有可能是联终端登录 155 | zlog.Debug("终端登录地址发生变更", tmnStr, "删除", a.connID) 156 | tmnList.Remove(e) 157 | } 158 | } 159 | } 160 | } 161 | if isNewTmn { 162 | tmnList.PushBack(addrConnID{tmnStr, conn.GetConnID()}) 163 | zlog.Debug("终端登录", tmnStr, "connID", conn.GetConnID()) 164 | } else { 165 | zlog.Debug("终端重新登录", tmnStr, "connID", conn.GetConnID()) 166 | } 167 | tmnLock.Unlock() 168 | } else { 169 | zlog.Debug("终端重新登录", tmnStr, "connID", conn.GetConnID()) 170 | } 171 | 172 | reply := make([]byte, 128, 128) 173 | len := zptl.Ptl698_45BuildReplyPacket(rData, reply) 174 | err = conn.SendBuffMsg(reply[0:len]) 175 | if err != nil { 176 | zlog.Error(err) 177 | } else { 178 | conn.SetProperty("ltime", time.Now()) 179 | conn.SetProperty("addr", tmnStr) 180 | zlog.Debugf("L: % X\n", reply[0:len]) 181 | } 182 | return 183 | 184 | case zptl.LINK_HAERTBEAT: 185 | if utils.GlobalObject.SupportReplyHeart { 186 | if connStatus != connT698 { 187 | zlog.Error("终端未登录就发心跳", tmnStr) 188 | conn.NeedStop() 189 | } else { 190 | preTmnStr, err := conn.GetProperty("addr") 191 | if err == nil && preTmnStr == tmnStr { 192 | //todo: 级联心跳时, 需判断级联地址是否存在 193 | zlog.Debug("终端心跳", tmnStr) 194 | conn.SetProperty("htime", time.Now()) //更新心跳时间 195 | reply := make([]byte, 128, 128) 196 | len := zptl.Ptl698_45BuildReplyPacket(rData, reply) 197 | err := conn.SendBuffMsg(reply[0:len]) 198 | if err != nil { 199 | zlog.Error(err) 200 | } else { 201 | zlog.Debugf("H: % X", reply[0:len]) 202 | } 203 | } else { 204 | zlog.Error("终端登录地址与心跳地址不匹配!", preTmnStr, tmnStr) 205 | conn.NeedStop() 206 | } 207 | } 208 | return 209 | } 210 | break 211 | 212 | case zptl.LINK_EXIT: 213 | if connStatus != connT698 { 214 | zlog.Error("终端未登录就想退出", tmnStr) 215 | } else { 216 | zlog.Debug("终端退出", tmnStr) 217 | reply := make([]byte, 128, 128) 218 | len := zptl.Ptl698_45BuildReplyPacket(rData, reply) 219 | err := conn.SendMsg(reply[0:len]) 220 | if err != nil { 221 | zlog.Error(err) 222 | } 223 | } 224 | conn.NeedStop() 225 | return 226 | 227 | default: 228 | break 229 | } 230 | //寻找对应APP进行转发 231 | appLock.RLock() 232 | for e := appList.Front(); e != nil; e = e.Next() { 233 | a, ok := (e.Value).(addrConnID) 234 | //1. 终端主动上报msa==0,所有后台都转发 235 | //2. 后台msa为匹配要转发 236 | if ok && (msaStr == "0" || a.addrStr == msaStr) { 237 | go conn.SendMsgByConnID(a.connID, rData) 238 | } 239 | } 240 | appLock.RUnlock() 241 | } 242 | } 243 | 244 | // DoConnectionBegin 创建连接的时候执行 245 | func DoConnectionBegin(conn ziface.IConnection) { 246 | conn.SetProperty("status", connIdle) //默认状态 247 | conn.SetProperty("ctime", time.Now()) //连接时间 248 | } 249 | 250 | // DoConnectionLost 连接断开的时候执行 251 | func DoConnectionLost(conn ziface.IConnection) { 252 | connStatus, err := conn.GetProperty("status") 253 | if err != nil { 254 | panic("connStatus != err") 255 | } 256 | 257 | switch connStatus { 258 | case connT698: 259 | tmnLock.Lock() 260 | var next *list.Element 261 | for e := tmnList.Front(); e != nil; e = next { 262 | next = e.Next() 263 | a, ok := (e.Value).(addrConnID) 264 | if ok && a.connID == conn.GetConnID() { 265 | tmnList.Remove(e) 266 | if utils.GlobalObject.SupportCas != true { 267 | break 268 | } 269 | } 270 | } 271 | tmnLock.Unlock() 272 | break 273 | 274 | case connA698: 275 | appLock.Lock() 276 | var next *list.Element 277 | for e := appList.Front(); e != nil; e = next { 278 | next = e.Next() 279 | a, ok := (e.Value).(addrConnID) 280 | if ok && a.connID == conn.GetConnID() { 281 | appList.Remove(e) 282 | } 283 | } 284 | appLock.Unlock() 285 | break 286 | 287 | default: 288 | break 289 | } 290 | } 291 | 292 | func usrInput() { 293 | helper := `~~~~~~~~~~~~~~~~~~~ 294 | 1. 显示在线终端列表 295 | 2. 显示在线后台列表 296 | 3. 显示版本信息 297 | 4. 设置调试级别 298 | 5. 屏蔽心跳 299 | 6. 剔除终端 300 | 7. 尝试升级 301 | 8. 退出 302 | ~~~~~~~~~~~~~~~~~~~ 303 | :` 304 | var menu int 305 | 306 | for { 307 | menu = 0 308 | fmt.Scanln(&menu) 309 | fmt.Println("Hi you input is", menu) 310 | switch menu { 311 | case 1: 312 | tmnLock.RLock() 313 | var i int 314 | var next *list.Element 315 | for e := tmnList.Front(); e != nil; e = next { 316 | next = e.Next() 317 | t, ok := (e.Value).(addrConnID) 318 | if ok { 319 | fmt.Printf("%d %s, %d\n", i, t.addrStr, t.connID) 320 | i++ 321 | } 322 | } 323 | tmnLock.RUnlock() 324 | case 2: 325 | appLock.RLock() 326 | var i int 327 | var next *list.Element 328 | for e := appList.Front(); e != nil; e = next { 329 | next = e.Next() 330 | a, ok := (e.Value).(addrConnID) 331 | if ok { 332 | fmt.Printf("%d %s, %d\n", i, a.addrStr, a.connID) 333 | i++ 334 | } 335 | } 336 | appLock.RUnlock() 337 | case 4: 338 | fmt.Println("功能未实现!") 339 | case 5: 340 | fmt.Println("功能未实现!") 341 | case 6: 342 | fmt.Println("功能未实现!") 343 | case 7: 344 | fmt.Println("功能未实现!") 345 | case 8: 346 | os.Exit(0) 347 | } 348 | fmt.Printf(helper) 349 | } 350 | } 351 | 352 | func main() { 353 | // runtime.GOMAXPROCS(runtime.NumCPU()) 354 | 355 | // go func() { 356 | // log.Println(http.ListenAndServe("localhost:9999", nil)) 357 | // }() 358 | 359 | // zlog.SetLogFile("./log", "gfep.log") 360 | // zlog.OpenDebug() 361 | // zlog.ResetFlags(zlog.BitDefault | zlog.BitMicroSeconds) 362 | // zlog.CloseDebug() 363 | go usrInput() 364 | 365 | appList = list.New() 366 | tmnList = list.New() 367 | 368 | //创建一个server句柄 369 | s := znet.NewServer() 370 | 371 | //注册链接hook回调函数 372 | s.SetOnConnStart(DoConnectionBegin) 373 | s.SetOnConnStop(DoConnectionLost) 374 | 375 | //配置路由 376 | s.AddRouter(zptl.PTL_698_45, &PTL698_45Router{}) 377 | 378 | //开启服务 379 | s.Serve() 380 | } 381 | -------------------------------------------------------------------------------- /fep/gfep_nw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | "gfep/utils" 7 | "gfep/ziface" 8 | "gfep/zlog" 9 | "gfep/znet" 10 | "gfep/zptl" 11 | "os" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | "time" 16 | // _ "net/http/pprof" 17 | ) 18 | 19 | type addrConnID struct { 20 | addrStr string 21 | connID uint32 22 | } 23 | 24 | var ( 25 | appList *list.List 26 | appLock sync.RWMutex 27 | tmnList *list.List 28 | tmnLock sync.RWMutex 29 | ) 30 | 31 | const ( 32 | connIdle = 0 33 | connUnknow = 1 34 | connT698 = 2 35 | connT376 = 3 36 | connTNW = 4 37 | connA698 = 5 38 | connA376 = 6 39 | connANW = 7 40 | ) 41 | 42 | // PTLNWRouter NW规约路由 43 | type PTLNWRouter struct { 44 | znet.BaseRouter 45 | } 46 | 47 | // Handle nw报文处理方法 48 | func (r *PTLNWRouter) Handle(request ziface.IRequest) { 49 | conn := request.GetConnection() 50 | if conn.IsStop() { 51 | return 52 | } 53 | connStatus, err := conn.GetProperty("status") 54 | if err != nil { 55 | conn.NeedStop() 56 | return 57 | } 58 | rData := request.GetData() 59 | msaStr := strconv.Itoa(zptl.PtlNwMsaGet(rData)) 60 | tmnStr := zptl.PtlNwAddrStr(zptl.PtlNwAddrGet(rData)) 61 | conn.SetProperty("rtime", time.Now()) //最近报文接收时间 62 | 63 | if zptl.PtlNwGetDir(rData) == 0 { 64 | //from app 65 | if connStatus != connIdle && connStatus != connANW { 66 | conn.NeedStop() 67 | return 68 | } 69 | conn.SetProperty("status", connANW) 70 | zlog.Debugf("A: % X\n", rData) 71 | isNewApp := true 72 | appLock.Lock() 73 | for e := appList.Front(); e != nil; e = e.Next() { 74 | a, ok := (e.Value).(addrConnID) 75 | if ok && a.connID == conn.GetConnID() { 76 | if a.addrStr != msaStr { 77 | appList.Remove(e) 78 | } else { 79 | isNewApp = false 80 | } 81 | break 82 | } 83 | } 84 | if isNewApp { 85 | appList.PushBack(addrConnID{msaStr, conn.GetConnID()}) 86 | conn.SetProperty("addr", msaStr) 87 | zlog.Debug("后台登录", msaStr, "connID", conn.GetConnID()) 88 | } 89 | appLock.Unlock() 90 | 91 | if zptl.PtlNwGetFrameType(rData) == zptl.ONLINE { 92 | //todo: 处理app Online响应 93 | return 94 | } 95 | 96 | // zlog.Debug("后台登录", msaStr, "读取", tmnStr) 97 | //寻找匹配的终端连接,进行转发 98 | tmnLock.RLock() 99 | for e := tmnList.Front(); e != nil; e = e.Next() { 100 | a, ok := (e.Value).(addrConnID) 101 | //1. 终端地址匹配要转发 102 | //2. 广播/通配地址需要转发 103 | if ok && (a.addrStr == tmnStr || strings.HasSuffix(tmnStr, "FF")) { 104 | // zlog.Debug("后台", msaStr, "转发", tmnStr) 105 | go conn.SendMsgByConnID(a.connID, rData) 106 | } 107 | } 108 | tmnLock.RUnlock() 109 | } else { 110 | //from 终端 111 | if connStatus != connIdle && connStatus != connTNW { 112 | conn.NeedStop() 113 | return 114 | } 115 | conn.SetProperty("status", connTNW) 116 | zlog.Debugf("T: % X\n", rData) 117 | switch zptl.PtlNwGetFrameType(rData) { 118 | case zptl.LINK_LOGIN: 119 | if utils.GlobalObject.SupportCasLink { 120 | //todo: 处理级联终端登陆 121 | } 122 | 123 | preTmnStr, err := conn.GetProperty("addr") 124 | if err != nil || preTmnStr != tmnStr { 125 | isNewTmn := true 126 | tmnLock.Lock() 127 | if utils.GlobalObject.SupportCommTermianl != true { 128 | var next *list.Element 129 | for e := tmnList.Front(); e != nil; e = next { 130 | next = e.Next() 131 | a, ok := (e.Value).(addrConnID) 132 | //todo: 尝试比较级联终端 133 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 134 | isNewTmn = false 135 | break 136 | } 137 | if ok && a.addrStr == tmnStr && a.connID != conn.GetConnID() { 138 | zlog.Debug("终端重复登录", tmnStr, "删除", a.connID) 139 | //todo: 清除级联 140 | tmnList.Remove(e) 141 | } 142 | } 143 | } else { 144 | var next *list.Element 145 | for e := tmnList.Front(); e != nil; e = next { 146 | next = e.Next() 147 | a, ok := (e.Value).(addrConnID) 148 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 149 | isNewTmn = false 150 | break 151 | } 152 | if utils.GlobalObject.SupportCasLink != true { 153 | if ok && a.connID == conn.GetConnID() && a.addrStr != tmnStr { 154 | //todo: 有可能是联终端登录 155 | zlog.Debug("终端登录地址发生变更", tmnStr, "删除", a.connID) 156 | tmnList.Remove(e) 157 | } 158 | } 159 | } 160 | } 161 | if isNewTmn { 162 | tmnList.PushBack(addrConnID{tmnStr, conn.GetConnID()}) 163 | zlog.Debug("终端登录", tmnStr, "connID", conn.GetConnID()) 164 | } else { 165 | zlog.Debug("终端重新登录", tmnStr, "connID", conn.GetConnID()) 166 | } 167 | tmnLock.Unlock() 168 | } else { 169 | zlog.Debug("终端重新登录", tmnStr, "connID", conn.GetConnID()) 170 | } 171 | 172 | reply := make([]byte, 128, 128) 173 | len := zptl.PtlNwBuildReplyPacket(rData, reply) 174 | err = conn.SendBuffMsg(reply[0:len]) 175 | if err != nil { 176 | zlog.Error(err) 177 | } else { 178 | conn.SetProperty("ltime", time.Now()) 179 | conn.SetProperty("addr", tmnStr) 180 | zlog.Debugf("L: % X\n", reply[0:len]) 181 | } 182 | return 183 | 184 | case zptl.LINK_HAERTBEAT: 185 | if utils.GlobalObject.SupportReplyHeart { 186 | if connStatus != connTNW { 187 | zlog.Error("终端未登录就发心跳", tmnStr) 188 | conn.NeedStop() 189 | } else { 190 | preTmnStr, err := conn.GetProperty("addr") 191 | if err == nil && preTmnStr == tmnStr { 192 | //todo: 级联心跳时, 需判断级联地址是否存在 193 | zlog.Debug("终端心跳", tmnStr) 194 | conn.SetProperty("htime", time.Now()) //更新心跳时间 195 | reply := make([]byte, 128, 128) 196 | len := zptl.PtlNwBuildReplyPacket(rData, reply) 197 | err := conn.SendBuffMsg(reply[0:len]) 198 | if err != nil { 199 | zlog.Error(err) 200 | } else { 201 | zlog.Debugf("H: % X", reply[0:len]) 202 | } 203 | } else { 204 | zlog.Error("终端登录地址与心跳地址不匹配!", preTmnStr, tmnStr) 205 | conn.NeedStop() 206 | } 207 | } 208 | return 209 | } 210 | break 211 | 212 | case zptl.LINK_EXIT: 213 | if connStatus != connTNW { 214 | zlog.Error("终端未登录就想退出", tmnStr) 215 | } else { 216 | zlog.Debug("终端退出", tmnStr) 217 | reply := make([]byte, 128, 128) 218 | len := zptl.PtlNwBuildReplyPacket(rData, reply) 219 | err := conn.SendMsg(reply[0:len]) 220 | if err != nil { 221 | zlog.Error(err) 222 | } 223 | } 224 | conn.NeedStop() 225 | return 226 | 227 | default: 228 | break 229 | } 230 | //寻找对应APP进行转发 231 | appLock.RLock() 232 | for e := appList.Front(); e != nil; e = e.Next() { 233 | a, ok := (e.Value).(addrConnID) 234 | //1. 终端主动上报msa==0,所有后台都转发 235 | //2. 后台msa为匹配要转发 236 | if ok && (msaStr == "0" || a.addrStr == msaStr) { 237 | go conn.SendMsgByConnID(a.connID, rData) 238 | } 239 | } 240 | appLock.RUnlock() 241 | } 242 | } 243 | 244 | // DoConnectionBegin 创建连接的时候执行 245 | func DoConnectionBegin(conn ziface.IConnection) { 246 | conn.SetProperty("status", connIdle) //默认状态 247 | conn.SetProperty("ctime", time.Now()) //连接时间 248 | } 249 | 250 | // DoConnectionLost 连接断开的时候执行 251 | func DoConnectionLost(conn ziface.IConnection) { 252 | connStatus, err := conn.GetProperty("status") 253 | if err != nil { 254 | panic("connStatus != err") 255 | } 256 | 257 | switch connStatus { 258 | case connTNW: 259 | tmnLock.Lock() 260 | var next *list.Element 261 | for e := tmnList.Front(); e != nil; e = next { 262 | next = e.Next() 263 | a, ok := (e.Value).(addrConnID) 264 | if ok && a.connID == conn.GetConnID() { 265 | tmnList.Remove(e) 266 | if utils.GlobalObject.SupportCas != true { 267 | break 268 | } 269 | } 270 | } 271 | tmnLock.Unlock() 272 | break 273 | 274 | case connANW: 275 | appLock.Lock() 276 | var next *list.Element 277 | for e := appList.Front(); e != nil; e = next { 278 | next = e.Next() 279 | a, ok := (e.Value).(addrConnID) 280 | if ok && a.connID == conn.GetConnID() { 281 | appList.Remove(e) 282 | } 283 | } 284 | appLock.Unlock() 285 | break 286 | 287 | default: 288 | break 289 | } 290 | } 291 | 292 | func usrInput() { 293 | helper := `~~~~~~~~~~~~~~~~~~~ 294 | 1. 显示在线终端列表 295 | 2. 显示在线后台列表 296 | 3. 显示版本信息 297 | 4. 设置调试级别 298 | 5. 屏蔽心跳 299 | 6. 剔除终端 300 | 7. 尝试升级 301 | 8. 退出 302 | ~~~~~~~~~~~~~~~~~~~ 303 | :` 304 | var menu int 305 | 306 | for { 307 | menu = 0 308 | fmt.Scanln(&menu) 309 | fmt.Println("Hi you input is", menu) 310 | switch menu { 311 | case 1: 312 | tmnLock.RLock() 313 | var i int 314 | var next *list.Element 315 | for e := tmnList.Front(); e != nil; e = next { 316 | next = e.Next() 317 | t, ok := (e.Value).(addrConnID) 318 | if ok { 319 | fmt.Printf("%d %s, %d\n", i, t.addrStr, t.connID) 320 | i++ 321 | } 322 | } 323 | tmnLock.RUnlock() 324 | case 2: 325 | appLock.RLock() 326 | var i int 327 | var next *list.Element 328 | for e := appList.Front(); e != nil; e = next { 329 | next = e.Next() 330 | a, ok := (e.Value).(addrConnID) 331 | if ok { 332 | fmt.Printf("%d %s, %d\n", i, a.addrStr, a.connID) 333 | i++ 334 | } 335 | } 336 | appLock.RUnlock() 337 | case 4: 338 | fmt.Println("功能未实现!") 339 | case 5: 340 | fmt.Println("功能未实现!") 341 | case 6: 342 | fmt.Println("功能未实现!") 343 | case 7: 344 | fmt.Println("功能未实现!") 345 | case 8: 346 | os.Exit(0) 347 | } 348 | fmt.Printf(helper) 349 | } 350 | } 351 | 352 | func main() { 353 | // runtime.GOMAXPROCS(runtime.NumCPU()) 354 | 355 | // go func() { 356 | // log.Println(http.ListenAndServe("localhost:9999", nil)) 357 | // }() 358 | 359 | // zlog.SetLogFile("./log", "gfep.log") 360 | // zlog.OpenDebug() 361 | // zlog.ResetFlags(zlog.BitDefault | zlog.BitMicroSeconds) 362 | // zlog.CloseDebug() 363 | // go usrInput() 364 | 365 | appList = list.New() 366 | tmnList = list.New() 367 | 368 | //创建一个server句柄 369 | s := znet.NewServer() 370 | 371 | //注册链接hook回调函数 372 | s.SetOnConnStart(DoConnectionBegin) 373 | s.SetOnConnStop(DoConnectionLost) 374 | 375 | //配置路由 376 | s.AddRouter(zptl.PTL_NW, &PTLNWRouter{}) 377 | 378 | //开启服务 379 | s.Serve() 380 | } 381 | -------------------------------------------------------------------------------- /gfep.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | "gfep/bridge" 7 | "gfep/timewriter" 8 | "gfep/utils" 9 | "gfep/ziface" 10 | "gfep/znet" 11 | "gfep/zptl" 12 | "log" 13 | "os" 14 | "runtime" 15 | "strconv" 16 | "strings" 17 | "sync" 18 | "time" 19 | // _ "net/http/pprof" 20 | ) 21 | 22 | type addrConnID struct { 23 | addrStr string 24 | connID uint32 25 | } 26 | 27 | var ( 28 | app376List *list.List 29 | app376Lock sync.RWMutex 30 | tmn376List *list.List 31 | tmn376Lock sync.RWMutex 32 | 33 | app698List *list.List 34 | app698Lock sync.RWMutex 35 | tmn698List *list.List 36 | tmn698Lock sync.RWMutex 37 | 38 | appNwList *list.List 39 | appNwLock sync.RWMutex 40 | tmnNwList *list.List 41 | tmnNwLock sync.RWMutex 42 | 43 | log376 *log.Logger 44 | log698 *log.Logger 45 | logNw *log.Logger 46 | ) 47 | 48 | const ( 49 | connIdle = 0 50 | connUnknow = 1 51 | connT698 = 2 52 | connT376 = 3 53 | connTNW = 4 54 | connA698 = 5 55 | connA376 = 6 56 | connANW = 7 57 | ) 58 | 59 | // Ptl1376_1Router 376规约路由 60 | type Ptl1376_1Router struct { 61 | znet.BaseRouter 62 | } 63 | 64 | // Handle 376报文处理方法 65 | func (r *Ptl1376_1Router) Handle(request ziface.IRequest) { 66 | conn := request.GetConnection() 67 | if conn.IsStop() { 68 | return 69 | } 70 | connStatus, err := conn.GetProperty("status") 71 | if err != nil { 72 | conn.NeedStop() 73 | return 74 | } 75 | rData := request.GetData() 76 | msaStr := strconv.Itoa(zptl.Ptl1376_1MsaGet(rData)) 77 | tmnStr := zptl.Ptl1376_1AddrStr(zptl.Ptl1376_1AddrGet(rData)) 78 | conn.SetProperty("rtime", time.Now()) //最近报文接收时间 79 | 80 | if zptl.Ptl1376_1GetDir(rData) == 0 { 81 | //from app 82 | if connStatus != connIdle && connStatus != connA376 { 83 | conn.NeedStop() 84 | return 85 | } 86 | conn.SetProperty("status", connA376) 87 | log376.Printf("A: % X\n", rData) 88 | isNewApp := true 89 | app376Lock.Lock() 90 | for e := app376List.Front(); e != nil; e = e.Next() { 91 | a, ok := (e.Value).(addrConnID) 92 | if ok && a.connID == conn.GetConnID() { 93 | if a.addrStr != msaStr { 94 | app376List.Remove(e) 95 | } else { 96 | isNewApp = false 97 | } 98 | break 99 | } 100 | } 101 | if isNewApp { 102 | app376List.PushBack(addrConnID{msaStr, conn.GetConnID()}) 103 | conn.SetProperty("addr", msaStr) 104 | log376.Println("后台登录", msaStr, "connID", conn.GetConnID()) 105 | } 106 | app376Lock.Unlock() 107 | 108 | if zptl.Ptl1376_1GetFrameType(rData) == zptl.ONLINE { 109 | //todo: 处理app Online响应 110 | return 111 | } 112 | 113 | //寻找匹配的终端连接,进行转发 114 | tmn376Lock.RLock() 115 | for e := tmn376List.Front(); e != nil; e = e.Next() { 116 | a, ok := (e.Value).(addrConnID) 117 | //1. 终端地址匹配要转发 118 | //2. 广播/通配地址需要转发 119 | if ok && (a.addrStr == tmnStr || strings.HasSuffix(tmnStr, "FF")) { 120 | go conn.SendMsgByConnID(a.connID, rData) 121 | } 122 | } 123 | tmn376Lock.RUnlock() 124 | } else { 125 | //from 终端 126 | if connStatus != connIdle && connStatus != connT376 { 127 | conn.NeedStop() 128 | return 129 | } 130 | conn.SetProperty("status", connT376) 131 | log376.Printf("T: % X\n", rData) 132 | switch zptl.Ptl1376_1GetFrameType(rData) { 133 | case zptl.LINK_LOGIN: 134 | if utils.GlobalObject.SupportCasLink { 135 | //todo: 处理级联终端登陆 136 | } 137 | 138 | preTmnStr, err := conn.GetProperty("addr") 139 | if err != nil || preTmnStr != tmnStr { 140 | isNewTmn := true 141 | tmn376Lock.Lock() 142 | if !utils.GlobalObject.SupportCommTermianl { 143 | var next *list.Element 144 | for e := tmn376List.Front(); e != nil; e = next { 145 | next = e.Next() 146 | a, ok := (e.Value).(addrConnID) 147 | //todo: 尝试比较级联终端 148 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 149 | isNewTmn = false 150 | break 151 | } 152 | if ok && a.addrStr == tmnStr && a.connID != conn.GetConnID() { 153 | log376.Println("终端重复登录", tmnStr, "删除", a.connID) 154 | //todo: 清除级联 155 | tmn376List.Remove(e) 156 | } 157 | } 158 | } else { 159 | var next *list.Element 160 | for e := tmn376List.Front(); e != nil; e = next { 161 | next = e.Next() 162 | a, ok := (e.Value).(addrConnID) 163 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 164 | isNewTmn = false 165 | break 166 | } 167 | if !utils.GlobalObject.SupportCasLink { 168 | if ok && a.connID == conn.GetConnID() && a.addrStr != tmnStr { 169 | //todo: 有可能是联终端登录 170 | log376.Println("终端登录地址发生变更", tmnStr, "删除", a.connID) 171 | tmn376List.Remove(e) 172 | } 173 | } 174 | } 175 | } 176 | if isNewTmn { 177 | tmn376List.PushBack(addrConnID{tmnStr, conn.GetConnID()}) 178 | log376.Println("终端登录", tmnStr, "connID", conn.GetConnID()) 179 | } else { 180 | log376.Println("终端重新登录", tmnStr, "connID", conn.GetConnID()) 181 | } 182 | tmn376Lock.Unlock() 183 | } else { 184 | log376.Println("终端重新登录", tmnStr, "connID", conn.GetConnID()) 185 | } 186 | 187 | reply := make([]byte, 128) 188 | plen := zptl.Ptl1376_1BuildReplyPacket(rData, reply) 189 | err = conn.SendBuffMsg(reply[0:plen]) 190 | if err != nil { 191 | log376.Println(err) 192 | } else { 193 | conn.SetProperty("ltime", time.Now()) 194 | conn.SetProperty("addr", tmnStr) 195 | log376.Printf("L: % X\n", reply[0:plen]) 196 | } 197 | return 198 | 199 | case zptl.LINK_HAERTBEAT: 200 | if utils.GlobalObject.SupportReplyHeart { 201 | if connStatus != connT376 { 202 | log376.Println("终端未登录就发心跳", tmnStr) 203 | conn.NeedStop() 204 | } else { 205 | preTmnStr, err := conn.GetProperty("addr") 206 | if err == nil && preTmnStr == tmnStr { 207 | //todo: 级联心跳时, 需判断级联地址是否存在 208 | log376.Println("终端心跳", tmnStr) 209 | conn.SetProperty("htime", time.Now()) //更新心跳时间 210 | reply := make([]byte, 128) 211 | plen := zptl.Ptl1376_1BuildReplyPacket(rData, reply) 212 | err := conn.SendBuffMsg(reply[0:plen]) 213 | if err != nil { 214 | log376.Println(err) 215 | } else { 216 | log376.Printf("H: % X", reply[0:plen]) 217 | } 218 | } else { 219 | log376.Println("终端登录地址与心跳地址不匹配!", preTmnStr, tmnStr) 220 | conn.NeedStop() 221 | } 222 | } 223 | return 224 | } 225 | 226 | case zptl.LINK_EXIT: 227 | if connStatus != connT376 { 228 | log376.Println("终端未登录就想退出", tmnStr) 229 | } else { 230 | log376.Println("终端退出", tmnStr) 231 | reply := make([]byte, 128) 232 | plen := zptl.Ptl1376_1BuildReplyPacket(rData, reply) 233 | err := conn.SendMsg(reply[0:plen]) 234 | if err != nil { 235 | log376.Println(err) 236 | } 237 | } 238 | conn.NeedStop() 239 | return 240 | 241 | default: 242 | break 243 | } 244 | //寻找对应APP进行转发 245 | app376Lock.RLock() 246 | for e := app376List.Front(); e != nil; e = e.Next() { 247 | a, ok := (e.Value).(addrConnID) 248 | //1. 终端主动上报msa==0,所有后台都转发 249 | //2. 后台msa为匹配要转发 250 | if ok && (msaStr == "0" || a.addrStr == msaStr) { 251 | go conn.SendMsgByConnID(a.connID, rData) 252 | } 253 | } 254 | app376Lock.RUnlock() 255 | } 256 | } 257 | 258 | // PTL698_45Router 698规约路由 259 | type PTL698_45Router struct { 260 | znet.BaseRouter 261 | } 262 | 263 | // Handle 698报文处理方法 264 | func (r *PTL698_45Router) Handle(request ziface.IRequest) { 265 | conn := request.GetConnection() 266 | if conn.IsStop() { 267 | return 268 | } 269 | connStatus, err := conn.GetProperty("status") 270 | if err != nil { 271 | conn.NeedStop() 272 | return 273 | } 274 | rData := request.GetData() 275 | msaStr := strconv.Itoa(zptl.Ptl698_45MsaGet(rData)) 276 | tmnStr := zptl.Ptl698_45AddrStr(zptl.Ptl698_45AddrGet(rData)) 277 | conn.SetProperty("rtime", time.Now()) //最近报文接收时间 278 | 279 | if zptl.Ptl698_45GetDir(rData) == 0 { 280 | //from app 281 | if connStatus != connIdle && connStatus != connA698 { 282 | conn.NeedStop() 283 | return 284 | } 285 | conn.SetProperty("status", connA698) 286 | log698.Printf("A: % X\n", rData) 287 | isNewApp := true 288 | app698Lock.Lock() 289 | for e := app698List.Front(); e != nil; e = e.Next() { 290 | a, ok := (e.Value).(addrConnID) 291 | if ok && a.connID == conn.GetConnID() { 292 | if a.addrStr != msaStr { 293 | app698List.Remove(e) 294 | } else { 295 | isNewApp = false 296 | } 297 | break 298 | } 299 | } 300 | if isNewApp { 301 | app698List.PushBack(addrConnID{msaStr, conn.GetConnID()}) 302 | conn.SetProperty("addr", msaStr) 303 | log698.Println("后台登录", msaStr, "connID", conn.GetConnID()) 304 | } 305 | app698Lock.Unlock() 306 | 307 | if zptl.Ptl698_45GetFrameType(rData) == zptl.ONLINE { 308 | //todo: 处理app Online响应 309 | return 310 | } 311 | 312 | // log698.Println("后台登录", msaStr, "读取", tmnStr) 313 | //寻找匹配的终端连接,进行转发 314 | tmn698Lock.RLock() 315 | for e := tmn698List.Front(); e != nil; e = e.Next() { 316 | a, ok := (e.Value).(addrConnID) 317 | //1. 终端地址匹配要转发 318 | //2. 广播/通配地址需要转发 319 | if ok && (a.addrStr == tmnStr || strings.HasSuffix(tmnStr, "AA")) { 320 | // log698.Println("后台", msaStr, "转发", tmnStr) 321 | go conn.SendMsgByConnID(a.connID, rData) 322 | } 323 | } 324 | tmn698Lock.RUnlock() 325 | } else { 326 | //from 终端 327 | if connStatus != connIdle && connStatus != connT698 { 328 | conn.NeedStop() 329 | return 330 | } 331 | conn.SetProperty("status", connT698) 332 | log698.Printf("T: % X\n", rData) 333 | switch zptl.Ptl698_45GetFrameType(rData) { 334 | case zptl.LINK_LOGIN: 335 | if utils.GlobalObject.SupportCasLink { 336 | //todo: 处理级联终端登陆 337 | } 338 | 339 | preTmnStr, err := conn.GetProperty("addr") 340 | if err != nil || preTmnStr != tmnStr { 341 | isNewTmn := true 342 | tmn698Lock.Lock() 343 | if !utils.GlobalObject.SupportCommTermianl { 344 | var next *list.Element 345 | for e := tmn698List.Front(); e != nil; e = next { 346 | next = e.Next() 347 | a, ok := (e.Value).(addrConnID) 348 | //todo: 尝试比较级联终端 349 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 350 | isNewTmn = false 351 | break 352 | } 353 | if ok && a.addrStr == tmnStr && a.connID != conn.GetConnID() { 354 | log698.Println("终端重复登录", tmnStr, "删除", a.connID) 355 | //todo: 清除级联 356 | b, err := conn.GetProperty("bridge") 357 | if err == nil { 358 | if v, ok := b.(*bridge.Conn); ok { 359 | v.Stop() 360 | } 361 | conn.RemoveProperty("bridge") 362 | } 363 | tmn698List.Remove(e) 364 | } 365 | } 366 | } else { 367 | var next *list.Element 368 | for e := tmn698List.Front(); e != nil; e = next { 369 | next = e.Next() 370 | a, ok := (e.Value).(addrConnID) 371 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 372 | isNewTmn = false 373 | break 374 | } 375 | if !utils.GlobalObject.SupportCasLink { 376 | if ok && a.connID == conn.GetConnID() && a.addrStr != tmnStr { 377 | //todo: 有可能是联终端登录 378 | log698.Println("终端登录地址发生变更", tmnStr, "删除", a.connID) 379 | b, err := conn.GetProperty("bridge") 380 | if err == nil { 381 | if v, ok := b.(*bridge.Conn); ok { 382 | v.Stop() 383 | } 384 | conn.RemoveProperty("bridge") 385 | } 386 | tmn698List.Remove(e) 387 | } 388 | } 389 | } 390 | } 391 | if isNewTmn { 392 | if len(utils.GlobalObject.BridgeHost698) > 0 && utils.GlobalObject.BridgeHost698[0] != '0' { 393 | tsa := zptl.Ptl698_45AddrGet(rData) 394 | b := bridge.NewConn(utils.GlobalObject.BridgeHost698, tsa[1:], zptl.PTL_698_45, time.Minute, func(b []byte) { 395 | err = conn.SendBuffMsg(b) 396 | if err != nil { 397 | log698.Println(err) 398 | } else { 399 | log698.Printf("B: % X\n", b) 400 | } 401 | }) 402 | b.Start() 403 | conn.SetProperty("bridge", b) 404 | log698.Printf("B: % X create!\n", tsa) 405 | } 406 | tmn698List.PushBack(addrConnID{tmnStr, conn.GetConnID()}) 407 | log698.Println("终端登录", tmnStr, "connID", conn.GetConnID()) 408 | } else { 409 | log698.Println("终端重新登录", tmnStr, "connID", conn.GetConnID()) 410 | } 411 | tmn698Lock.Unlock() 412 | } else { 413 | log698.Println("终端重新登录", tmnStr, "connID", conn.GetConnID()) 414 | } 415 | 416 | reply := make([]byte, 128) 417 | plen := zptl.Ptl698_45BuildReplyPacket(rData, reply) 418 | err = conn.SendBuffMsg(reply[0:plen]) 419 | if err != nil { 420 | log698.Println(err) 421 | } else { 422 | conn.SetProperty("ltime", time.Now()) 423 | conn.SetProperty("addr", tmnStr) 424 | log698.Printf("L: % X\n", reply[0:plen]) 425 | } 426 | return 427 | 428 | case zptl.LINK_HAERTBEAT: 429 | if utils.GlobalObject.SupportReplyHeart { 430 | if connStatus != connT698 { 431 | log698.Println("终端未登录就发心跳", tmnStr) 432 | conn.NeedStop() 433 | } else { 434 | preTmnStr, err := conn.GetProperty("addr") 435 | if err == nil && preTmnStr == tmnStr { 436 | //todo: 级联心跳时, 需判断级联地址是否存在 437 | log698.Println("终端心跳", tmnStr) 438 | conn.SetProperty("htime", time.Now()) //更新心跳时间 439 | reply := make([]byte, 128) 440 | plen := zptl.Ptl698_45BuildReplyPacket(rData, reply) 441 | err := conn.SendBuffMsg(reply[0:plen]) 442 | if err != nil { 443 | log698.Println(err) 444 | } else { 445 | log698.Printf("H: % X", reply[0:plen]) 446 | } 447 | } else { 448 | log698.Println("终端登录地址与心跳地址不匹配!", preTmnStr, tmnStr) 449 | conn.NeedStop() 450 | } 451 | } 452 | return 453 | } 454 | 455 | case zptl.LINK_EXIT: 456 | if connStatus != connT698 { 457 | log698.Println("终端未登录就想退出", tmnStr) 458 | } else { 459 | log698.Println("终端退出", tmnStr) 460 | reply := make([]byte, 128) 461 | plen := zptl.Ptl698_45BuildReplyPacket(rData, reply) 462 | err := conn.SendMsg(reply[0:plen]) 463 | if err != nil { 464 | log698.Println(err) 465 | } 466 | } 467 | conn.NeedStop() 468 | return 469 | 470 | default: 471 | break 472 | } 473 | 474 | if utils.GlobalObject.SupportReplyReport && zptl.Ptl698_45IsReport(rData) { 475 | reply := make([]byte, 512) 476 | plen := zptl.Ptl698_45BuildReportAckPacket(rData, reply) 477 | err := conn.SendBuffMsg(reply[0:plen]) 478 | if err != nil { 479 | log698.Println(err) 480 | } else { 481 | log698.Printf("K: % X", reply[0:plen]) 482 | } 483 | } 484 | 485 | //寻找对应APP进行转发 486 | isMatch := false 487 | app698Lock.RLock() 488 | for e := app698List.Front(); e != nil; e = e.Next() { 489 | a, ok := (e.Value).(addrConnID) 490 | //1. 终端主动上报msa==0,所有后台都转发 491 | //2. 后台msa为匹配要转发 492 | if ok && (msaStr == "0" || a.addrStr == msaStr) { 493 | go conn.SendMsgByConnID(a.connID, rData) 494 | isMatch = true 495 | } 496 | } 497 | app698Lock.RUnlock() 498 | 499 | //发送给桥接主站 500 | if msaStr == "0" || !isMatch { 501 | b, err := conn.GetProperty("bridge") 502 | if err == nil { 503 | if v, ok := b.(*bridge.Conn); ok { 504 | v.Send(rData) 505 | } 506 | } 507 | } 508 | } 509 | } 510 | 511 | // PTLNWRouter NW规约路由 512 | type PTLNWRouter struct { 513 | znet.BaseRouter 514 | } 515 | 516 | // Handle nw报文处理方法 517 | func (r *PTLNWRouter) Handle(request ziface.IRequest) { 518 | conn := request.GetConnection() 519 | if conn.IsStop() { 520 | return 521 | } 522 | connStatus, err := conn.GetProperty("status") 523 | if err != nil { 524 | conn.NeedStop() 525 | return 526 | } 527 | rData := request.GetData() 528 | msaStr := strconv.Itoa(zptl.PtlNwMsaGet(rData)) 529 | tmnStr := zptl.PtlNwAddrStr(zptl.PtlNwAddrGet(rData)) 530 | conn.SetProperty("rtime", time.Now()) //最近报文接收时间 531 | 532 | if zptl.PtlNwGetDir(rData) == 0 { 533 | //from app 534 | if connStatus != connIdle && connStatus != connANW { 535 | conn.NeedStop() 536 | return 537 | } 538 | conn.SetProperty("status", connANW) 539 | logNw.Printf("A: % X\n", rData) 540 | isNewApp := true 541 | appNwLock.Lock() 542 | for e := appNwList.Front(); e != nil; e = e.Next() { 543 | a, ok := (e.Value).(addrConnID) 544 | if ok && a.connID == conn.GetConnID() { 545 | if a.addrStr != msaStr { 546 | appNwList.Remove(e) 547 | } else { 548 | isNewApp = false 549 | } 550 | break 551 | } 552 | } 553 | if isNewApp { 554 | appNwList.PushBack(addrConnID{msaStr, conn.GetConnID()}) 555 | conn.SetProperty("addr", msaStr) 556 | logNw.Println("后台登录", msaStr, "connID", conn.GetConnID()) 557 | } 558 | appNwLock.Unlock() 559 | 560 | if zptl.PtlNwGetFrameType(rData) == zptl.ONLINE { 561 | //todo: 处理app Online响应 562 | return 563 | } 564 | 565 | // logNw.Println("后台登录", msaStr, "读取", tmnStr) 566 | //寻找匹配的终端连接,进行转发 567 | tmnNwLock.RLock() 568 | for e := tmnNwList.Front(); e != nil; e = e.Next() { 569 | a, ok := (e.Value).(addrConnID) 570 | //1. 终端地址匹配要转发 571 | //2. 广播/通配地址需要转发 572 | if ok && (a.addrStr == tmnStr || strings.HasSuffix(tmnStr, "FF")) { 573 | // logNw.Println("后台", msaStr, "转发", tmnStr) 574 | go conn.SendMsgByConnID(a.connID, rData) 575 | } 576 | } 577 | tmnNwLock.RUnlock() 578 | } else { 579 | //from 终端 580 | if connStatus != connIdle && connStatus != connTNW { 581 | conn.NeedStop() 582 | return 583 | } 584 | conn.SetProperty("status", connTNW) 585 | logNw.Printf("T: % X\n", rData) 586 | switch zptl.PtlNwGetFrameType(rData) { 587 | case zptl.LINK_LOGIN: 588 | if utils.GlobalObject.SupportCasLink { 589 | //todo: 处理级联终端登陆 590 | } 591 | 592 | preTmnStr, err := conn.GetProperty("addr") 593 | if err != nil || preTmnStr != tmnStr { 594 | isNewTmn := true 595 | tmnNwLock.Lock() 596 | if !utils.GlobalObject.SupportCommTermianl { 597 | var next *list.Element 598 | for e := tmnNwList.Front(); e != nil; e = next { 599 | next = e.Next() 600 | a, ok := (e.Value).(addrConnID) 601 | //todo: 尝试比较级联终端 602 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 603 | isNewTmn = false 604 | break 605 | } 606 | if ok && a.addrStr == tmnStr && a.connID != conn.GetConnID() { 607 | logNw.Println("终端重复登录", tmnStr, "删除", a.connID) 608 | //todo: 清除级联 609 | tmnNwList.Remove(e) 610 | } 611 | } 612 | } else { 613 | var next *list.Element 614 | for e := tmnNwList.Front(); e != nil; e = next { 615 | next = e.Next() 616 | a, ok := (e.Value).(addrConnID) 617 | if ok && a.addrStr == tmnStr && a.connID == conn.GetConnID() { 618 | isNewTmn = false 619 | break 620 | } 621 | if !utils.GlobalObject.SupportCasLink { 622 | if ok && a.connID == conn.GetConnID() && a.addrStr != tmnStr { 623 | //todo: 有可能是联终端登录 624 | logNw.Println("终端登录地址发生变更", tmnStr, "删除", a.connID) 625 | tmnNwList.Remove(e) 626 | } 627 | } 628 | } 629 | } 630 | if isNewTmn { 631 | tmnNwList.PushBack(addrConnID{tmnStr, conn.GetConnID()}) 632 | logNw.Println("终端登录", tmnStr, "connID", conn.GetConnID()) 633 | } else { 634 | logNw.Println("终端重新登录", tmnStr, "connID", conn.GetConnID()) 635 | } 636 | tmnNwLock.Unlock() 637 | } else { 638 | logNw.Println("终端重新登录", tmnStr, "connID", conn.GetConnID()) 639 | } 640 | 641 | reply := make([]byte, 128) 642 | plen := zptl.PtlNwBuildReplyPacket(rData, reply) 643 | err = conn.SendBuffMsg(reply[0:plen]) 644 | if err != nil { 645 | logNw.Println(err) 646 | } else { 647 | conn.SetProperty("ltime", time.Now()) 648 | conn.SetProperty("addr", tmnStr) 649 | logNw.Printf("L: % X\n", reply[0:plen]) 650 | } 651 | return 652 | 653 | case zptl.LINK_HAERTBEAT: 654 | if utils.GlobalObject.SupportReplyHeart { 655 | if connStatus != connTNW { 656 | logNw.Println("终端未登录就发心跳", tmnStr) 657 | conn.NeedStop() 658 | } else { 659 | preTmnStr, err := conn.GetProperty("addr") 660 | if err == nil && preTmnStr == tmnStr { 661 | //todo: 级联心跳时, 需判断级联地址是否存在 662 | logNw.Println("终端心跳", tmnStr) 663 | conn.SetProperty("htime", time.Now()) //更新心跳时间 664 | reply := make([]byte, 128) 665 | plen := zptl.PtlNwBuildReplyPacket(rData, reply) 666 | err := conn.SendBuffMsg(reply[0:plen]) 667 | if err != nil { 668 | logNw.Println(err) 669 | } else { 670 | logNw.Printf("H: % X", reply[0:plen]) 671 | } 672 | } else { 673 | logNw.Println("终端登录地址与心跳地址不匹配!", preTmnStr, tmnStr) 674 | conn.NeedStop() 675 | } 676 | } 677 | return 678 | } 679 | 680 | case zptl.LINK_EXIT: 681 | if connStatus != connTNW { 682 | logNw.Println("终端未登录就想退出", tmnStr) 683 | } else { 684 | logNw.Println("终端退出", tmnStr) 685 | reply := make([]byte, 128) 686 | plen := zptl.PtlNwBuildReplyPacket(rData, reply) 687 | err := conn.SendMsg(reply[0:plen]) 688 | if err != nil { 689 | logNw.Println(err) 690 | } 691 | } 692 | conn.NeedStop() 693 | return 694 | 695 | default: 696 | break 697 | } 698 | //寻找对应APP进行转发 699 | appNwLock.RLock() 700 | for e := appNwList.Front(); e != nil; e = e.Next() { 701 | a, ok := (e.Value).(addrConnID) 702 | //1. 终端主动上报msa==0,所有后台都转发 703 | //2. 后台msa为匹配要转发 704 | if ok && (msaStr == "0" || a.addrStr == msaStr) { 705 | go conn.SendMsgByConnID(a.connID, rData) 706 | } 707 | } 708 | appNwLock.RUnlock() 709 | } 710 | } 711 | 712 | // DoConnectionBegin 创建连接的时候执行 713 | func DoConnectionBegin(conn ziface.IConnection) { 714 | conn.SetProperty("status", connIdle) //默认状态 715 | conn.SetProperty("ctime", time.Now()) //连接时间 716 | } 717 | 718 | // DoConnectionLost 连接断开的时候执行 719 | func DoConnectionLost(conn ziface.IConnection) { 720 | connStatus, err := conn.GetProperty("status") 721 | if err != nil { 722 | panic("connStatus != err") 723 | } 724 | 725 | switch connStatus { 726 | case connT376: 727 | tmn376Lock.Lock() 728 | var next *list.Element 729 | for e := tmn376List.Front(); e != nil; e = next { 730 | next = e.Next() 731 | a, ok := (e.Value).(addrConnID) 732 | if ok && a.connID == conn.GetConnID() { 733 | tmn376List.Remove(e) 734 | if !utils.GlobalObject.SupportCas { 735 | break 736 | } 737 | } 738 | } 739 | tmn376Lock.Unlock() 740 | 741 | case connA376: 742 | app376Lock.Lock() 743 | var next *list.Element 744 | for e := app376List.Front(); e != nil; e = next { 745 | next = e.Next() 746 | a, ok := (e.Value).(addrConnID) 747 | if ok && a.connID == conn.GetConnID() { 748 | app376List.Remove(e) 749 | } 750 | } 751 | app376Lock.Unlock() 752 | 753 | case connT698: 754 | tmn698Lock.Lock() 755 | var next *list.Element 756 | for e := tmn698List.Front(); e != nil; e = next { 757 | next = e.Next() 758 | a, ok := (e.Value).(addrConnID) 759 | if ok && a.connID == conn.GetConnID() { 760 | b, err := conn.GetProperty("bridge") 761 | if err == nil { 762 | if v, ok := b.(*bridge.Conn); ok { 763 | v.Stop() 764 | } 765 | conn.RemoveProperty("bridge") 766 | } 767 | tmn698List.Remove(e) 768 | if !utils.GlobalObject.SupportCas { 769 | break 770 | } 771 | } 772 | } 773 | tmn698Lock.Unlock() 774 | 775 | case connA698: 776 | app698Lock.Lock() 777 | var next *list.Element 778 | for e := app698List.Front(); e != nil; e = next { 779 | next = e.Next() 780 | a, ok := (e.Value).(addrConnID) 781 | if ok && a.connID == conn.GetConnID() { 782 | app698List.Remove(e) 783 | } 784 | } 785 | app698Lock.Unlock() 786 | 787 | case connTNW: 788 | tmnNwLock.Lock() 789 | var next *list.Element 790 | for e := tmnNwList.Front(); e != nil; e = next { 791 | next = e.Next() 792 | a, ok := (e.Value).(addrConnID) 793 | if ok && a.connID == conn.GetConnID() { 794 | tmnNwList.Remove(e) 795 | if !utils.GlobalObject.SupportCas { 796 | break 797 | } 798 | } 799 | } 800 | tmnNwLock.Unlock() 801 | 802 | case connANW: 803 | appNwLock.Lock() 804 | var next *list.Element 805 | for e := appNwList.Front(); e != nil; e = next { 806 | next = e.Next() 807 | a, ok := (e.Value).(addrConnID) 808 | if ok && a.connID == conn.GetConnID() { 809 | appNwList.Remove(e) 810 | } 811 | } 812 | appNwLock.Unlock() 813 | 814 | default: 815 | break 816 | } 817 | } 818 | 819 | func usrInput() { 820 | helper := `~~~~~~~~~~~~~~~~~~~ 821 | 1. 显示在线终端列表 822 | 2. 显示在线后台列表 823 | 3. 显示版本信息 824 | 4. 设置调试级别 825 | 5. 屏蔽心跳 826 | 6. 剔除终端 827 | 7. 尝试升级 828 | 8. 退出 829 | ~~~~~~~~~~~~~~~~~~~ 830 | :` 831 | var menu int 832 | 833 | for { 834 | menu = 0 835 | _, _ = fmt.Scanln(&menu) 836 | fmt.Println("Hi you input is", menu) 837 | switch menu { 838 | case 1: 839 | var i int 840 | var next *list.Element 841 | 842 | tmn376Lock.RLock() 843 | for e := tmn376List.Front(); e != nil; e = next { 844 | next = e.Next() 845 | t, ok := (e.Value).(addrConnID) 846 | if ok { 847 | fmt.Printf("376[%d] %s, %d\n", i, t.addrStr, t.connID) 848 | i++ 849 | } 850 | } 851 | tmn376Lock.RUnlock() 852 | 853 | tmn698Lock.RLock() 854 | for e := tmn698List.Front(); e != nil; e = next { 855 | next = e.Next() 856 | t, ok := (e.Value).(addrConnID) 857 | if ok { 858 | fmt.Printf("698[%d] %s, %d\n", i, t.addrStr, t.connID) 859 | i++ 860 | } 861 | } 862 | tmn698Lock.RUnlock() 863 | 864 | tmnNwLock.RLock() 865 | for e := tmnNwList.Front(); e != nil; e = next { 866 | next = e.Next() 867 | t, ok := (e.Value).(addrConnID) 868 | if ok { 869 | fmt.Printf("Nw[%d] %s, %d\n", i, t.addrStr, t.connID) 870 | i++ 871 | } 872 | } 873 | tmnNwLock.RUnlock() 874 | case 2: 875 | var i int 876 | var next *list.Element 877 | 878 | app376Lock.RLock() 879 | for e := app376List.Front(); e != nil; e = next { 880 | next = e.Next() 881 | a, ok := (e.Value).(addrConnID) 882 | if ok { 883 | fmt.Printf("376[%d] %s, %d\n", i, a.addrStr, a.connID) 884 | i++ 885 | } 886 | } 887 | app376Lock.RUnlock() 888 | 889 | app698Lock.RLock() 890 | for e := app698List.Front(); e != nil; e = next { 891 | next = e.Next() 892 | a, ok := (e.Value).(addrConnID) 893 | if ok { 894 | fmt.Printf("698[%d] %s, %d\n", i, a.addrStr, a.connID) 895 | i++ 896 | } 897 | } 898 | app698Lock.RUnlock() 899 | 900 | appNwLock.RLock() 901 | for e := appNwList.Front(); e != nil; e = next { 902 | next = e.Next() 903 | a, ok := (e.Value).(addrConnID) 904 | if ok { 905 | fmt.Printf("Nw[%d] %s, %d\n", i, a.addrStr, a.connID) 906 | i++ 907 | } 908 | } 909 | appNwLock.RUnlock() 910 | case 3: 911 | fmt.Println("V0.0.1") 912 | case 4: 913 | fmt.Println("功能未实现!") 914 | case 5: 915 | fmt.Println("功能未实现!") 916 | case 6: 917 | fmt.Println("功能未实现!") 918 | case 7: 919 | fmt.Println("功能未实现!") 920 | case 8: 921 | os.Exit(0) 922 | } 923 | fmt.Printf("%s", helper) 924 | } 925 | } 926 | 927 | func logInit() { 928 | log376 = log.New(&timewriter.TimeWriter{ 929 | Dir: utils.GlobalObject.LogDir, 930 | Compress: true, 931 | ReserveDay: 30, 932 | ModuleName: "376", 933 | }, "", log.LstdFlags) 934 | 935 | log698 = log.New(&timewriter.TimeWriter{ 936 | Dir: utils.GlobalObject.LogDir, 937 | Compress: true, 938 | ReserveDay: 30, 939 | ModuleName: "698", 940 | }, "", log.LstdFlags) 941 | 942 | logNw = log.New(&timewriter.TimeWriter{ 943 | Dir: utils.GlobalObject.LogDir, 944 | Compress: true, 945 | ReserveDay: 30, 946 | ModuleName: "nw", 947 | }, "", log.LstdFlags) 948 | } 949 | 950 | func main() { 951 | // runtime.GOMAXPROCS(runtime.NumCPU()) 952 | 953 | // go func() { 954 | // log.Println(http.ListenAndServe("localhost:9999", nil)) 955 | // }() 956 | 957 | if runtime.GOOS != "linux" { 958 | go usrInput() 959 | } 960 | 961 | app698List = list.New() 962 | tmn698List = list.New() 963 | 964 | app376List = list.New() 965 | tmn376List = list.New() 966 | 967 | appNwList = list.New() 968 | tmnNwList = list.New() 969 | logInit() 970 | 971 | //创建一个server句柄 972 | s := znet.NewServer() 973 | 974 | //注册链接hook回调函数 975 | s.SetOnConnStart(DoConnectionBegin) 976 | s.SetOnConnStop(DoConnectionLost) 977 | 978 | //配置路由 979 | s.AddRouter(zptl.PTL_1376_1, &Ptl1376_1Router{}) 980 | s.AddRouter(zptl.PTL_698_45, &PTL698_45Router{}) 981 | s.AddRouter(zptl.PTL_NW, &PTLNWRouter{}) 982 | 983 | //开启服务 984 | s.Serve() 985 | } 986 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module gfep 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /test/gterminal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var wg sync.WaitGroup 11 | 12 | var crc16CcittTable = []uint16{ 13 | 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 14 | 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 15 | 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 16 | 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 17 | 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 18 | 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 19 | 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 20 | 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 21 | 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 22 | 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 23 | 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 24 | 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 25 | 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 26 | 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 27 | 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 28 | 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 29 | 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 30 | 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 31 | 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 32 | 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 33 | 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 34 | 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 35 | 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 36 | 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 37 | 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 38 | 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 39 | 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 40 | 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 41 | 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 42 | 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 43 | 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 44 | 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, 45 | } 46 | 47 | //crc16Calculate crc16 calculate 48 | func crc16Calculate(data []byte) uint16 { 49 | var fcs uint16 = 0xffff 50 | 51 | for _, v := range data { 52 | fcs = (fcs >> 8) ^ crc16CcittTable[uint8(uint16(v)^fcs)] 53 | } 54 | fcs ^= 0xffff 55 | return fcs 56 | } 57 | 58 | func getDataTime(buf []byte) { 59 | t := time.Now().Local() 60 | year := uint16(t.Year()) 61 | buf[0] = byte(year >> 8) 62 | buf[1] = byte(year) 63 | buf[2] = byte(t.Month()) 64 | buf[3] = byte(t.Day()) 65 | buf[4] = byte(t.Weekday()) 66 | buf[5] = byte(t.Hour()) 67 | buf[6] = byte(t.Minute()) 68 | buf[7] = byte(t.Second()) 69 | millisecond := t.Nanosecond() / 1e6 70 | buf[8] = byte(millisecond >> 8) 71 | buf[9] = byte(millisecond) 72 | } 73 | 74 | func send(c net.Conn, cNo int) { 75 | sbuf := make([]byte, 32) 76 | sbuf[0] = 0x68 77 | sbuf[1] = 0x1E 78 | sbuf[2] = 0x00 79 | sbuf[3] = 0x81 80 | sbuf[4] = 0x05 81 | sbuf[5] = 0x15 82 | sbuf[6] = 0x61 83 | sbuf[7] = byte((cNo >> 24) & 0xff) 84 | sbuf[8] = byte((cNo >> 16) & 0xff) 85 | sbuf[9] = byte((cNo >> 8) & 0xff) 86 | sbuf[10] = byte((cNo >> 0) & 0xff) 87 | sbuf[11] = 0x00 88 | crc := crc16Calculate(sbuf[1:12]) 89 | sbuf[12] = byte((crc >> 0) & 0xff) 90 | sbuf[13] = byte((crc >> 8) & 0xff) 91 | sbuf[14] = 0x01 92 | sbuf[15] = 0x00 93 | sbuf[16] = 0x00 94 | sbuf[17] = 0x00 95 | sbuf[18] = 0x3C 96 | getDataTime(sbuf[19:]) 97 | crc = crc16Calculate(sbuf[1:29]) 98 | sbuf[29] = byte((crc >> 0) & 0xff) 99 | sbuf[30] = byte((crc >> 8) & 0xff) 100 | sbuf[31] = 0x16 101 | 102 | for { 103 | //客户端请求数据写入 conn,并传输 104 | cnt, err := c.Write([]byte(sbuf)) 105 | if err != nil { 106 | fmt.Printf("客户端发送数据失败 %d, %s\n", cNo, err) 107 | c.Close() 108 | wg.Done() 109 | break 110 | } 111 | fmt.Printf("客户端发送: %d, %d, % X\n", cNo, cnt, sbuf[:cnt]) 112 | time.Sleep(1 * time.Second) 113 | } 114 | } 115 | 116 | func rece(c net.Conn, cNo int) { 117 | 118 | //接收缓存 119 | rbuf := make([]byte, 1024) 120 | 121 | for { 122 | //服务器端返回的数据写入空buf 123 | cnt, err := c.Read(rbuf) 124 | if err != nil { 125 | fmt.Printf("客户端接收数据失败: %d, %s\n", cNo, err) 126 | c.Close() 127 | wg.Done() 128 | break 129 | } 130 | fmt.Printf("服务器端回复: %d, %d, % X\n", cNo, cnt, rbuf[:cnt]) 131 | } 132 | } 133 | 134 | // ClientSocket 客户端连接 135 | func ClientSocket(count int) { 136 | for i := 0; i < count; i++ { 137 | wg.Add(1) 138 | go func(i int) { 139 | time.Sleep(time.Duration(i*10000) * time.Microsecond) 140 | conn, err := net.Dial("tcp", "10.9.52.62:20083") 141 | if err != nil { 142 | fmt.Println("客户端建立连接失败", i, err) 143 | return 144 | } 145 | fmt.Println("客户端建立连接OK", i) 146 | 147 | wg.Add(1) 148 | go send(conn, i) 149 | 150 | wg.Add(1) 151 | go rece(conn, i) 152 | }(i) 153 | } 154 | wg.Wait() 155 | } 156 | 157 | func main() { 158 | 159 | fmt.Printf("698客户端测试\n") 160 | 161 | ClientSocket(10) 162 | 163 | wg.Wait() 164 | } 165 | -------------------------------------------------------------------------------- /test/test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var wg sync.WaitGroup 11 | 12 | func send(c net.Conn, cNo int) { 13 | sbuf := make([]byte, 32) 14 | sbuf[0] = 0x68 15 | sbuf[1] = 0x1E 16 | sbuf[2] = 0x00 17 | sbuf[3] = 0x81 18 | sbuf[4] = 0x05 19 | sbuf[5] = 0x15 20 | sbuf[6] = 0x61 21 | sbuf[7] = 0x50 22 | sbuf[8] = 0x88 23 | sbuf[9] = 0x58 24 | sbuf[10] = 0x01 25 | sbuf[11] = 0x00 26 | sbuf[12] = 0xDA 27 | sbuf[13] = 0x0E 28 | sbuf[14] = 0x01 29 | sbuf[15] = 0x00 30 | sbuf[16] = 0x00 31 | sbuf[17] = 0x00 32 | sbuf[18] = 0x3C 33 | sbuf[19] = 0x07 34 | sbuf[20] = 0xE4 35 | sbuf[21] = 0x03 36 | sbuf[22] = 0x19 37 | sbuf[23] = 0x03 38 | sbuf[24] = 0x0B 39 | sbuf[25] = 0x1D 40 | sbuf[26] = 0x01 41 | sbuf[27] = 0x03 42 | sbuf[28] = 0x84 43 | sbuf[29] = 0x9C 44 | sbuf[30] = 0x10 45 | sbuf[31] = 0x16 46 | 47 | for { 48 | //客户端请求数据写入 conn,并传输 49 | _, err := c.Write([]byte(sbuf)) 50 | if err != nil { 51 | fmt.Printf("客户端发送数据失败 %d, %s\n", cNo, err) 52 | c.Close() 53 | wg.Done() 54 | break 55 | } 56 | //fmt.Printf("客户端发送: %d, %d, %s\n", cNo, cnt, hextostr(sbuf[:cnt], " ")) 57 | time.Sleep(1 * time.Second) 58 | } 59 | } 60 | 61 | func rece(c net.Conn, cNo int) { 62 | 63 | //接收缓存 64 | rbuf := make([]byte, 1024) 65 | 66 | for { 67 | //服务器端返回的数据写入空buf 68 | _, err := c.Read(rbuf) 69 | if err != nil { 70 | fmt.Printf("客户端接收数据失败: %d, %s\n", cNo, err) 71 | c.Close() 72 | wg.Done() 73 | break 74 | } 75 | //fmt.Printf("服务器端回复: %d, %d, %s\n", cNo, cnt, hextostr(rbuf[:cnt], " ")) 76 | } 77 | } 78 | 79 | // ClientSocket 客户端连接 80 | func ClientSocket() { 81 | for i := 0; i < 10000; i++ { 82 | wg.Add(1) 83 | go func(i int) { 84 | time.Sleep(time.Duration(i*10000) * time.Microsecond) 85 | conn, err := net.Dial("tcp", "127.0.0.1:20083") 86 | if err != nil { 87 | fmt.Println("客户端建立连接失败", i, err) 88 | return 89 | } 90 | fmt.Println("客户端建立连接OK", i) 91 | 92 | wg.Add(1) 93 | go send(conn, i) 94 | 95 | wg.Add(1) 96 | go rece(conn, i) 97 | }(i) 98 | } 99 | wg.Wait() 100 | } 101 | 102 | func main() { 103 | 104 | fmt.Printf("客户端测试\n") 105 | 106 | ClientSocket() 107 | 108 | wg.Wait() 109 | } 110 | -------------------------------------------------------------------------------- /timewriter/timewriter.go: -------------------------------------------------------------------------------- 1 | package timewriter 2 | 3 | import ( 4 | "compress/gzip" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "sort" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | "time" 16 | ) 17 | 18 | // io.WriteCloser 19 | var _ io.WriteCloser = (*TimeWriter)(nil) 20 | 21 | const ( 22 | compressSuffix = ".gz" 23 | timeFormat = "2006-01-02 15:04:05" 24 | ) 25 | 26 | type TimeWriter struct { 27 | Dir string 28 | Compress bool 29 | ReserveDay int 30 | ModuleName string 31 | 32 | curFilename string 33 | file *os.File 34 | mu sync.Mutex 35 | startMill sync.Once 36 | millCh chan bool 37 | } 38 | 39 | func (l *TimeWriter) Write(p []byte) (n int, err error) { 40 | l.mu.Lock() 41 | defer l.mu.Unlock() 42 | 43 | if l.file == nil { 44 | if err = l.openExistingOrNew(len(p)); err != nil { 45 | fmt.Printf("write fail, msg(%s)\n", err) 46 | return 0, err 47 | } 48 | } 49 | 50 | if l.curFilename != l.filename() { 51 | l.rotate() 52 | } 53 | 54 | n, err = l.file.Write(p) 55 | 56 | return n, err 57 | } 58 | 59 | func (l *TimeWriter) Close() error { 60 | l.mu.Lock() 61 | defer l.mu.Unlock() 62 | return l.close() 63 | } 64 | 65 | func (l *TimeWriter) Rotate() error { 66 | l.mu.Lock() 67 | defer l.mu.Unlock() 68 | return l.rotate() 69 | } 70 | 71 | func (l *TimeWriter) close() error { 72 | if l.file == nil { 73 | return nil 74 | } 75 | err := l.file.Close() 76 | l.file = nil 77 | return err 78 | } 79 | 80 | func (l *TimeWriter) rotate() error { 81 | if err := l.close(); err != nil { 82 | return err 83 | } 84 | if err := l.openNew(); err != nil { 85 | return err 86 | } 87 | 88 | l.mill() 89 | return nil 90 | } 91 | 92 | func (l *TimeWriter) oldLogFiles() ([]logInfo, error) { 93 | files, err := ioutil.ReadDir(l.Dir) 94 | if err != nil { 95 | return nil, fmt.Errorf("can't read log file directory: %s", err) 96 | } 97 | logFiles := []logInfo{} 98 | 99 | prefix, ext := l.prefixAndExt() 100 | 101 | for _, f := range files { 102 | if f.IsDir() { 103 | continue 104 | } 105 | if f.Name() == filepath.Base(l.curFilename) { 106 | continue 107 | } 108 | if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil { 109 | logFiles = append(logFiles, logInfo{t, f}) 110 | continue 111 | } else { 112 | fmt.Printf("err1(%s)\n", err) 113 | } 114 | if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil { 115 | logFiles = append(logFiles, logInfo{t, f}) 116 | continue 117 | } else { 118 | fmt.Printf("err2(%s)\n", err) 119 | } 120 | } 121 | 122 | sort.Sort(byFormatTime(logFiles)) 123 | 124 | return logFiles, nil 125 | } 126 | 127 | func (l *TimeWriter) timeFromName(filename, prefix, ext string) (time.Time, error) { 128 | if !strings.HasPrefix(filename, prefix) { 129 | return time.Time{}, errors.New("mismatched prefix") 130 | } 131 | if !strings.HasSuffix(filename, ext) { 132 | return time.Time{}, errors.New("mismatched extension") 133 | } 134 | ts := filename[len(prefix) : len(filename)-len(ext)] 135 | if len(ts) != 8 { 136 | return time.Time{}, errors.New("mismatched date") 137 | } 138 | if year, err := strconv.ParseInt(ts[0:4], 10, 64); err != nil { 139 | return time.Time{}, err 140 | } else if month, _ := strconv.ParseInt(ts[4:6], 10, 64); err != nil { 141 | return time.Time{}, err 142 | } else if day, _ := strconv.ParseInt(ts[6:8], 10, 64); err != nil { 143 | return time.Time{}, err 144 | } else { 145 | timeStr := fmt.Sprintf("%04d-%02d-%02d 00:00:00", year, month, day) 146 | if location, err := time.LoadLocation("Local"); err != nil { 147 | return time.Time{}, err 148 | } else if t, err := time.ParseInLocation(timeFormat, timeStr, location); err != nil { 149 | return time.Time{}, err 150 | } else { 151 | return t, nil 152 | } 153 | } 154 | 155 | } 156 | 157 | func (l *TimeWriter) prefixAndExt() (prefix, ext string) { 158 | filename := filepath.Base(l.filename()) 159 | ext = filepath.Ext(filename) 160 | prefix = filename[:len(filename)-len(ext)-8] 161 | return prefix, ext 162 | } 163 | 164 | func (l *TimeWriter) millRunOnce() error { 165 | if l.ReserveDay == 0 && !l.Compress { 166 | return nil 167 | } 168 | 169 | files, err := l.oldLogFiles() 170 | if err != nil { 171 | return err 172 | } 173 | 174 | var compress, remove []logInfo 175 | 176 | if l.ReserveDay > 0 { 177 | diff := time.Duration(int64(24*time.Hour) * int64(l.ReserveDay)) 178 | cutoff := time.Now().Add(-1 * diff) 179 | 180 | var remaining []logInfo 181 | for _, f := range files { 182 | if f.timestamp.Before(cutoff) { 183 | remove = append(remove, f) 184 | } else { 185 | remaining = append(remaining, f) 186 | } 187 | } 188 | 189 | files = remaining 190 | } 191 | 192 | if l.Compress { 193 | for _, f := range files { 194 | if !strings.HasSuffix(f.Name(), compressSuffix) { 195 | compress = append(compress, f) 196 | } 197 | } 198 | } 199 | 200 | for _, f := range remove { 201 | errRemove := os.Remove(filepath.Join(l.Dir, f.Name())) 202 | if err == nil && errRemove != nil { 203 | err = errRemove 204 | } 205 | } 206 | for _, f := range compress { 207 | fn := filepath.Join(l.Dir, f.Name()) 208 | errCompress := compressLogFile(fn, fn+compressSuffix) 209 | if err == nil && errCompress != nil { 210 | err = errCompress 211 | } 212 | } 213 | 214 | return err 215 | } 216 | 217 | func (l *TimeWriter) millRun() { 218 | for _ = range l.millCh { 219 | _ = l.millRunOnce() 220 | } 221 | } 222 | 223 | func (l *TimeWriter) mill() { 224 | l.startMill.Do(func() { 225 | l.millCh = make(chan bool, 1) 226 | go l.millRun() 227 | }) 228 | select { 229 | case l.millCh <- true: 230 | default: 231 | } 232 | } 233 | 234 | func (l *TimeWriter) openNew() error { 235 | name := l.filename() 236 | err := os.MkdirAll(l.Dir, 0744) 237 | if err != nil { 238 | return fmt.Errorf("can't make directories for new logfile: %s", err) 239 | } 240 | 241 | mode := os.FileMode(0644) 242 | 243 | f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) 244 | if err != nil { 245 | return fmt.Errorf("can't open new logfile: %s", err) 246 | } 247 | l.curFilename = name 248 | l.file = f 249 | return nil 250 | } 251 | 252 | func (l *TimeWriter) openExistingOrNew(writeLen int) error { 253 | 254 | filename := l.filename() 255 | if _, err := os.Stat(filename); os.IsNotExist(err) { 256 | return l.openNew() 257 | } else if err != nil { 258 | return fmt.Errorf("error getting log file info: %s", err) 259 | } 260 | 261 | file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644) 262 | if err != nil { 263 | return l.openNew() 264 | } 265 | l.curFilename = filename 266 | l.file = file 267 | return nil 268 | } 269 | 270 | func (l *TimeWriter) filename() string { 271 | year, month, day := time.Now().Date() 272 | date := fmt.Sprintf("%04d%02d%02d", year, month, day) 273 | name := fmt.Sprintf("%s.%s.log", l.ModuleName, date) 274 | // name := fmt.Sprintf("%s.%s.log", filepath.Base(os.Args[0]), date) 275 | if l.Dir != "" { 276 | return filepath.Join(l.Dir, name) 277 | } 278 | return filepath.Join(os.TempDir(), name) 279 | } 280 | 281 | func compressLogFile(src, dst string) (err error) { 282 | f, err := os.Open(src) 283 | if err != nil { 284 | return fmt.Errorf("failed to open log file: %v", err) 285 | } 286 | defer f.Close() 287 | 288 | fi, err := os.Stat(src) 289 | if err != nil { 290 | return fmt.Errorf("failed to stat log file: %v", err) 291 | } 292 | 293 | gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode()) 294 | if err != nil { 295 | return fmt.Errorf("failed to open compressed log file: %v", err) 296 | } 297 | defer gzf.Close() 298 | 299 | gz := gzip.NewWriter(gzf) 300 | 301 | defer func() { 302 | if err != nil { 303 | os.Remove(dst) 304 | err = fmt.Errorf("failed to compress log file: %v", err) 305 | } 306 | }() 307 | 308 | if _, err := io.Copy(gz, f); err != nil { 309 | return err 310 | } 311 | if err := gz.Close(); err != nil { 312 | return err 313 | } 314 | if err := gzf.Close(); err != nil { 315 | return err 316 | } 317 | 318 | if err := f.Close(); err != nil { 319 | return err 320 | } 321 | if err := os.Remove(src); err != nil { 322 | return err 323 | } 324 | 325 | return nil 326 | } 327 | 328 | type logInfo struct { 329 | timestamp time.Time 330 | os.FileInfo 331 | } 332 | 333 | // byFormatTime sorts by newest time formatted in the name. 334 | type byFormatTime []logInfo 335 | 336 | func (b byFormatTime) Less(i, j int) bool { 337 | return b[i].timestamp.After(b[j].timestamp) 338 | } 339 | 340 | func (b byFormatTime) Swap(i, j int) { 341 | b[i], b[j] = b[j], b[i] 342 | } 343 | 344 | func (b byFormatTime) Len() int { 345 | return len(b) 346 | } 347 | -------------------------------------------------------------------------------- /timewriter/timewriter_test.go: -------------------------------------------------------------------------------- 1 | package timewriter 2 | 3 | import ( 4 | "log" 5 | "testing" 6 | ) 7 | 8 | func TestWrite(t *testing.T) { 9 | timeWriter := &TimeWriter{ 10 | Dir: "./log", 11 | Compress: true, 12 | ReserveDay: 30, 13 | } 14 | logDebug := log.New(timeWriter, " [Debug] ", log.LstdFlags) 15 | logDebug.Println("this is debug") 16 | } 17 | -------------------------------------------------------------------------------- /utils/globalobj.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "gfep/ziface" 6 | "gfep/zlog" 7 | "io/ioutil" 8 | "os" 9 | ) 10 | 11 | // GlobalObj 存储一切有关Zinx框架的全局参数,供其他模块使用一些参数也可以通过 用户根据 zinx.json来配置 12 | type GlobalObj struct { 13 | /* 14 | Server 15 | */ 16 | TCPServer ziface.IServer //当前Zinx的全局Server对象 17 | Host string //当前服务器主机IP 18 | TCPPort int //当前服务器主机监听端口号 19 | BridgeHost698 string //698桥接主机IP 20 | Name string //当前服务器名称 21 | 22 | /* 23 | Zinx 24 | */ 25 | Version string //当前Zinx版本号 26 | MaxPacketSize uint32 //都需数据包的最大值 27 | MaxConn int //当前服务器主机允许的最大链接个数 28 | WorkerPoolSize uint32 //业务工作Worker池的数量 29 | MaxWorkerTaskLen uint32 //业务工作Worker对应负责的任务队列最大任务存储数量 30 | MaxMsgChanLen uint32 //SendBuffMsg发送消息的缓冲最大长度 31 | 32 | /* 33 | config file path 34 | */ 35 | ConfFilePath string 36 | 37 | /* 38 | logger 39 | */ 40 | LogDir string //日志所在文件夹 默认"./log" 41 | LogFile string //日志文件名称 默认"" --如果没有设置日志文件,打印信息将打印至stderr 42 | LogDebugClose bool //是否关闭Debug日志级别调试信息 默认false -- 默认打开debug信息 43 | 44 | /* 45 | Fep 46 | */ 47 | Timeout int //TCP连接超时时间(单位:分钟) 48 | SupportCompress bool //是否支持加密(南网使用) 49 | SupportCas bool //是否级联(南网使用) 50 | SupportCasLink bool //是否级联终端登陆、心跳(南网使用) 51 | SupportCommTermianl bool //是否支持终端重复登陆(Y/N) 52 | SupportReplyHeart bool //是否支持前置机维护心跳(Y/N) 53 | SupportReplyReport bool //是否支持前置机确认上报(Y/N) 54 | } 55 | 56 | // GlobalObject 定义一个全局的对象 57 | var GlobalObject *GlobalObj 58 | 59 | // PathExists 判断一个文件是否存在 60 | func PathExists(path string) (bool, error) { 61 | _, err := os.Stat(path) 62 | if err == nil { 63 | return true, nil 64 | } 65 | if os.IsNotExist(err) { 66 | return false, nil 67 | } 68 | return false, err 69 | } 70 | 71 | // Reload 读取用户的配置文件 72 | func (g *GlobalObj) Reload() { 73 | 74 | if confFileExists, _ := PathExists(g.ConfFilePath); !confFileExists { 75 | //fmt.Println("Config File ", g.ConfFilePath , " is not exist!!") 76 | return 77 | } 78 | 79 | data, err := ioutil.ReadFile(g.ConfFilePath) 80 | if err != nil { 81 | panic(err) 82 | } 83 | //将json数据解析到struct中 84 | err = json.Unmarshal(data, g) 85 | if err != nil { 86 | panic(err) 87 | } 88 | 89 | //Logger 设置 90 | if g.LogFile != "" { 91 | zlog.SetLogFile(g.LogDir, g.LogFile) 92 | } 93 | if g.LogDebugClose { 94 | zlog.CloseDebug() 95 | } 96 | } 97 | 98 | // 提供init方法,默认加载 99 | func init() { 100 | pwd, err := os.Getwd() 101 | if err != nil { 102 | pwd = "." 103 | } 104 | //初始化GlobalObject变量,设置一些默认值 105 | GlobalObject = &GlobalObj{ 106 | Name: "gfep", 107 | Version: "V0.2", 108 | TCPPort: 20083, 109 | Host: "0.0.0.0", 110 | BridgeHost698: "", //0.0.0.0:0 111 | MaxConn: 50000, 112 | MaxPacketSize: 2200, 113 | ConfFilePath: pwd + "/conf/gfep.json", 114 | WorkerPoolSize: 0, 115 | MaxWorkerTaskLen: 1024, 116 | MaxMsgChanLen: 8, 117 | LogDir: pwd + "/log", 118 | LogFile: "", 119 | LogDebugClose: false, 120 | 121 | Timeout: 30, 122 | SupportCompress: false, 123 | SupportCas: false, 124 | SupportCasLink: false, 125 | SupportCommTermianl: true, 126 | SupportReplyHeart: true, 127 | SupportReplyReport: false, 128 | } 129 | 130 | //从配置文件中加载一些用户配置的参数 131 | GlobalObject.Reload() 132 | } 133 | -------------------------------------------------------------------------------- /ziface/iconnection.go: -------------------------------------------------------------------------------- 1 | package ziface 2 | 3 | import "net" 4 | 5 | // IConnection 定义连接接口 6 | type IConnection interface { 7 | //Start 启动连接,让当前连接开始工作 8 | Start() 9 | //Stop 停止连接,结束当前连接状态M 10 | Stop() 11 | IsStop() bool 12 | NeedStop() 13 | 14 | //GetTCPConnection 从当前连接获取原始的socket TCPConn 15 | GetTCPConnection() *net.TCPConn 16 | //GetConnID 获取当前连接ID 17 | GetConnID() uint32 18 | //RemoteAddr 获取远程客户端地址信息 19 | RemoteAddr() net.Addr 20 | 21 | //SendMsg 直接将数据发送数据给远程的TCP客户端(无缓冲) 22 | SendMsg(data []byte) error 23 | //SendBuffMsg 直接将数据发送给远程的TCP客户端(有缓冲) 24 | SendBuffMsg(data []byte) error 25 | //SendMsgByConnID 将数据发送到指定connID客户端 26 | SendMsgByConnID(connID uint32, data []byte) error 27 | 28 | //SetProperty 设置链接属性 29 | SetProperty(key string, value interface{}) 30 | //GetProperty 获取链接属性 31 | GetProperty(key string) (interface{}, error) 32 | //RemoveProperty 移除链接属性 33 | RemoveProperty(key string) 34 | } 35 | -------------------------------------------------------------------------------- /ziface/iconnmanager.go: -------------------------------------------------------------------------------- 1 | package ziface 2 | 3 | // IConnManager 连接管理抽象层 4 | type IConnManager interface { 5 | Add(conn IConnection) //添加链接 6 | Remove(conn IConnection) //删除连接 7 | Get(connID uint32) (IConnection, error) //利用ConnID获取链接 8 | Len() int //获取当前连接 9 | ClearConn() //删除并停止所有链接 10 | } 11 | -------------------------------------------------------------------------------- /ziface/imessage.go: -------------------------------------------------------------------------------- 1 | package ziface 2 | 3 | // IMessage 将请求的一个消息封装到message中,定义抽象层接口 4 | type IMessage interface { 5 | GetDataLen() uint32 //获取消息数据段长度 6 | GetMsgID() uint32 //获取消息ID 7 | GetData() []byte //获取消息内容 8 | 9 | SetMsgID(uint32) //设计消息ID 10 | SetData([]byte) //设计消息内容 11 | SetDataLen(uint32) //设置消息数据段长度 12 | } 13 | -------------------------------------------------------------------------------- /ziface/imsghandler.go: -------------------------------------------------------------------------------- 1 | package ziface 2 | 3 | // IMsgHandle 消息管理抽象层 4 | type IMsgHandle interface { 5 | DoMsgHandler(request IRequest) //马上以非阻塞方式处理消息 6 | AddRouter(msgID uint32, router IRouter) //为消息添加具体的处理逻辑 7 | StartWorkerPool() //启动worker工作池 8 | SendMsgToTaskQueue(request IRequest) //将消息交给TaskQueue,由worker进行处理 9 | } 10 | -------------------------------------------------------------------------------- /ziface/iprotocol.go: -------------------------------------------------------------------------------- 1 | package ziface 2 | 3 | // IProtocol 报文检测 4 | type IProtocol interface { 5 | //Chkfrm 报文检测, 返回合法报文数量 6 | Chkfrm(data []byte) int32 7 | 8 | //Reset 复位 9 | Reset() 10 | } 11 | -------------------------------------------------------------------------------- /ziface/irequest.go: -------------------------------------------------------------------------------- 1 | package ziface 2 | 3 | // IRequest 实际上是把客户端请求的链接信息 和 请求的数据 包装到了 Request里 4 | type IRequest interface { 5 | GetConnection() IConnection //获取请求连接信息 6 | GetData() []byte //获取请求消息的数据 7 | GetMsgID() uint32 //获取请求的消息ID 8 | } 9 | -------------------------------------------------------------------------------- /ziface/irouter.go: -------------------------------------------------------------------------------- 1 | package ziface 2 | 3 | // IRouter 路由接口, 这里面路由是 使用框架者给该链接自定的 处理业务方法 4 | // 路由里的IRequest 则包含用该链接的链接信息和该链接的请求数据信息 5 | type IRouter interface { 6 | PreHandle(request IRequest) //在处理conn业务之前的钩子方法 7 | Handle(request IRequest) //处理conn业务的方法 8 | PostHandle(request IRequest) //处理conn业务之后的钩子方法 9 | } 10 | -------------------------------------------------------------------------------- /ziface/iserver.go: -------------------------------------------------------------------------------- 1 | package ziface 2 | 3 | // IServer 定义服务器接口 4 | type IServer interface { 5 | //Start 启动服务器方法 6 | Start() 7 | //Stop 停止服务器方法 8 | Stop() 9 | //Serve 开启业务服务方法 10 | Serve() 11 | //AddRouter 路由功能:给当前服务注册一个路由业务方法,供客户端链接处理使用 12 | AddRouter(msgID uint32, router IRouter) 13 | //GetConnMgr 得到链接管理 14 | GetConnMgr() IConnManager 15 | //SetOnConnStart 设置该Server的连接创建时Hook函数 16 | SetOnConnStart(func(IConnection)) 17 | //SetOnConnStop 设置该Server的连接断开时的Hook函数 18 | SetOnConnStop(func(IConnection)) 19 | //CallOnConnStart 调用连接OnConnStart Hook函数 20 | CallOnConnStart(conn IConnection) 21 | //CallOnConnStop 调用连接OnConnStop Hook函数 22 | CallOnConnStop(conn IConnection) 23 | } 24 | -------------------------------------------------------------------------------- /zlog/stdzlog.go: -------------------------------------------------------------------------------- 1 | package zlog 2 | 3 | /* 4 | 全局默认提供一个Log对外句柄,可以直接使用API系列调用 5 | 全局日志对象 StdZinxLog 6 | */ 7 | 8 | import "os" 9 | 10 | var StdZinxLog = NewZinxLog(os.Stderr, "", BitDefault) 11 | 12 | //获取StdZinxLog 标记位 13 | func Flags() int { 14 | return StdZinxLog.Flags() 15 | } 16 | 17 | //设置StdZinxLog标记位 18 | func ResetFlags(flag int) { 19 | StdZinxLog.ResetFlags(flag) 20 | } 21 | 22 | //添加flag标记 23 | func AddFlag(flag int) { 24 | StdZinxLog.AddFlag(flag) 25 | } 26 | 27 | //设置StdZinxLog 日志头前缀 28 | func SetPrefix(prefix string) { 29 | StdZinxLog.SetPrefix(prefix) 30 | } 31 | 32 | //设置StdZinxLog绑定的日志文件 33 | func SetLogFile(fileDir string, fileName string) { 34 | StdZinxLog.SetLogFile(fileDir, fileName) 35 | } 36 | 37 | //设置关闭debug 38 | func CloseDebug() { 39 | StdZinxLog.CloseDebug() 40 | } 41 | 42 | //设置打开debug 43 | func OpenDebug() { 44 | StdZinxLog.OpenDebug() 45 | } 46 | 47 | // ====> Debug <==== 48 | func Debugf(format string, v ...interface{}) { 49 | StdZinxLog.Debugf(format, v...) 50 | } 51 | 52 | func Debug(v ...interface{}) { 53 | StdZinxLog.Debug(v...) 54 | } 55 | 56 | // ====> Info <==== 57 | func Infof(format string, v ...interface{}) { 58 | StdZinxLog.Infof(format, v...) 59 | } 60 | 61 | func Info(v ...interface{}) { 62 | StdZinxLog.Info(v...) 63 | } 64 | 65 | // ====> Warn <==== 66 | func Warnf(format string, v ...interface{}) { 67 | StdZinxLog.Warnf(format, v...) 68 | } 69 | 70 | func Warn(v ...interface{}) { 71 | StdZinxLog.Warn(v...) 72 | } 73 | 74 | // ====> Error <==== 75 | func Errorf(format string, v ...interface{}) { 76 | StdZinxLog.Errorf(format, v...) 77 | } 78 | 79 | func Error(v ...interface{}) { 80 | StdZinxLog.Error(v...) 81 | } 82 | 83 | // ====> Fatal 需要终止程序 <==== 84 | func Fatalf(format string, v ...interface{}) { 85 | StdZinxLog.Fatalf(format, v...) 86 | } 87 | 88 | func Fatal(v ...interface{}) { 89 | StdZinxLog.Fatal(v...) 90 | } 91 | 92 | // ====> Panic <==== 93 | func Panicf(format string, v ...interface{}) { 94 | StdZinxLog.Panicf(format, v...) 95 | } 96 | 97 | func Panic(v ...interface{}) { 98 | StdZinxLog.Panic(v...) 99 | } 100 | 101 | // ====> Stack <==== 102 | func Stack(v ...interface{}) { 103 | StdZinxLog.Stack(v...) 104 | } 105 | 106 | func init() { 107 | //因为StdZinxLog对象 对所有输出方法做了一层包裹,所以在打印调用函数的时候,比正常的logger对象多一层调用 108 | //一般的zinxLogger对象 calldDepth=2, StdZinxLog的calldDepth=3 109 | StdZinxLog.calldDepth = 3 110 | } 111 | -------------------------------------------------------------------------------- /zlog/zlogger.go: -------------------------------------------------------------------------------- 1 | package zlog 2 | 3 | /* 4 | 日志类全部方法 及 API 5 | 6 | Add By Aceld(刘丹冰) 2019-4-23 7 | */ 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "io" 13 | "os" 14 | "runtime" 15 | "sync" 16 | "time" 17 | ) 18 | 19 | const ( 20 | LOG_MAX_BUF = 1024 * 1024 21 | ) 22 | 23 | //日志头部信息标记位,采用bitmap方式,用户可以选择头部需要哪些标记位被打印 24 | const ( 25 | BitDate = 1 << iota //日期标记位 2019/01/23 26 | BitTime //时间标记位 01:23:12 27 | BitMicroSeconds //微秒级标记位 01:23:12.111222 28 | BitLongFile // 完整文件名称 /home/go/src/zinx/server.go 29 | BitShortFile // 最后文件名 server.go 30 | BitLevel // 当前日志级别: 0(Debug), 1(Info), 2(Warn), 3(Error), 4(Panic), 5(Fatal) 31 | BitStdFlag = BitDate | BitTime //标准头部日志格式 32 | BitDefault = BitLevel | BitShortFile | BitStdFlag //默认日志头部格式 33 | ) 34 | 35 | //日志级别 36 | const ( 37 | LogDebug = iota 38 | LogInfo 39 | LogWarn 40 | LogError 41 | LogPanic 42 | LogFatal 43 | ) 44 | 45 | //日志级别对应的显示字符串 46 | var levels = []string{ 47 | "[DEBUG]", 48 | "[INFO]", 49 | "[WARN]", 50 | "[ERROR]", 51 | "[PANIC]", 52 | "[FATAL]", 53 | } 54 | 55 | type ZinxLogger struct { 56 | //确保多协程读写文件,防止文件内容混乱,做到协程安全 57 | mu sync.Mutex 58 | //每行log日志的前缀字符串,拥有日志标记 59 | prefix string 60 | //日志标记位 61 | flag int 62 | //日志输出的文件描述符 63 | out io.Writer 64 | //输出的缓冲区 65 | buf bytes.Buffer 66 | //当前日志绑定的输出文件 67 | file *os.File 68 | //是否打印调试debug信息 69 | debugClose bool 70 | //获取日志文件名和代码上述的runtime.Call 的函数调用层数 71 | calldDepth int 72 | } 73 | 74 | /* 75 | 创建一个日志 76 | out: 标准输出的文件io 77 | prefix: 日志的前缀 78 | flag: 当前日志头部信息的标记位 79 | */ 80 | func NewZinxLog(out io.Writer, prefix string, flag int) *ZinxLogger { 81 | 82 | //默认 debug打开, calledDepth深度为2,ZinxLogger对象调用日志打印方法最多调用两层到达output函数 83 | zlog := &ZinxLogger{out: out, prefix: prefix, flag: flag, file: nil, debugClose: false, calldDepth: 2} 84 | //设置log对象 回收资源 析构方法(不设置也可以,go的Gc会自动回收,强迫症没办法) 85 | runtime.SetFinalizer(zlog, CleanZinxLog) 86 | return zlog 87 | } 88 | 89 | /* 90 | 回收日志处理 91 | */ 92 | func CleanZinxLog(log *ZinxLogger) { 93 | log.closeFile() 94 | } 95 | 96 | /* 97 | 制作当条日志数据的 格式头信息 98 | */ 99 | func (log *ZinxLogger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int, level int) { 100 | //如果当前前缀字符串不为空,那么需要先写前缀 101 | if log.prefix != "" { 102 | buf.WriteByte('<') 103 | buf.WriteString(log.prefix) 104 | buf.WriteByte('>') 105 | } 106 | 107 | //已经设置了时间相关的标识位,那么需要加时间信息在日志头部 108 | if log.flag&(BitDate|BitTime|BitMicroSeconds) != 0 { 109 | //日期位被标记 110 | if log.flag&BitDate != 0 { 111 | year, month, day := t.Date() 112 | itoa(buf, year, 4) 113 | buf.WriteByte('/') // "2019/" 114 | itoa(buf, int(month), 2) 115 | buf.WriteByte('/') // "2019/04/" 116 | itoa(buf, day, 2) 117 | buf.WriteByte(' ') // "2019/04/11 " 118 | } 119 | 120 | //时钟位被标记 121 | if log.flag&(BitTime|BitMicroSeconds) != 0 { 122 | hour, min, sec := t.Clock() 123 | itoa(buf, hour, 2) 124 | buf.WriteByte(':') // "11:" 125 | itoa(buf, min, 2) 126 | buf.WriteByte(':') // "11:15:" 127 | itoa(buf, sec, 2) // "11:15:33" 128 | //微秒被标记 129 | if log.flag&BitMicroSeconds != 0 { 130 | buf.WriteByte('.') 131 | itoa(buf, t.Nanosecond()/1e3, 6) // "11:15:33.123123 132 | } 133 | buf.WriteByte(' ') 134 | } 135 | 136 | // 日志级别位被标记 137 | if log.flag&BitLevel != 0 { 138 | buf.WriteString(levels[level]) 139 | } 140 | 141 | //日志当前代码调用文件名名称位被标记 142 | if log.flag&(BitShortFile|BitLongFile) != 0 { 143 | //短文件名称 144 | if log.flag&BitShortFile != 0 { 145 | short := file 146 | for i := len(file) - 1; i > 0; i-- { 147 | if file[i] == '/' { 148 | //找到最后一个'/'之后的文件名称 如:/home/go/src/zinx.go 得到 "zinx.go" 149 | short = file[i+1:] 150 | break 151 | } 152 | } 153 | file = short 154 | } 155 | buf.WriteString(file) 156 | buf.WriteByte(':') 157 | itoa(buf, line, -1) //行数 158 | buf.WriteString(": ") 159 | } 160 | } 161 | } 162 | 163 | /* 164 | 输出日志文件,原方法 165 | */ 166 | func (log *ZinxLogger) OutPut(level int, s string) error { 167 | 168 | now := time.Now() // 得到当前时间 169 | var file string //当前调用日志接口的文件名称 170 | var line int //当前代码行数 171 | log.mu.Lock() 172 | defer log.mu.Unlock() 173 | 174 | if log.flag&(BitShortFile|BitLongFile) != 0 { 175 | log.mu.Unlock() 176 | var ok bool 177 | //得到当前调用者的文件名称和执行到的代码行数 178 | _, file, line, ok = runtime.Caller(log.calldDepth) 179 | if !ok { 180 | file = "unknown-file" 181 | line = 0 182 | } 183 | log.mu.Lock() 184 | } 185 | 186 | //清零buf 187 | log.buf.Reset() 188 | //写日志头 189 | log.formatHeader(&log.buf, now, file, line, level) 190 | //写日志内容 191 | log.buf.WriteString(s) 192 | //补充回车 193 | if len(s) > 0 && s[len(s)-1] != '\n' { 194 | log.buf.WriteByte('\n') 195 | } 196 | 197 | //将填充好的buf 写到IO输出上 198 | _, err := log.out.Write(log.buf.Bytes()) 199 | return err 200 | } 201 | 202 | // ====> Debug <==== 203 | func (log *ZinxLogger) Debugf(format string, v ...interface{}) { 204 | if log.debugClose == true { 205 | return 206 | } 207 | _ = log.OutPut(LogDebug, fmt.Sprintf(format, v...)) 208 | } 209 | 210 | func (log *ZinxLogger) Debug(v ...interface{}) { 211 | if log.debugClose == true { 212 | return 213 | } 214 | _ = log.OutPut(LogDebug, fmt.Sprintln(v...)) 215 | } 216 | 217 | // ====> Info <==== 218 | func (log *ZinxLogger) Infof(format string, v ...interface{}) { 219 | _ = log.OutPut(LogInfo, fmt.Sprintf(format, v...)) 220 | } 221 | 222 | func (log *ZinxLogger) Info(v ...interface{}) { 223 | _ = log.OutPut(LogInfo, fmt.Sprintln(v...)) 224 | } 225 | 226 | // ====> Warn <==== 227 | func (log *ZinxLogger) Warnf(format string, v ...interface{}) { 228 | _ = log.OutPut(LogWarn, fmt.Sprintf(format, v...)) 229 | } 230 | 231 | func (log *ZinxLogger) Warn(v ...interface{}) { 232 | _ = log.OutPut(LogWarn, fmt.Sprintln(v...)) 233 | } 234 | 235 | // ====> Error <==== 236 | func (log *ZinxLogger) Errorf(format string, v ...interface{}) { 237 | _ = log.OutPut(LogError, fmt.Sprintf(format, v...)) 238 | } 239 | 240 | func (log *ZinxLogger) Error(v ...interface{}) { 241 | _ = log.OutPut(LogError, fmt.Sprintln(v...)) 242 | } 243 | 244 | // ====> Fatal 需要终止程序 <==== 245 | func (log *ZinxLogger) Fatalf(format string, v ...interface{}) { 246 | _ = log.OutPut(LogFatal, fmt.Sprintf(format, v...)) 247 | os.Exit(1) 248 | } 249 | 250 | func (log *ZinxLogger) Fatal(v ...interface{}) { 251 | _ = log.OutPut(LogFatal, fmt.Sprintln(v...)) 252 | os.Exit(1) 253 | } 254 | 255 | // ====> Panic <==== 256 | func (log *ZinxLogger) Panicf(format string, v ...interface{}) { 257 | s := fmt.Sprintf(format, v...) 258 | _ = log.OutPut(LogPanic, s) 259 | panic(s) 260 | } 261 | 262 | func (log *ZinxLogger) Panic(v ...interface{}) { 263 | s := fmt.Sprintln(v...) 264 | _ = log.OutPut(LogPanic, s) 265 | panic(s) 266 | } 267 | 268 | // ====> Stack <==== 269 | func (log *ZinxLogger) Stack(v ...interface{}) { 270 | s := fmt.Sprint(v...) 271 | s += "\n" 272 | buf := make([]byte, LOG_MAX_BUF) 273 | n := runtime.Stack(buf, true) //得到当前堆栈信息 274 | s += string(buf[:n]) 275 | s += "\n" 276 | _ = log.OutPut(LogError, s) 277 | } 278 | 279 | //获取当前日志bitmap标记 280 | func (log *ZinxLogger) Flags() int { 281 | log.mu.Lock() 282 | defer log.mu.Unlock() 283 | return log.flag 284 | } 285 | 286 | //重新设置日志Flags bitMap 标记位 287 | func (log *ZinxLogger) ResetFlags(flag int) { 288 | log.mu.Lock() 289 | defer log.mu.Unlock() 290 | log.flag = flag 291 | } 292 | 293 | //添加flag标记 294 | func (log *ZinxLogger) AddFlag(flag int) { 295 | log.mu.Lock() 296 | defer log.mu.Unlock() 297 | log.flag |= flag 298 | } 299 | 300 | //设置日志的 用户自定义前缀字符串 301 | func (log *ZinxLogger) SetPrefix(prefix string) { 302 | log.mu.Lock() 303 | defer log.mu.Unlock() 304 | log.prefix = prefix 305 | } 306 | 307 | //设置日志文件输出 308 | func (log *ZinxLogger) SetLogFile(fileDir string, fileName string) { 309 | var file *os.File 310 | 311 | //创建日志文件夹 312 | _ = mkdirLog(fileDir) 313 | 314 | fullPath := fileDir + "/" + fileName 315 | if log.checkFileExist(fullPath) { 316 | //文件存在,打开 317 | file, _ = os.OpenFile(fullPath, os.O_APPEND|os.O_RDWR, 0644) 318 | } else { 319 | //文件不存在,创建 320 | file, _ = os.OpenFile(fullPath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) 321 | } 322 | 323 | log.mu.Lock() 324 | defer log.mu.Unlock() 325 | 326 | //关闭之前绑定的文件 327 | log.closeFile() 328 | log.file = file 329 | log.out = file 330 | } 331 | 332 | //关闭日志绑定的文件 333 | func (log *ZinxLogger) closeFile() { 334 | if log.file != nil { 335 | _ = log.file.Close() 336 | log.file = nil 337 | log.out = os.Stderr 338 | } 339 | } 340 | 341 | func (log *ZinxLogger) CloseDebug() { 342 | log.debugClose = true 343 | } 344 | 345 | func (log *ZinxLogger) OpenDebug() { 346 | log.debugClose = false 347 | } 348 | 349 | // ================== 以下是一些工具方法 ========== 350 | 351 | //判断日志文件是否存在 352 | func (log *ZinxLogger) checkFileExist(filename string) bool { 353 | exist := true 354 | if _, err := os.Stat(filename); os.IsNotExist(err) { 355 | exist = false 356 | } 357 | return exist 358 | } 359 | 360 | func mkdirLog(dir string) (e error) { 361 | _, er := os.Stat(dir) 362 | b := er == nil || os.IsExist(er) 363 | if !b { 364 | if err := os.MkdirAll(dir, 0775); err != nil { 365 | if os.IsPermission(err) { 366 | e = err 367 | } 368 | } 369 | } 370 | return 371 | } 372 | 373 | //将一个整形转换成一个固定长度的字符串,字符串宽度应该是大于0的 374 | //要确保buffer是有容量空间的 375 | func itoa(buf *bytes.Buffer, i int, wid int) { 376 | var u uint = uint(i) 377 | if u == 0 && wid <= 1 { 378 | buf.WriteByte('0') 379 | return 380 | } 381 | 382 | // Assemble decimal in reverse order. 383 | var b [32]byte 384 | bp := len(b) 385 | for ; u > 0 || wid > 0; u /= 10 { 386 | bp-- 387 | wid-- 388 | b[bp] = byte(u%10) + '0' 389 | } 390 | 391 | // avoid slicing b to avoid an allocation. 392 | for bp < len(b) { 393 | buf.WriteByte(b[bp]) 394 | bp++ 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /zlog/zlogger_test.go: -------------------------------------------------------------------------------- 1 | package zlog 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestStdZLog(t *testing.T) { 8 | 9 | //测试 默认debug输出 10 | Debug("zinx debug content1") 11 | Debug("zinx debug content2") 12 | 13 | Debugf(" zinx debug a = %d\n", 10) 14 | 15 | //设置log标记位,加上长文件名称 和 微秒 标记 16 | ResetFlags(BitDate | BitLongFile | BitLevel) 17 | Info("zinx info content") 18 | 19 | //设置日志前缀,主要标记当前日志模块 20 | SetPrefix("MODULE") 21 | Error("zinx error content") 22 | 23 | //添加标记位 24 | AddFlag(BitShortFile | BitTime) 25 | Stack(" Zinx Stack! ") 26 | 27 | //设置日志写入文件 28 | SetLogFile("./log", "testfile.log") 29 | Debug("===> zinx debug content ~~666") 30 | Debug("===> zinx debug content ~~888") 31 | Error("===> zinx Error!!!! ~~~555~~~") 32 | 33 | //关闭debug调试 34 | CloseDebug() 35 | Debug("===> 我不应该出现~!") 36 | Debug("===> 我不应该出现~!") 37 | Error("===> zinx Error after debug close !!!!") 38 | 39 | } 40 | -------------------------------------------------------------------------------- /znet/connection.go: -------------------------------------------------------------------------------- 1 | package znet 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "gfep/bridge" 7 | "gfep/utils" 8 | "gfep/ziface" 9 | "gfep/zptl" 10 | "net" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | // Connection connection 16 | type Connection struct { 17 | //当前Conn属于哪个Server 18 | TCPServer ziface.IServer 19 | //当前连接的socket TCP套接字 20 | Conn *net.TCPConn 21 | //当前连接的ID 也可以称作为SessionID,ID全局唯一 22 | ConnID uint32 23 | //当前连接的关闭状态 24 | isClosed bool 25 | needStop bool 26 | //消息管理MsgId和对应处理方法的消息管理模块 27 | MsgHandler ziface.IMsgHandle 28 | //告知该链接已经退出/停止的channel 29 | ExitBuffChan chan bool 30 | //无缓冲管道,用于读、写两个goroutine之间的消息通信 31 | msgChan chan []byte 32 | //有缓冲管道,用于读、写两个goroutine之间的消息通信 33 | msgBuffChan chan []byte 34 | msgBuffChanIsClosed bool 35 | 36 | //链接属性 37 | // property map[string]interface{} 38 | //保护链接属性修改的锁 39 | propertyLock sync.RWMutex 40 | 41 | //链接属性: 先不用map节约资源 42 | status int //当前状态 43 | addr string //终端/主站地址字符串 44 | ctime time.Time //连接时间 45 | ltime time.Time //登录时间 46 | htime time.Time //心跳时间 47 | rtime time.Time //最近一次报文接收时间 48 | binfo *bridge.Conn //桥接信息 49 | // 级联终端信息 50 | } 51 | 52 | // NewConntion 创建连接的方法 53 | func NewConntion(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection { 54 | //初始化Conn属性 55 | c := &Connection{ 56 | TCPServer: server, 57 | Conn: conn, 58 | ConnID: connID, 59 | isClosed: false, 60 | needStop: false, 61 | MsgHandler: msgHandler, 62 | ExitBuffChan: make(chan bool, 1), 63 | msgChan: make(chan []byte), 64 | msgBuffChan: make(chan []byte, utils.GlobalObject.MaxMsgChanLen), 65 | msgBuffChanIsClosed: false, 66 | // property: make(map[string]interface{}), 67 | binfo: nil, 68 | } 69 | 70 | //将新创建的Conn添加到链接管理中 71 | c.TCPServer.GetConnMgr().Add(c) 72 | return c 73 | } 74 | 75 | // StartWriter 写消息Goroutine, 用户将数据发送给客户端 76 | func (c *Connection) StartWriter() { 77 | // fmt.Println("[Writer Goroutine is running]") 78 | // defer fmt.Println(c.RemoteAddr().String(), "[conn Writer exit!]") 79 | 80 | for { 81 | select { 82 | case data := <-c.msgChan: 83 | //有数据要写给客户端 84 | if _, err := c.Conn.Write(data); err != nil { 85 | _ = c.Conn.Close() 86 | fmt.Println("Send Data error:, ", err, " Conn Writer exit") 87 | return 88 | } 89 | //fmt.Printf("Send data succ! data = %+v\n", data) 90 | case data, ok := <-c.msgBuffChan: 91 | if ok { 92 | //有数据要写给客户端 93 | if _, err := c.Conn.Write(data); err != nil { 94 | c.closeMsgBuffChan() 95 | _ = c.Conn.Close() 96 | fmt.Println("Send Buff Data error:, ", err, " Conn Writer exit") 97 | return 98 | } 99 | } else { 100 | fmt.Println("msgBuffChan is Closed") 101 | } 102 | 103 | case <-c.ExitBuffChan: 104 | return 105 | } 106 | } 107 | } 108 | 109 | func cbRecvPacket(ptype uint32, data []byte, arg interface{}) { 110 | c, ok := arg.(*Connection) 111 | if ok { 112 | //得到当前客户端请求的Request数据 113 | req := Request{ 114 | conn: c, 115 | msg: NewMsgPackage(ptype, data), 116 | } 117 | 118 | if utils.GlobalObject.WorkerPoolSize > 0 { 119 | //已经启动工作池机制,将消息交给Worker处理 120 | c.MsgHandler.SendMsgToTaskQueue(&req) 121 | } else { 122 | //从绑定好的消息和对应的处理方法中执行对应的Handle方法 123 | go c.MsgHandler.DoMsgHandler(&req) 124 | } 125 | } else { 126 | fmt.Println("arg is not Connection") 127 | } 128 | } 129 | 130 | // StartReader 读消息Goroutine,用于从客户端中读取数据 131 | func (c *Connection) StartReader() { 132 | // fmt.Println("[Reader Goroutine is running]") 133 | // defer fmt.Println(c.RemoteAddr().String(), "[conn Reader exit!]") 134 | 135 | ptlChk := zptl.NewChkfrm(zptl.PTL_698_45|zptl.PTL_NW|zptl.PTL_1376_1, 1000, cbRecvPacket, c) 136 | rbuf := make([]byte, zptl.PmaxPtlFrameLen/2) 137 | 138 | for { 139 | rlen, err := c.Conn.Read(rbuf) 140 | if err != nil { 141 | break 142 | } 143 | ptlChk.Chkfrm(rbuf[0:rlen]) 144 | if c.needStop { 145 | break 146 | } 147 | } 148 | c.Stop() 149 | } 150 | 151 | // Start 启动连接,让当前连接开始工作 152 | func (c *Connection) Start() { 153 | //按照用户传递进来的创建连接时需要处理的业务,执行钩子方法 154 | c.TCPServer.CallOnConnStart(c) 155 | //1 开启用户从客户端读取数据流程的Goroutine 156 | go c.StartReader() 157 | //2 开启用于写回客户端数据流程的Goroutine 158 | go c.StartWriter() 159 | } 160 | 161 | func (c *Connection) closeMsgBuffChan() { 162 | c.propertyLock.Lock() 163 | if !c.msgBuffChanIsClosed { 164 | c.msgBuffChanIsClosed = true 165 | close(c.msgBuffChan) 166 | c.msgBuffChan = nil 167 | } 168 | c.propertyLock.Unlock() 169 | } 170 | 171 | // Stop 停止连接,结束当前连接状态 172 | func (c *Connection) Stop() { 173 | // fmt.Println("Conn Stop()...ConnID = ", c.ConnID) 174 | //如果当前链接已经关闭 175 | if c.isClosed { 176 | return 177 | } 178 | c.isClosed = true 179 | 180 | //如果用户注册了该链接的关闭回调业务,那么在此刻应该显示调用 181 | c.TCPServer.CallOnConnStop(c) 182 | 183 | // 关闭socket链接 184 | _ = c.Conn.Close() 185 | //关闭Writer 186 | c.ExitBuffChan <- true 187 | 188 | //将链接从连接管理器中删除 189 | c.TCPServer.GetConnMgr().Remove(c) 190 | 191 | //关闭该链接全部管道 192 | close(c.ExitBuffChan) 193 | c.closeMsgBuffChan() 194 | // c.property = nil 195 | } 196 | 197 | // IsStop 是否停止连接 198 | func (c *Connection) IsStop() bool { 199 | if c.needStop { 200 | return true 201 | } 202 | return c.isClosed 203 | } 204 | 205 | // NeedStop 需要停止连接 206 | func (c *Connection) NeedStop() { 207 | c.needStop = true 208 | } 209 | 210 | // GetTCPConnection 从当前连接获取原始的socket TCPConn 211 | func (c *Connection) GetTCPConnection() *net.TCPConn { 212 | return c.Conn 213 | } 214 | 215 | // GetConnID 获取当前连接ID 216 | func (c *Connection) GetConnID() uint32 { 217 | return c.ConnID 218 | } 219 | 220 | // RemoteAddr 获取远程客户端地址信息 221 | func (c *Connection) RemoteAddr() net.Addr { 222 | return c.Conn.RemoteAddr() 223 | } 224 | 225 | // SendMsg 直接将Message数据发送数据给远程的TCP客户端 226 | func (c *Connection) SendMsg(data []byte) error { 227 | if c.isClosed { 228 | return errors.New("Connection closed when send msg") 229 | } 230 | 231 | defer func() { 232 | if err := recover(); err != nil { 233 | } 234 | }() 235 | 236 | //写回客户端 237 | c.msgChan <- data 238 | 239 | return nil 240 | } 241 | 242 | // SendBuffMsg 直接将Message数据发送数据给远程的TCP客户端 243 | func (c *Connection) SendBuffMsg(data []byte) error { 244 | if c.msgBuffChanIsClosed { 245 | return errors.New("Connection closed when send buff msg") 246 | } 247 | 248 | defer func() { 249 | if err := recover(); err != nil { 250 | } 251 | }() 252 | 253 | //写回客户端, 高并发下有可能msgBuffChan被关闭 254 | if c.msgBuffChan != nil { 255 | c.msgBuffChan <- data 256 | } 257 | 258 | return nil 259 | } 260 | 261 | // SendMsgByConnID 直接将Message数据发送数据给远程的TCP客户端 262 | func (c *Connection) SendMsgByConnID(connID uint32, data []byte) error { 263 | conn, err := c.TCPServer.GetConnMgr().Get(connID) 264 | if err != nil { 265 | return errors.New("Connection closed when send msg") 266 | } 267 | 268 | return conn.SendBuffMsg(data) 269 | } 270 | 271 | // SetProperty 设置链接属性 272 | func (c *Connection) SetProperty(key string, value interface{}) { 273 | c.propertyLock.Lock() 274 | defer c.propertyLock.Unlock() 275 | 276 | // c.property[key] = value 277 | switch key { 278 | case "status": 279 | if v, ok := value.(int); ok { 280 | c.status = v 281 | } 282 | case "addr": 283 | if v, ok := value.(string); ok { 284 | c.addr = v 285 | } 286 | case "ctime": 287 | if v, ok := value.(time.Time); ok { 288 | c.ctime = v 289 | } 290 | case "ltime": 291 | if v, ok := value.(time.Time); ok { 292 | c.ltime = v 293 | } 294 | case "htime": 295 | if v, ok := value.(time.Time); ok { 296 | c.htime = v 297 | } 298 | case "rtime": 299 | if v, ok := value.(time.Time); ok { 300 | c.rtime = v 301 | } 302 | case "bridge": 303 | if v, ok := value.(*bridge.Conn); ok { 304 | c.binfo = v 305 | } 306 | } 307 | } 308 | 309 | // GetProperty 获取链接属性 310 | func (c *Connection) GetProperty(key string) (interface{}, error) { 311 | c.propertyLock.RLock() 312 | defer c.propertyLock.RUnlock() 313 | 314 | // if value, ok := c.property[key]; ok { 315 | // return value, nil 316 | // } 317 | switch key { 318 | case "status": 319 | return c.status, nil 320 | case "addr": 321 | return c.addr, nil 322 | case "ctime": 323 | return c.ctime, nil 324 | case "ltime": 325 | return c.ltime, nil 326 | case "htime": 327 | return c.htime, nil 328 | case "rtime": 329 | return c.rtime, nil 330 | case "bridge": 331 | if c.binfo == nil { 332 | return nil, errors.New("binfo is nil") 333 | } 334 | return c.binfo, nil 335 | } 336 | return nil, errors.New("no property found") 337 | } 338 | 339 | // RemoveProperty 移除链接属性 340 | func (c *Connection) RemoveProperty(key string) { 341 | c.propertyLock.Lock() 342 | defer c.propertyLock.Unlock() 343 | 344 | switch key { 345 | case "bridge": 346 | c.binfo = nil 347 | } 348 | // delete(c.property, key) 349 | c.status = 0 350 | } 351 | -------------------------------------------------------------------------------- /znet/connmanager.go: -------------------------------------------------------------------------------- 1 | package znet 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "gfep/ziface" 7 | "sync" 8 | ) 9 | 10 | // ConnManager 连接管理模块 11 | type ConnManager struct { 12 | connections map[uint32]ziface.IConnection //管理的连接信息 13 | connLock sync.RWMutex //读写连接的读写锁 14 | } 15 | 16 | // NewConnManager 创建一个链接管理 17 | func NewConnManager() *ConnManager { 18 | return &ConnManager{ 19 | connections: make(map[uint32]ziface.IConnection), 20 | } 21 | } 22 | 23 | // Add 添加链接 24 | func (connMgr *ConnManager) Add(conn ziface.IConnection) { 25 | //保护共享资源Map 加写锁 26 | connMgr.connLock.Lock() 27 | defer connMgr.connLock.Unlock() 28 | 29 | //将conn连接添加到ConnMananger中 30 | connMgr.connections[conn.GetConnID()] = conn 31 | 32 | fmt.Println("connection add to ConnManager successfully: conn num = ", connMgr.Len()) 33 | } 34 | 35 | // Remove 删除连接 36 | func (connMgr *ConnManager) Remove(conn ziface.IConnection) { 37 | //保护共享资源Map 加写锁 38 | connMgr.connLock.Lock() 39 | defer connMgr.connLock.Unlock() 40 | 41 | //删除连接信息 42 | delete(connMgr.connections, conn.GetConnID()) 43 | 44 | fmt.Println("connection Remove ConnID=", conn.GetConnID(), " successfully: conn num = ", connMgr.Len()) 45 | } 46 | 47 | // Get 利用ConnID获取链接 48 | func (connMgr *ConnManager) Get(connID uint32) (ziface.IConnection, error) { 49 | //保护共享资源Map 加读锁 50 | connMgr.connLock.RLock() 51 | defer connMgr.connLock.RUnlock() 52 | 53 | if conn, ok := connMgr.connections[connID]; ok { 54 | return conn, nil 55 | } 56 | return nil, errors.New("connection not found") 57 | } 58 | 59 | // Len 获取当前连接 60 | func (connMgr *ConnManager) Len() int { 61 | return len(connMgr.connections) 62 | } 63 | 64 | // ClearConn 清除并停止所有连接 65 | func (connMgr *ConnManager) ClearConn() { 66 | //保护共享资源Map 加写锁 67 | connMgr.connLock.Lock() 68 | defer connMgr.connLock.Unlock() 69 | 70 | //停止并删除全部的连接信息 71 | for connID, conn := range connMgr.connections { 72 | //停止 73 | conn.Stop() 74 | //删除 75 | delete(connMgr.connections, connID) 76 | } 77 | 78 | fmt.Println("Clear All Connections successfully: conn num = ", connMgr.Len()) 79 | } 80 | -------------------------------------------------------------------------------- /znet/message.go: -------------------------------------------------------------------------------- 1 | package znet 2 | 3 | // Message message 4 | type Message struct { 5 | DataLen uint32 //消息的长度 6 | ID uint32 //消息的ID 7 | Data []byte //消息的内容 8 | } 9 | 10 | // NewMsgPackage 创建一个Message消息包 11 | func NewMsgPackage(id uint32, data []byte) *Message { 12 | return &Message{ 13 | DataLen: uint32(len(data)), 14 | ID: id, 15 | Data: data, 16 | } 17 | } 18 | 19 | // GetDataLen 获取消息数据段长度 20 | func (msg *Message) GetDataLen() uint32 { 21 | return msg.DataLen 22 | } 23 | 24 | // GetMsgID 获取消息ID 25 | func (msg *Message) GetMsgID() uint32 { 26 | return msg.ID 27 | } 28 | 29 | // GetData 获取消息内容 30 | func (msg *Message) GetData() []byte { 31 | return msg.Data 32 | } 33 | 34 | // SetDataLen 设置消息数据段长度 35 | func (msg *Message) SetDataLen(len uint32) { 36 | msg.DataLen = len 37 | } 38 | 39 | // SetMsgID 设计消息ID 40 | func (msg *Message) SetMsgID(msgID uint32) { 41 | msg.ID = msgID 42 | } 43 | 44 | // SetData 设计消息内容 45 | func (msg *Message) SetData(data []byte) { 46 | msg.Data = data 47 | } 48 | -------------------------------------------------------------------------------- /znet/msghandler.go: -------------------------------------------------------------------------------- 1 | package znet 2 | 3 | import ( 4 | "fmt" 5 | "gfep/utils" 6 | "gfep/ziface" 7 | "strconv" 8 | ) 9 | 10 | // MsgHandle msg handle 11 | type MsgHandle struct { 12 | Apis map[uint32]ziface.IRouter //存放每个MsgId 所对应的处理方法的map属性 13 | WorkerPoolSize uint32 //业务工作Worker池的数量 14 | TaskQueue []chan ziface.IRequest //Worker负责取任务的消息队列 15 | } 16 | 17 | // NewMsgHandle new msg handle 18 | func NewMsgHandle() *MsgHandle { 19 | return &MsgHandle{ 20 | Apis: make(map[uint32]ziface.IRouter), 21 | WorkerPoolSize: utils.GlobalObject.WorkerPoolSize, 22 | //一个worker对应一个queue 23 | TaskQueue: make([]chan ziface.IRequest, utils.GlobalObject.WorkerPoolSize), 24 | } 25 | } 26 | 27 | // SendMsgToTaskQueue 将消息交给TaskQueue,由worker进行处理 28 | func (mh *MsgHandle) SendMsgToTaskQueue(request ziface.IRequest) { 29 | //根据ConnID来分配当前的连接应该由哪个worker负责处理 30 | //轮询的平均分配法则 31 | 32 | //得到需要处理此条连接的workerID 33 | workerID := request.GetConnection().GetConnID() % mh.WorkerPoolSize 34 | //fmt.Println("Add ConnID=", request.GetConnection().GetConnID()," request msgID=", request.GetMsgID(), "to workerID=", workerID) 35 | //将请求消息发送给任务队列 36 | mh.TaskQueue[workerID] <- request 37 | } 38 | 39 | // DoMsgHandler 马上以非阻塞方式处理消息 40 | func (mh *MsgHandle) DoMsgHandler(request ziface.IRequest) { 41 | handler, ok := mh.Apis[request.GetMsgID()] 42 | if !ok { 43 | fmt.Println("api msgID = ", request.GetMsgID(), " is not FOUND!") 44 | return 45 | } 46 | 47 | //执行对应处理方法 48 | handler.PreHandle(request) 49 | handler.Handle(request) 50 | handler.PostHandle(request) 51 | } 52 | 53 | // AddRouter 为消息添加具体的处理逻辑 54 | func (mh *MsgHandle) AddRouter(msgID uint32, router ziface.IRouter) { 55 | //1 判断当前msg绑定的API处理方法是否已经存在 56 | if _, ok := mh.Apis[msgID]; ok { 57 | panic("repeated api , msgID = " + strconv.Itoa(int(msgID))) 58 | } 59 | //2 添加msg与api的绑定关系 60 | mh.Apis[msgID] = router 61 | fmt.Println("Add api msgID = ", msgID) 62 | } 63 | 64 | // StartOneWorker 启动一个Worker工作流程 65 | func (mh *MsgHandle) StartOneWorker(workerID int, taskQueue chan ziface.IRequest) { 66 | fmt.Println("Worker ID = ", workerID, " is started.") 67 | //不断的等待队列中的消息 68 | for { 69 | select { 70 | //有消息则取出队列的Request,并执行绑定的业务方法 71 | case request := <-taskQueue: 72 | mh.DoMsgHandler(request) 73 | } 74 | } 75 | } 76 | 77 | // StartWorkerPool 启动worker工作池 78 | func (mh *MsgHandle) StartWorkerPool() { 79 | //遍历需要启动worker的数量,依此启动 80 | for i := 0; i < int(mh.WorkerPoolSize); i++ { 81 | //一个worker被启动 82 | //给当前worker对应的任务队列开辟空间 83 | mh.TaskQueue[i] = make(chan ziface.IRequest, utils.GlobalObject.MaxWorkerTaskLen) 84 | //启动当前Worker,阻塞的等待对应的任务队列是否有消息传递进来 85 | go mh.StartOneWorker(i, mh.TaskQueue[i]) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /znet/request.go: -------------------------------------------------------------------------------- 1 | package znet 2 | 3 | import "gfep/ziface" 4 | 5 | // Request request 6 | type Request struct { 7 | conn ziface.IConnection //已经和客户端建立好的 链接 8 | msg ziface.IMessage //客户端请求的数据 9 | } 10 | 11 | // GetConnection 获取请求连接信息 12 | func (r *Request) GetConnection() ziface.IConnection { 13 | return r.conn 14 | } 15 | 16 | // GetData 获取请求消息的数据 17 | func (r *Request) GetData() []byte { 18 | return r.msg.GetData() 19 | } 20 | 21 | // GetMsgID 获取请求的消息的ID 22 | func (r *Request) GetMsgID() uint32 { 23 | return r.msg.GetMsgID() 24 | } 25 | -------------------------------------------------------------------------------- /znet/router.go: -------------------------------------------------------------------------------- 1 | package znet 2 | 3 | import "gfep/ziface" 4 | 5 | // BaseRouter 实现router时,先嵌入这个基类,然后根据需要对这个基类的方法进行重写 6 | type BaseRouter struct{} 7 | 8 | //这里之所以BaseRouter的方法都为空, 9 | // 是因为有的Router不希望有PreHandle或PostHandle 10 | // 所以Router全部继承BaseRouter的好处是,不需要实现PreHandle和PostHandle也可以实例化 11 | 12 | // PreHandle pre handle 13 | func (br *BaseRouter) PreHandle(req ziface.IRequest) {} 14 | 15 | // Handle handle 16 | func (br *BaseRouter) Handle(req ziface.IRequest) {} 17 | 18 | // PostHandle post handle 19 | func (br *BaseRouter) PostHandle(req ziface.IRequest) {} 20 | -------------------------------------------------------------------------------- /znet/server.go: -------------------------------------------------------------------------------- 1 | package znet 2 | 3 | import ( 4 | "fmt" 5 | "gfep/utils" 6 | "gfep/ziface" 7 | "net" 8 | "time" 9 | ) 10 | 11 | var zinxLogo = ` 12 | ###### ######## ######## ######## 13 | ## ## ## ## ## ## 14 | ## ## ## ## ## 15 | ## #### ###### ###### ######## 16 | ## ## ## ## ## 17 | ## ## ## ## ## 18 | ###### ## ######## ## 19 | ` 20 | var topLine = `┌───────────────────────────────────────────────────┐` 21 | var borderLine = `│` 22 | var bottomLine = `└───────────────────────────────────────────────────┘` 23 | 24 | // Server iServer 接口实现,定义一个Server服务类 25 | type Server struct { 26 | //服务器的名称 27 | Name string 28 | //tcp4 or other 29 | IPVersion string 30 | //服务绑定的IP地址 31 | IP string 32 | //服务绑定的端口 33 | Port int 34 | //当前Server的消息管理模块,用来绑定MsgId和对应的处理方法 35 | msgHandler ziface.IMsgHandle 36 | //当前Server的链接管理器 37 | ConnMgr ziface.IConnManager 38 | //该Server的连接创建时Hook函数 39 | OnConnStart func(conn ziface.IConnection) 40 | //该Server的连接断开时的Hook函数 41 | OnConnStop func(conn ziface.IConnection) 42 | } 43 | 44 | // NewServer 创建一个服务器句柄 45 | func NewServer() ziface.IServer { 46 | 47 | s := &Server{ 48 | Name: utils.GlobalObject.Name, 49 | IPVersion: "tcp4", 50 | IP: utils.GlobalObject.Host, 51 | Port: utils.GlobalObject.TCPPort, 52 | msgHandler: NewMsgHandle(), 53 | ConnMgr: NewConnManager(), 54 | } 55 | return s 56 | } 57 | 58 | //============== 实现 ziface.IServer 里的全部接口方法 ======== 59 | 60 | // Start 开启网络服务 61 | func (s *Server) Start() { 62 | fmt.Printf("[START] Server name: %s,listenner at IP: %s, Port %d is starting\n", s.Name, s.IP, s.Port) 63 | 64 | //开启一个go去做服务端Linster业务 65 | go func() { 66 | //0 启动worker工作池机制 67 | s.msgHandler.StartWorkerPool() 68 | 69 | //1 获取一个TCP的Addr 70 | addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port)) 71 | if err != nil { 72 | fmt.Println("resolve tcp addr err: ", err) 73 | return 74 | } 75 | 76 | //2 监听服务器地址 77 | listener, err := net.ListenTCP(s.IPVersion, addr) 78 | if err != nil { 79 | fmt.Println("listen", s.IPVersion, "err", err) 80 | return 81 | } 82 | 83 | //已经监听成功 84 | fmt.Println("start Zinx server ", s.Name, " succ, now listenning...") 85 | 86 | //TODO server.go 应该有一个自动生成ID的方法 87 | cid := uint32(0) 88 | 89 | //3 启动server网络连接业务 90 | for { 91 | //3.1 阻塞等待客户端建立连接请求 92 | conn, err := listener.AcceptTCP() 93 | if err != nil { 94 | fmt.Println("Accept err ", err) 95 | continue 96 | } 97 | fmt.Println("Get conn remote addr = ", conn.RemoteAddr().String()) 98 | 99 | //3.2 设置服务器最大连接控制,如果超过最大连接,那么则关闭此新的连接 100 | if s.ConnMgr.Len() >= utils.GlobalObject.MaxConn { 101 | _ = conn.Close() 102 | continue 103 | } 104 | 105 | _ = conn.SetKeepAlive(true) 106 | _ = conn.SetKeepAlivePeriod(time.Minute * 2) 107 | _ = conn.SetNoDelay(true) 108 | _ = conn.SetReadBuffer(2200) 109 | _ = conn.SetWriteBuffer(2200) 110 | 111 | //3.3 处理该新连接请求的 业务 方法, 此时应该有 handler 和 conn是绑定的 112 | dealConn := NewConntion(s, conn, cid, s.msgHandler) 113 | cid++ 114 | 115 | //3.4 启动当前链接的处理业务 116 | go dealConn.Start() 117 | } 118 | }() 119 | } 120 | 121 | // Stop 停止服务 122 | func (s *Server) Stop() { 123 | fmt.Println("[STOP] Zinx server , name ", s.Name) 124 | 125 | //将其他需要清理的连接信息或者其他信息 也要一并停止或者清理 126 | s.ConnMgr.ClearConn() 127 | } 128 | 129 | // Serve 运行服务 130 | func (s *Server) Serve() { 131 | s.Start() 132 | 133 | //TODO Server.Serve() 是否在启动服务的时候 还要处理其他的事情呢 可以在这里添加 134 | 135 | //阻塞,否则主Go退出, listenner的go将会退出 136 | select {} 137 | } 138 | 139 | // AddRouter 路由功能:给当前服务注册一个路由业务方法,供客户端链接处理使用 140 | func (s *Server) AddRouter(msgID uint32, router ziface.IRouter) { 141 | s.msgHandler.AddRouter(msgID, router) 142 | } 143 | 144 | // GetConnMgr 得到链接管理 145 | func (s *Server) GetConnMgr() ziface.IConnManager { 146 | return s.ConnMgr 147 | } 148 | 149 | // SetOnConnStart 设置该Server的连接创建时Hook函数 150 | func (s *Server) SetOnConnStart(hookFunc func(ziface.IConnection)) { 151 | s.OnConnStart = hookFunc 152 | } 153 | 154 | // SetOnConnStop 设置该Server的连接断开时的Hook函数 155 | func (s *Server) SetOnConnStop(hookFunc func(ziface.IConnection)) { 156 | s.OnConnStop = hookFunc 157 | } 158 | 159 | // CallOnConnStart 调用连接OnConnStart Hook函数 160 | func (s *Server) CallOnConnStart(conn ziface.IConnection) { 161 | if s.OnConnStart != nil { 162 | fmt.Println("---> CallOnConnStart....") 163 | s.OnConnStart(conn) 164 | } 165 | } 166 | 167 | // CallOnConnStop 调用连接OnConnStop Hook函数 168 | func (s *Server) CallOnConnStop(conn ziface.IConnection) { 169 | if s.OnConnStop != nil { 170 | fmt.Println("---> CallOnConnStop....") 171 | s.OnConnStop(conn) 172 | } 173 | } 174 | 175 | func init() { 176 | fmt.Println(zinxLogo) 177 | fmt.Println(topLine) 178 | fmt.Printf("%s [Github] https://github.com/liuning587/gfep %s\n", borderLine, borderLine) 179 | fmt.Println(bottomLine) 180 | fmt.Printf("[gfep] Version: %s, MaxConn: %d, MaxPacketSize: %d\n", 181 | utils.GlobalObject.Version, 182 | utils.GlobalObject.MaxConn, 183 | utils.GlobalObject.MaxPacketSize) 184 | } 185 | -------------------------------------------------------------------------------- /znet/server_test.go: -------------------------------------------------------------------------------- 1 | package znet 2 | 3 | import ( 4 | "fmt" 5 | "gfep/ziface" 6 | "net" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | // ClientTest 模拟客户端 12 | func ClientTest() { 13 | 14 | fmt.Println("Client Test ... start") 15 | //3秒之后发起测试请求,给服务端开启服务的机会 16 | time.Sleep(3 * time.Second) 17 | 18 | conn, err := net.Dial("tcp", "127.0.0.1:7777") 19 | if err != nil { 20 | fmt.Println("client start err, exit!") 21 | return 22 | } 23 | 24 | for { 25 | _, err := conn.Write([]byte("Zinx V0.2 test")) 26 | if err != nil { 27 | fmt.Println("write error err ", err) 28 | return 29 | } 30 | 31 | buf := make([]byte, 512) 32 | cnt, err := conn.Read(buf) 33 | if err != nil { 34 | fmt.Println("read buf error ") 35 | return 36 | } 37 | 38 | fmt.Printf(" server call back : %s, cnt = %d\n", buf, cnt) 39 | 40 | time.Sleep(1 * time.Second) 41 | } 42 | } 43 | 44 | /* 45 | //Server 模块的测试函数 46 | func TestServer(t *testing.T) { 47 | 48 | 49 | // 服务端测试 50 | //1 创建一个server 句柄 s 51 | s := NewServer("[zinx V0.1]") 52 | 53 | // 客户端测试 54 | go ClientTest() 55 | 56 | //2 开启服务 57 | s.Serve() 58 | } 59 | */ 60 | 61 | //ping test 自定义路由 62 | type PingRouter struct { 63 | BaseRouter 64 | } 65 | 66 | // PreHandle Test PreHandle 67 | func (p *PingRouter) PreHandle(request ziface.IRequest) { 68 | fmt.Println("Call Router PreHandle") 69 | _, err := request.GetConnection().GetTCPConnection().Write([]byte("before ping ....\n")) 70 | if err != nil { 71 | fmt.Println("call back ping ping ping error") 72 | } 73 | } 74 | 75 | // Handle Test Handle 76 | func (p *PingRouter) Handle(request ziface.IRequest) { 77 | fmt.Println("Call PingRouter Handle") 78 | _, err := request.GetConnection().GetTCPConnection().Write([]byte("ping...ping...ping\n")) 79 | if err != nil { 80 | fmt.Println("call back ping ping ping error") 81 | } 82 | } 83 | 84 | // PostHandle Test PostHandle 85 | func (p *PingRouter) PostHandle(request ziface.IRequest) { 86 | fmt.Println("Call Router PostHandle") 87 | _, err := request.GetConnection().GetTCPConnection().Write([]byte("After ping .....\n")) 88 | if err != nil { 89 | fmt.Println("call back ping ping ping error") 90 | } 91 | } 92 | 93 | func TestServerV0_3(t *testing.T) { 94 | //创建一个server句柄 95 | s := NewServer() 96 | 97 | s.AddRouter(0, &PingRouter{}) 98 | 99 | // 客户端测试 100 | go ClientTest() 101 | 102 | //2 开启服务 103 | s.Serve() 104 | } 105 | -------------------------------------------------------------------------------- /zptl/ptl.go: -------------------------------------------------------------------------------- 1 | package zptl 2 | 3 | const ( 4 | PTL_1376_1 = 0x0001 /**< 1376.1协议 */ 5 | PTL_1376_2 = 0x0002 /**< 1376.2协议 */ 6 | PTL_645_97 = 0x0004 /**< 645-97协议 */ 7 | PTL_645_07 = 0x0008 /**< 645-07协议 */ 8 | PTL_698_45 = 0x0010 /**< 698.45协议 */ 9 | PTL_NW = 0x0020 /**< 南网2013协议 */ 10 | PTL_NWM = 0x0040 /**< 南网2013协议(密) */ 11 | PTL_SSAL = 0x0080 /**< 国网安全应用层协议 */ 12 | PTL_ALL = 0x00ff /**< 以上任意协议 */ 13 | PTL_RAW = 0x0100 /**< 原始报文 */ 14 | PTL_698_45_HEAD = 0x0200 /**< 698头HCS结尾 */ 15 | PTL_64507_1 = 0x1008 /**< 单相64507*/ 16 | PTL_UNKNOW = 0xffff /**< 未知 */ 17 | ) 18 | 19 | const ( 20 | LINK_LOGIN = 0 21 | LINK_EXIT = 1 22 | LINK_HAERTBEAT = 2 23 | OTHER = 3 24 | FUNCERROR = 4 25 | ONLINE = 5 26 | ) 27 | 28 | //常量 29 | const ( 30 | PmaxPtlFrameLen = 2200 //最大报文长度 31 | Pmax698PtlFrameLen = 256 * 1024 //最大报文长度(698物联版) 32 | ) 33 | 34 | type ptlChkTab struct { 35 | ptype uint32 36 | isValid func([]byte) int32 37 | } 38 | 39 | var thePtlChkTab = [...]ptlChkTab{ 40 | {PTL_698_45, ptl698_45IsValid}, 41 | {PTL_NW, ptlNwIsValid}, 42 | {PTL_NWM, ptlNwmIsValid}, 43 | {PTL_1376_1, ptl1376_1IsValid}, 44 | // {PTL_645_07, ptl645_07IsValid}, 45 | // {PTL_645_97, ptl645_97IsValid}, 46 | // {PTL_1376_2, ptl1376_2IsValid}, 47 | //todo: other plt 48 | } 49 | 50 | // GetType 获取报文类型 51 | func GetType(data []byte) uint32 { 52 | for i := 0; i < len(thePtlChkTab); i++ { 53 | if 0 < thePtlChkTab[i].isValid(data) { 54 | return thePtlChkTab[i].ptype 55 | } 56 | } 57 | 58 | return PTL_UNKNOW 59 | } 60 | 61 | // GetLen 获取报文长度 62 | func GetLen(data []byte) int32 { 63 | for i := 0; i < len(thePtlChkTab); i++ { 64 | rlen := thePtlChkTab[i].isValid(data) 65 | if 0 < rlen { 66 | return rlen 67 | } 68 | } 69 | return 0 70 | } 71 | 72 | // IsValid 判断报文释放合法 73 | func IsValid(ptype uint32, data []byte) (int32, uint32) { 74 | var ret int32 = -1 //默认不合法 75 | for i := 0; i < len(thePtlChkTab); i++ { 76 | if ptype&thePtlChkTab[i].ptype != 0 { 77 | rlen := thePtlChkTab[i].isValid(data) 78 | if 0 < rlen { 79 | return rlen, thePtlChkTab[i].ptype 80 | } 81 | if rlen == 0 { 82 | ret = 0 83 | } 84 | } 85 | } 86 | 87 | return ret, PTL_UNKNOW 88 | } 89 | 90 | //判断是否为协议首字节 91 | func isFirstByte(ptype uint32, head byte) bool { 92 | if head == 0x68 { 93 | return true 94 | } 95 | 96 | if ptype == PTL_NWM && head == 0x88 { 97 | return true 98 | } 99 | 100 | if ptype == PTL_SSAL && head == 0x98 { 101 | return true 102 | } 103 | 104 | return false 105 | } 106 | 107 | //findFirstByte 获取指定协议报文首字节偏移 108 | func findFirstByte(ptype uint32, data []byte) int32 { 109 | var i int32 110 | var dlen = int32(len(data)) 111 | 112 | if ptype&(PTL_NWM|PTL_SSAL) != 0 { 113 | for i = 0; i < dlen; i++ { 114 | if data[i] == 0x68 || data[i] == 0x88 || data[i] == 0x98 { 115 | break 116 | } 117 | } 118 | } else if ptype&PTL_NWM != 0 { 119 | for i = 0; i < dlen; i++ { 120 | if data[i] == 0x68 || data[i] == 0x88 { 121 | break 122 | } 123 | } 124 | } else if ptype&PTL_SSAL != 0 { 125 | for i = 0; i < dlen; i++ { 126 | if data[i] == 0x68 || data[i] == 0x98 { 127 | break 128 | } 129 | } 130 | } else { 131 | for i = 0; i < dlen; i++ { 132 | if data[i] == 0x68 { 133 | break 134 | } 135 | } 136 | } 137 | 138 | if i < dlen { 139 | return i 140 | } 141 | return -1 142 | } 143 | 144 | //Check 从输入缓存中找出首条合法报文 145 | func Check(ptype uint32, data []byte) (int32, int32, uint32) { 146 | var pos int32 = 0 147 | inlen := int32(len(data)) 148 | 149 | for inlen > 0 { 150 | offset := findFirstByte(ptype, data[pos:]) 151 | if offset < 0 { 152 | return -1, 0, PTL_UNKNOW //头部(68,88,98)都找不到, 直接退出 153 | } 154 | pos += offset 155 | inlen -= offset 156 | if inlen == 1 { 157 | return -1, 0, PTL_UNKNOW //最后1个字节是(68,88,98) 158 | } 159 | 160 | rlen, ptype := IsValid(ptype, data[pos:]) 161 | if rlen >= 0 { 162 | return pos, rlen, ptype 163 | } 164 | pos++ 165 | inlen-- 166 | } 167 | 168 | return -1, 0, PTL_UNKNOW 169 | } 170 | -------------------------------------------------------------------------------- /zptl/ptl1376_1.go: -------------------------------------------------------------------------------- 1 | package zptl 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | ) 7 | 8 | func ptl1376_1IsValid(buf []byte) int32 { 9 | if len(buf) < 1 { 10 | return 0 11 | } 12 | 13 | //第一字节必须为0x68 14 | if buf[0] != 0x68 { 15 | return -1 16 | } 17 | 18 | //固定报文头长度不足 19 | if len(buf) < 6 { 20 | return 0 21 | } 22 | 23 | //第六个字节必须为0x68 24 | if buf[5] != 0x68 { 25 | return -1 26 | } 27 | 28 | //一帧报文必备项长度不足 29 | if len(buf) < 8 { 30 | return 0 31 | } 32 | 33 | //前后两个长度L不一致 34 | if (buf[1] != buf[3]) || (buf[2] != buf[4]) { 35 | return -1 36 | } 37 | 38 | //计算用户数据长度 39 | userDataLength := ((uint16(buf[2]) << 8) + uint16(buf[1])) >> 2 40 | if (userDataLength > (PmaxPtlFrameLen - 8)) || (userDataLength < 8) { 41 | return -1 42 | } 43 | 44 | //一帧报文必备项长度不足 45 | if len(buf) < int(userDataLength+7) { 46 | return 0 47 | } 48 | 49 | //校验和 50 | if buf[userDataLength+6] != GetCs(buf[6:userDataLength+6]) { 51 | return -1 52 | } 53 | 54 | //一帧报文必备项长度不足 55 | if len(buf) < int(userDataLength+8) { 56 | return 0 57 | } 58 | 59 | //判断最后字节0x16 60 | if buf[userDataLength+7] != 0x16 { 61 | return -1 62 | } 63 | 64 | return int32(userDataLength + 8) 65 | } 66 | 67 | //-------------------------------------------------------------------- 68 | 69 | func getFn(buf []byte) uint16 { 70 | var fn uint16 = uint16(buf[1]) * 8 71 | 72 | for i := 0; i < 8; i++ { 73 | if ((buf[0] >> i) & 0x01) == 0x01 { 74 | fn += uint16(i) + 1 75 | } 76 | } 77 | return fn 78 | } 79 | 80 | // Ptl1376_1GetDir 获取报文传输方向,0:主站-->终端, 1:终端-->主站 81 | func Ptl1376_1GetDir(buf []byte) int { 82 | if buf[6]&0x80 != 0 { 83 | return 1 84 | } 85 | return 0 86 | } 87 | 88 | // Ptl1376_1GetFrameType 获取报文类型 89 | func Ptl1376_1GetFrameType(buf []byte) int { 90 | if len(buf) < 20 { 91 | return OTHER 92 | } 93 | 94 | if buf[12] == 0x02 { //链路连接管理(登录,心跳,退出登录) 95 | switch binary.BigEndian.Uint32(buf[14:18]) { 96 | case 0x00000100: 97 | return LINK_LOGIN 98 | case 0x00000200: 99 | return LINK_EXIT 100 | case 0x00000400: 101 | return LINK_HAERTBEAT 102 | default: 103 | break 104 | } 105 | } else if buf[12] == 0xFE { 106 | if buf[1] == 0x42 && buf[2] == 0x00 && 107 | buf[7] == 0x00 && buf[8] == 0x00 && buf[9] == 0x00 && buf[10] == 0x00 && 108 | buf[14] == 0x00 && buf[15] == 0x00 && buf[16] == 0x00 && buf[17] == 0x00 { 109 | return ONLINE 110 | } 111 | } 112 | return OTHER 113 | } 114 | 115 | // Ptl1376_1BuildReplyPacket 打包登陆、心跳回复包 116 | // 登录REQ: 68 32 00 32 00 68 C9 21 45 03 00 00 02 74 00 00 01 00 A9 16 117 | // 登录ACK: 68 4A 00 4A 00 68 0B 21 45 03 00 02 00 64 00 00 04 00 02 00 00 01 00 00 E1 16 118 | // 心跳REQ: 68 4A 00 4A 00 68 C9 34 08 94 04 00 02 75 00 00 04 00 33 30 14 12 D2 20 93 16 119 | // 心跳ACK: 68 4A 00 4A 00 68 0B 34 08 94 04 02 00 65 00 00 04 00 02 00 00 04 00 00 50 16 120 | func Ptl1376_1BuildReplyPacket(in []byte, out []byte) int { 121 | out[0] = 0x68 122 | out[1] = 0x48 | (in[1] & 0x03) 123 | out[2] = 0x00 124 | out[3] = out[1] 125 | out[4] = 0x00 126 | out[5] = 0x68 127 | out[6] = 0x0B //CTRL 128 | 129 | out[7] = in[7] 130 | out[8] = in[8] 131 | out[9] = in[9] 132 | out[10] = in[10] 133 | 134 | out[11] = 0x02 //in[11] MSA 135 | out[12] = 0x00 //AFN 136 | out[13] = 0x60 | (in[13] & 0x0f) //SEQ 137 | 138 | offset := 14 139 | out[offset+0] = 0x00 140 | out[offset+1] = 0x00 141 | out[offset+2] = 0x04 142 | out[offset+3] = 0x00 143 | out[offset+4] = 0x02 //确认afn 144 | out[offset+5] = in[14] 145 | out[offset+6] = in[15] 146 | out[offset+7] = in[16] 147 | out[offset+8] = in[17] 148 | out[offset+9] = 0x00 149 | out[offset+10] = GetCs(out[6 : offset+10]) 150 | out[offset+11] = 0x16 151 | 152 | return offset + 12 153 | } 154 | 155 | // Ptl1376_1AddrCmp 终端地址比较 156 | func Ptl1376_1AddrCmp(addr []byte, buf []byte) bool { 157 | if len(addr) != len(buf) { 158 | return false 159 | } 160 | 161 | for i := 0; i <= len(addr); i++ { 162 | if addr[i] != buf[i] { 163 | return false 164 | } 165 | } 166 | 167 | return true 168 | } 169 | 170 | // Ptl1376_1AddrGet 从报文中取出终端地址 171 | func Ptl1376_1AddrGet(buf []byte) []byte { 172 | return buf[7:11] 173 | } 174 | 175 | // Ptl1376_1AddrStr 获取终端字符串 176 | func Ptl1376_1AddrStr(addr []byte) string { 177 | if len(addr) == 4 { 178 | return fmt.Sprintf("%02X%02X-%02X%02X", addr[1], addr[0], addr[3], addr[2]) 179 | } 180 | return "" 181 | } 182 | 183 | // Ptl1376_1MsaCmp 主站MSA地址比较 184 | func Ptl1376_1MsaCmp(msa int, buf []byte) bool { 185 | return msa == int(buf[11]) 186 | } 187 | 188 | // Ptl1376_1MsaGet 从报文中取出主站MSA地址 189 | func Ptl1376_1MsaGet(buf []byte) int { 190 | return int(buf[11]) 191 | } 192 | 193 | // Ptl1376_1IsMsaValid 判断主站发出的msa是否有效 194 | func Ptl1376_1IsMsaValid(msa int) bool { 195 | return msa != 0 196 | } 197 | 198 | // Ptl1376_1BuildPacket 创建登录包 199 | // 登录: 68 32 00 32 00 68 C9 21 45 03 00 00 02 74 00 00 01 00 A9 16 200 | // 心跳: 68 4A 00 4A 00 68 C9 34 08 94 04 00 02 75 00 00 04 00 33 30 14 12 D2 20 93 16 201 | func Ptl1376_1BuildPacket(tp uint8, tsa []byte) []byte { 202 | return []byte{} 203 | } 204 | -------------------------------------------------------------------------------- /zptl/ptl698_45.go: -------------------------------------------------------------------------------- 1 | package zptl 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | func ptl698_45HeadIsValid(buf []byte) int32 { 10 | var fLen int32 11 | var hLen uint16 12 | var cs uint16 13 | 14 | if len(buf) < 1 { 15 | return 0 16 | } 17 | 18 | if buf[0] != 0x68 { 19 | return -1 20 | } 21 | 22 | if len(buf) < 3 { 23 | return 0 24 | } 25 | 26 | fLen = (int32(buf[2]&0x3f) << 8) + int32(buf[1]) 27 | if buf[2]&0x40 != 0 { 28 | fLen *= 1024 29 | } 30 | if (fLen > Pmax698PtlFrameLen-2) || (fLen < 7) { 31 | return -1 32 | } 33 | 34 | if len(buf) < 6 { 35 | return 0 36 | } 37 | 38 | hLen = 6 + uint16(buf[4]&0x0f) + 1 39 | 40 | if len(buf) < int(hLen+2) { 41 | return 0 42 | } 43 | 44 | //check hcs 45 | cs = Crc16Calculate(buf[1:hLen]) 46 | if cs != ((uint16(buf[hLen+1]) << 8) + uint16(buf[hLen])) { 47 | return -1 48 | } 49 | 50 | return int32(hLen + 2) 51 | } 52 | 53 | func ptl698_45IsValid(buf []byte) int32 { 54 | var fLen int32 55 | var hlen uint16 56 | var cs uint16 57 | 58 | if len(buf) < 1 { 59 | return 0 60 | } 61 | 62 | if buf[0] != 0x68 { 63 | return -1 64 | } 65 | 66 | if len(buf) < 3 { 67 | return 0 68 | } 69 | 70 | fLen = (int32(buf[2]&0x3f) << 8) + int32(buf[1]) 71 | if buf[2]&0x40 != 0 { 72 | fLen *= 1024 73 | } 74 | if (fLen > Pmax698PtlFrameLen-2) || (fLen < 7) { 75 | return -1 76 | } 77 | 78 | if len(buf) < 6 { 79 | return 0 80 | } 81 | 82 | hlen = 6 + uint16(buf[4]&0x0f) + 1 83 | 84 | if len(buf) < int(hlen+2) { 85 | return 0 86 | } 87 | 88 | //check hcs 89 | cs = Crc16Calculate(buf[1:hlen]) 90 | if cs != ((uint16(buf[hlen+1]) << 8) + uint16(buf[hlen])) { 91 | return -1 92 | } 93 | 94 | if len(buf) < int(fLen+1) { 95 | return 0 96 | } 97 | 98 | //check fcs 99 | cs = Crc16Calculate(buf[1 : fLen-1]) 100 | if cs != ((uint16(buf[fLen]) << 8) + uint16(buf[fLen-1])) { 101 | return -1 102 | } 103 | 104 | if len(buf) < int(fLen+2) { 105 | return 0 106 | } 107 | if buf[fLen+1] != 0x16 { 108 | return -1 109 | } 110 | 111 | return int32(fLen + 2) 112 | } 113 | 114 | //-------------------------------------------------------------------- 115 | 116 | // Ptl698_45GetDir 获取报文传输方向,0:主站-->终端, 1:终端-->主站 117 | func Ptl698_45GetDir(buf []byte) int { 118 | if buf[3]&0x80 != 0 { 119 | return 1 120 | } 121 | return 0 122 | } 123 | 124 | // Ptl698_45GetFrameType 获取报文类型 125 | func Ptl698_45GetFrameType(buf []byte) int { 126 | if buf[3]&0x07 == 0x01 { //链路连接管理(登录,心跳,退出登录) 127 | switch buf[7+buf[4]&0x0f+2+2] { //todo: check 128 | case 0: 129 | return LINK_LOGIN 130 | case 1: 131 | return LINK_HAERTBEAT 132 | case 2: 133 | return LINK_EXIT 134 | default: 135 | break 136 | } 137 | } 138 | return OTHER 139 | } 140 | 141 | func getDataTime(buf []byte) { 142 | t := time.Now().Local() 143 | year := uint16(t.Year()) 144 | buf[0] = byte(year >> 8) 145 | buf[1] = byte(year) 146 | buf[2] = byte(t.Month()) 147 | buf[3] = byte(t.Day()) 148 | buf[4] = byte(t.Weekday()) 149 | buf[5] = byte(t.Hour()) 150 | buf[6] = byte(t.Minute()) 151 | buf[7] = byte(t.Second()) 152 | millisecond := t.Nanosecond() / 1e6 153 | buf[8] = byte(millisecond >> 8) 154 | buf[9] = byte(millisecond) 155 | } 156 | 157 | // Ptl698_45BuildReplyPacket 打包登陆、心跳回复包 158 | func Ptl698_45BuildReplyPacket(in []byte, out []byte) int { 159 | out[0] = 0x68 160 | out[1] = 0x00 161 | out[2] = 0x00 162 | out[3] = 0x01 163 | //服务器地址、客户机地址 164 | for i := 0; i < int(in[4]&0xf+3); i++ { 165 | out[4+i] = in[4+i] 166 | } 167 | offset := int(4 + in[4]&0xf + 3) //起始1、长度2、控制域1、地址 168 | offset += 2 169 | 170 | out[offset+0] = 0x81 171 | out[offset+1] = 0x00 172 | out[offset+2] = 0x80 //时钟可信 173 | offset += 3 174 | 175 | //请求时间 176 | for i := 0; i < 10; i++ { 177 | out[offset+i] = in[offset+2+i] 178 | } 179 | offset += 10 180 | 181 | //收到时间: todo: 更具系统时间获取 182 | getDataTime(out[offset:]) 183 | 184 | //响应时间 185 | for i := 0; i < 10; i++ { 186 | out[offset+10+i] = out[offset+i] 187 | } 188 | offset += 20 189 | 190 | //长度区域 191 | out[1] = byte(((offset + 3 - 2) >> 0) & 0xff) 192 | out[2] = byte(((offset + 3 - 2) >> 8) & 0xff) 193 | 194 | offsetHcs := int(4 + in[4]&0xf + 3) //起始1、长度2、控制域1、地址 195 | crc := Crc16Calculate(out[1:offsetHcs]) 196 | out[offsetHcs+0] = byte((crc >> 0) & 0xff) 197 | out[offsetHcs+1] = byte((crc >> 8) & 0xff) 198 | 199 | crc = Crc16Calculate(out[1:offset]) 200 | out[offset+0] = byte((crc >> 0) & 0xff) 201 | out[offset+1] = byte((crc >> 8) & 0xff) 202 | 203 | out[offset+2] = 0x16 204 | 205 | offset += 3 206 | 207 | return offset 208 | } 209 | 210 | // Ptl698_45AddrCmp 终端地址比较 211 | func Ptl698_45AddrCmp(addr []byte, buf []byte) bool { 212 | if buf[5] == 0xaa && buf[4] == 15 { 213 | return true 214 | } 215 | for i := 0; i <= int(addr[0]&0x0f)+2; i++ { 216 | if addr[i] != buf[i] { 217 | return false 218 | } 219 | } 220 | 221 | return true 222 | } 223 | 224 | // Ptl698_45AddrGet 从报文中取出终端地址 225 | func Ptl698_45AddrGet(buf []byte) []byte { 226 | return append([]byte{buf[4] & 0x0f}, buf[5:5+(buf[4]&0x0f)+1]...) 227 | } 228 | 229 | // Ptl698_45AddrStr 获取终端字符串 230 | func Ptl698_45AddrStr(addr []byte) string { 231 | var sa = make([]string, 0) 232 | for _, v := range addr { 233 | sa = append(sa, fmt.Sprintf("%02X", v)) 234 | } 235 | ss := strings.Join(sa, "") 236 | return ss 237 | } 238 | 239 | // Ptl698_45MsaCmp 主站MSA地址比较 240 | func Ptl698_45MsaCmp(msa int, buf []byte) bool { 241 | return msa == int(buf[6+buf[4]&0x0f]) 242 | } 243 | 244 | // Ptl698_45MsaGet 从报文中取出主站MSA地址 245 | func Ptl698_45MsaGet(buf []byte) int { 246 | return int(buf[6+buf[4]&0x0f]) 247 | } 248 | 249 | // Ptl698_45IsMsaValid 判断主站发出的msa是否有效 250 | func Ptl698_45IsMsaValid(msa int) bool { 251 | return msa != 0 252 | } 253 | 254 | // Ptl698_45BuildPacket 创建登录包 255 | // tp: 0-登录 1-心跳 2-退出 256 | // 登录: 68 1E 00 81 05 01 00 00 00 00 00 00 D2 B6 01 00 00 01 2C 07 E6 02 17 03 08 06 38 03 22 1C BC 16 257 | // 确认: 68 30 00 01 05 01 00 00 00 00 00 00 52 D9 81 00 00 07 E6 02 17 03 08 05 02 02 F9 07 E6 02 17 03 08 06 09 00 32 07 E6 02 17 03 08 06 09 00 32 F0 E7 16 258 | // 心跳: 68 1E 00 81 05 01 00 00 00 00 00 00 D2 B6 01 00 01 01 2C 07 E6 02 17 03 00 04 33 00 68 77 34 16 259 | func Ptl698_45BuildPacket(tp uint8, tsa []byte) []byte { 260 | packet := []byte{0x68, 0x1E - 6 + byte(len(tsa)), 0x00, 0x81, byte(len(tsa) - 1)} //, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 261 | packet2 := []byte{0x00, 0xD2, 0xB6, 0x01, 0x00, tp, 0x01, 0x2C, 0x07, 0xE6, 0x02, 0x17, 0x03, 0x08, 0x06, 0x38, 0x03, 0x22, 0x1C, 0xBC, 0x16} 262 | 263 | getDataTime(packet2[8:]) 264 | 265 | packet = append(packet, tsa...) 266 | packet = append(packet, packet2...) 267 | 268 | //todo: 心跳周期 269 | 270 | offsetHcs := int(4 + packet[4]&0xf + 3) //起始1、长度2、控制域1、地址 271 | crc := Crc16Calculate(packet[1:offsetHcs]) 272 | packet[offsetHcs+0] = byte((crc >> 0) & 0xff) 273 | packet[offsetHcs+1] = byte((crc >> 8) & 0xff) 274 | 275 | offset := len(packet) - 3 276 | crc = Crc16Calculate(packet[1:offset]) 277 | packet[offset+0] = byte((crc >> 0) & 0xff) 278 | packet[offset+1] = byte((crc >> 8) & 0xff) 279 | return packet 280 | } 281 | 282 | // Ptl698_45IsReport 是否为上报报文 283 | // 68 3A 00 83 05 11 11 00 00 11 11 00 A5 31 88 02 1F 01 60 12 03 00 05 00 20 2A 02 00 00 60 40 02 00 00 60 41 02 00 00 60 42 02 00 01 50 01 02 00 01 20 04 02 01 01 00 00 00 52 F6 16 284 | func Ptl698_45IsReport(buf []byte) bool { 285 | pos := 4 + buf[4]&0x0f + 2 + 3 286 | if len(buf) < 18 { 287 | return false 288 | } 289 | 290 | // if buf[pos] == 0x88 && buf[pos+1] == 0x01 { 291 | // return true 292 | // } 293 | if buf[pos] == 0x88 && buf[pos+1] == 0x02 { 294 | return true 295 | } 296 | // if buf[pos] == 0x88 && buf[pos+1] == 0x03 { 297 | // return true 298 | // } 299 | return false 300 | } 301 | 302 | // Ptl698_45BuildReportAckPacket 打包上报回复包 303 | func Ptl698_45BuildReportAckPacket(in []byte, out []byte) int { 304 | out[0] = 0x68 305 | out[1] = 0x00 306 | out[2] = 0x00 307 | out[3] = 0x03 308 | //服务器地址、客户机地址 309 | for i := 0; i < int(in[4]&0xf+3); i++ { 310 | out[4+i] = in[4+i] 311 | } //todo: 注意SA 312 | offset := int(4 + in[4]&0xf + 3) //起始1、长度2、控制域1、地址 313 | offset += 2 314 | 315 | out[offset+0] = 0x08 316 | out[offset+1] = in[offset+1] 317 | out[offset+2] = in[offset+2] 318 | rtype := out[offset+1] 319 | offset += 3 320 | 321 | switch rtype { 322 | case 1: //OAD 323 | return 0 //unsupport 324 | case 2: //Record 325 | out[offset+0] = 0x01 326 | out[offset+1] = in[offset+1] 327 | out[offset+2] = in[offset+2] 328 | out[offset+3] = in[offset+3] 329 | out[offset+4] = in[offset+4] 330 | offset += 5 331 | case 3: // 332 | return 0 //unsupport 333 | default: 334 | return 0 //unsupport 335 | } 336 | 337 | out[offset+0] = 0x00 //时间标签 338 | offset += 1 339 | 340 | //长度区域 341 | out[1] = byte(((offset + 3 - 2) >> 0) & 0xff) 342 | out[2] = byte(((offset + 3 - 2) >> 8) & 0xff) 343 | 344 | offsetHcs := int(4 + in[4]&0xf + 3) //起始1、长度2、控制域1、地址 345 | crc := Crc16Calculate(out[1:offsetHcs]) 346 | out[offsetHcs+0] = byte((crc >> 0) & 0xff) 347 | out[offsetHcs+1] = byte((crc >> 8) & 0xff) 348 | 349 | crc = Crc16Calculate(out[1:offset]) 350 | out[offset+0] = byte((crc >> 0) & 0xff) 351 | out[offset+1] = byte((crc >> 8) & 0xff) 352 | 353 | out[offset+2] = 0x16 354 | 355 | offset += 3 356 | 357 | return offset 358 | } 359 | -------------------------------------------------------------------------------- /zptl/ptl_nw.go: -------------------------------------------------------------------------------- 1 | package zptl 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | ) 7 | 8 | func ptlNwIsValid(buf []byte) int32 { 9 | if len(buf) < 1 { 10 | return 0 11 | } 12 | 13 | //第一字节必须为0x68 14 | if buf[0] != 0x68 { 15 | return -1 16 | } 17 | 18 | //固定报文头长度不足 19 | if len(buf) < 6 { 20 | return 0 21 | } 22 | 23 | //第六个字节必须为0x68 24 | if buf[5] != 0x68 { 25 | return -1 26 | } 27 | 28 | //一帧报文必备项长度不足 29 | if len(buf) < 8 { 30 | return 0 31 | } 32 | 33 | //前后两个长度L不一致 34 | if (buf[1] != buf[3]) || (buf[2] != buf[4]) { 35 | return -1 36 | } 37 | 38 | //计算用户数据长度 39 | userDataLength := (uint16(buf[2]) << 8) + uint16(buf[1]) 40 | if (userDataLength > (PmaxPtlFrameLen - 8)) || (userDataLength < 8) { 41 | return -1 42 | } 43 | 44 | //一帧报文必备项长度不足 45 | if len(buf) < int(userDataLength+7) { 46 | return 0 47 | } 48 | 49 | //校验和 50 | if buf[userDataLength+6] != GetCs(buf[6:userDataLength+6]) { 51 | return -1 52 | } 53 | 54 | //一帧报文必备项长度不足 55 | if len(buf) < int(userDataLength+8) { 56 | return 0 57 | } 58 | 59 | //判断最后字节0x16 60 | if buf[userDataLength+7] != 0x16 { 61 | return -1 62 | } 63 | 64 | return int32(userDataLength + 8) 65 | } 66 | 67 | //-------------------------------------------------------------------- 68 | 69 | // PtlNwGetDir 获取报文传输方向,0:主站-->终端, 1:终端-->主站 70 | func PtlNwGetDir(buf []byte) int { 71 | if buf[6]&0x80 != 0 { 72 | return 1 73 | } 74 | return 0 75 | } 76 | 77 | // PtlNwGetFrameType 获取报文类型 78 | func PtlNwGetFrameType(buf []byte) int { 79 | if len(buf) < 23 { 80 | return OTHER 81 | } 82 | 83 | if buf[14] == 0x02 { //链路连接管理(登录,心跳,退出登录) 84 | switch binary.LittleEndian.Uint32(buf[18:22]) { 85 | case 0xE0001000: 86 | return LINK_LOGIN 87 | case 0xE0001002: 88 | return LINK_EXIT 89 | case 0xE0001001: 90 | return LINK_HAERTBEAT 91 | default: 92 | break 93 | } 94 | } 95 | return OTHER 96 | } 97 | 98 | // PtlNwBuildReplyPacket 打包登陆、心跳回复包 99 | // 68 12 00 12 00 68 C9 23 00 00 01 00 00 00 02 71 00 00 00 10 00 E0 00 01 51 16 100 | // 68 11 00 11 00 68 0B 23 00 00 01 00 00 00 00 61 00 00 00 00 00 E0 00 70 16 101 | func PtlNwBuildReplyPacket(in []byte, out []byte) int { 102 | out[0] = 0x68 103 | out[1] = 0x11 104 | out[2] = 0x00 105 | out[3] = 0x11 106 | out[4] = 0x00 107 | out[5] = 0x68 108 | out[6] = 0x0B //CTRL 109 | 110 | out[7] = in[7] 111 | out[8] = in[8] 112 | out[9] = in[9] 113 | out[10] = in[10] 114 | out[11] = in[11] 115 | out[12] = in[12] 116 | 117 | out[13] = 0x00 //in[13] MSA 118 | out[14] = 0x00 //AFN 119 | out[15] = 0x60 | (in[15] & 0x0f) //SEQ 120 | 121 | offset := 16 122 | out[offset+0] = 0x00 123 | out[offset+1] = 0x00 124 | out[offset+2] = 0x00 125 | out[offset+3] = 0x00 126 | out[offset+4] = 0x00 127 | out[offset+5] = 0xE0 128 | out[offset+6] = 0x00 129 | out[offset+7] = GetCs(out[6 : offset+7]) 130 | out[offset+8] = 0x16 131 | 132 | return offset + 9 133 | } 134 | 135 | // PtlNwAddrCmp 终端地址比较 136 | func PtlNwAddrCmp(addr []byte, buf []byte) bool { 137 | if len(addr) != len(buf) { 138 | return false 139 | } 140 | 141 | for i := 0; i <= len(addr); i++ { 142 | if addr[i] != buf[i] { 143 | return false 144 | } 145 | } 146 | 147 | return true 148 | } 149 | 150 | // PtlNwAddrGet 从报文中取出终端地址 151 | func PtlNwAddrGet(buf []byte) []byte { 152 | return buf[7:13] 153 | } 154 | 155 | // PtlNwAddrStr 获取终端字符串 156 | func PtlNwAddrStr(addr []byte) string { 157 | if len(addr) == 6 { 158 | return fmt.Sprintf("%02X%02X%02X-%02X%02X%02X", addr[2], addr[1], addr[0], addr[5], addr[4], addr[3]) 159 | } 160 | return "" 161 | } 162 | 163 | // PtlNwMsaCmp 主站MSA地址比较 164 | func PtlNwMsaCmp(msa int, buf []byte) bool { 165 | return int(buf[13]) == msa 166 | } 167 | 168 | // PtlNwMsaGet 从报文中取出主站MSA地址 169 | func PtlNwMsaGet(buf []byte) int { 170 | return int(buf[13]) 171 | } 172 | 173 | // PtlNwIsMsaValid 判断主站发出的msa是否有效 174 | func PtlNwIsMsaValid(msa int) bool { 175 | return msa != 0 176 | } 177 | 178 | // PtlNwBuildPacket 创建登录包 179 | // 登录: 180 | // 心跳: 181 | func PtlNwBuildPacket(tp uint8, tsa []byte) []byte { 182 | return []byte{} 183 | } 184 | -------------------------------------------------------------------------------- /zptl/ptl_nwm.go: -------------------------------------------------------------------------------- 1 | package zptl 2 | 3 | // 88 01 00 25 68 1D 00 1D 00 68 4B 23 00 00 11 11 21 49 0D 74 02 01 00 FF 01 4 | // 00 20 19 03 07 00 00 20 19 03 07 14 00 00 18 16 77 5 | 6 | func ptlNwmIsValid(buf []byte) int32 { 7 | if len(buf) < 1 { 8 | return 0 9 | } 10 | 11 | //第一字节必须为0x88 12 | if buf[0] != 0x88 { 13 | return -1 14 | } 15 | 16 | //固定报文头长度不足 17 | if len(buf) < 2 { 18 | return 0 19 | } 20 | 21 | //第二个字节必须为0x01 22 | if buf[1] != 0x01 { 23 | return -1 24 | } 25 | 26 | //一帧报文必备项长度不足 27 | if len(buf) < 4 { 28 | return 0 29 | } 30 | 31 | //计算用户数据长度 32 | userDataLength := (uint16(buf[2]) << 8) + uint16(buf[3]) 33 | if (userDataLength > 2048) || (userDataLength < 7) { 34 | return -1 35 | } 36 | 37 | //报文长度不足 38 | if len(buf) < int(userDataLength+4+1) { 39 | return 0 40 | } 41 | 42 | //判断最后字节0x77 43 | if buf[userDataLength+4] != 0x77 { 44 | return -1 45 | } 46 | 47 | return int32(userDataLength + 5) 48 | } 49 | -------------------------------------------------------------------------------- /zptl/ptl_test.go: -------------------------------------------------------------------------------- 1 | package zptl 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func cb(ptype uint32, data []byte, arg interface{}) { 9 | fmt.Println("ptype: ", ptype) 10 | PrintBuf(0, data) 11 | fmt.Println("TSA: ", Ptl698_45AddrGet(data), Ptl698_45AddrStr(Ptl698_45AddrGet(data))) 12 | } 13 | 14 | func TestChkfrm_00(t *testing.T) { 15 | //创建一个报文检测对象 16 | chkfrm := NewChkfrm(PTL_698_45, 1000, cb, nil) 17 | 18 | var cnt int32 19 | // cnt = chkfrm.Chkfrm(Str2hex("68")) 20 | // fmt.Println("cnt: ", cnt) 21 | packet := Str2hex("68 21 00 C3 05 11 11 11 11 01 00 CC 38 81 85 01 01 40 00 02 00 01 1C 07 E4 04 03 0F 02 0A 00 00 72 99 16") 22 | rlen, ptype := IsValid(PTL_698_45, packet) 23 | fmt.Println("rlen: ", rlen, " ptype: ", ptype) 24 | 25 | cnt = chkfrm.Chkfrm(Str2hex("68")) 26 | fmt.Println("cnt: ", cnt) 27 | cnt = chkfrm.Chkfrm(Str2hex("21 00 C3 05 11 11 11 11 01 00 CC 38 81")) 28 | fmt.Println("cnt: ", cnt) 29 | cnt = chkfrm.Chkfrm(Str2hex("85 01 01 40 00 02 00 01 1C 07 E4 04 03 0F 02 0A 00 00 72 99 16")) 30 | fmt.Println("cnt: ", cnt) 31 | } 32 | 33 | // package main 34 | 35 | // import "fmt" 36 | 37 | // func main() { 38 | // x := make(map[string][]string) 39 | 40 | // x["key"] = append(x["key"], "value") 41 | // x["key"] = append(x["key"], "value1") 42 | 43 | // fmt.Println(x["key"][0]) 44 | // fmt.Println(x["key"][1]) 45 | // } 46 | -------------------------------------------------------------------------------- /zptl/ptlchk.go: -------------------------------------------------------------------------------- 1 | package zptl 2 | 3 | //Chkfrm ptl check frame 4 | type Chkfrm struct { 5 | //超时时间ms 6 | timeout int64 7 | //最近接收到数据时标 8 | rtime int64 9 | //协议类型 10 | ptype uint32 11 | //回调函数 12 | f func(uint32, []byte, interface{}) 13 | //args: 回调函数的形参 14 | arg interface{} 15 | 16 | //报文检测长度 17 | //pos uint32 18 | //报文检测缓存 19 | buf []byte 20 | } 21 | 22 | //NewChkfrm 初始化报文检测的方法 23 | func NewChkfrm(ptype uint32, timeout int64, f func(uint32, []byte, interface{}), arg interface{}) *Chkfrm { 24 | //初始化chkfrm属性 25 | p := &Chkfrm{ 26 | timeout: timeout, 27 | rtime: 0, 28 | ptype: ptype, 29 | f: f, 30 | arg: arg, 31 | //pos: 0, 32 | buf: nil, 33 | } 34 | return p 35 | } 36 | 37 | //Chkfrm 报文检测, 返回合法报文数量 38 | func (p *Chkfrm) Chkfrm(data []byte) int32 { 39 | var cnt int32 = 0 40 | 41 | if p.ptype == 0 { 42 | return 0 43 | } 44 | 45 | if p.ptype == PTL_RAW { 46 | if p.f != nil { 47 | p.f(PTL_RAW, data, p.arg) 48 | } 49 | return 1 50 | } 51 | 52 | if getTick()-p.rtime > p.timeout { 53 | if len(p.buf) > 0 { 54 | p.buf = p.buf[1:] 55 | } else { 56 | p.Reset() 57 | } 58 | } 59 | 60 | if len(data) <= 0 { 61 | return cnt 62 | } 63 | 64 | p.rtime = getTick() 65 | if p.buf == nil { 66 | if p.ptype&PTL_698_45 != 0 { 67 | p.buf = make([]byte, 0) 68 | } else { 69 | p.buf = make([]byte, 0, PmaxPtlFrameLen) 70 | } 71 | if p.buf == nil { 72 | return cnt 73 | } 74 | } 75 | 76 | //切片叠加 77 | p.buf = append(p.buf, data...) 78 | 79 | for len(p.buf) > 0 { 80 | offset := findFirstByte(p.ptype, p.buf) 81 | if offset < 0 { 82 | p.buf = p.buf[:0] 83 | return cnt //68,88,98都找不到, 不需要申请空间 84 | } 85 | if offset > 0 { 86 | p.buf = p.buf[offset:] 87 | } 88 | rlen, ptype := IsValid(p.ptype, p.buf) 89 | if rlen > 0 { 90 | if p.f != nil { 91 | p.f(ptype, p.buf[:rlen], p.arg) 92 | } 93 | p.buf = p.buf[rlen:] 94 | } else if rlen == 0 { 95 | break //报文不完整 96 | } else { 97 | p.buf = p.buf[1:] 98 | } 99 | } 100 | 101 | return cnt 102 | } 103 | 104 | //Reset 复位 105 | func (p *Chkfrm) Reset() { 106 | // p.pos = 0 107 | p.rtime = 0 108 | //可以考虑释放buf 109 | p.buf = nil 110 | } 111 | -------------------------------------------------------------------------------- /zptl/utils.go: -------------------------------------------------------------------------------- 1 | package zptl 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | var crc16CcittTable = []uint16{ 9 | 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 10 | 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 11 | 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 12 | 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 13 | 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 14 | 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 15 | 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 16 | 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 17 | 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 18 | 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 19 | 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 20 | 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 21 | 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 22 | 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 23 | 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 24 | 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 25 | 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 26 | 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 27 | 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 28 | 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 29 | 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 30 | 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 31 | 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 32 | 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 33 | 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 34 | 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 35 | 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 36 | 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 37 | 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 38 | 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 39 | 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 40 | 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, 41 | } 42 | 43 | //Crc16Calculate crc16 calculate 44 | func Crc16Calculate(data []byte) uint16 { 45 | var fcs uint16 = 0xffff 46 | 47 | for _, v := range data { 48 | fcs = (fcs >> 8) ^ crc16CcittTable[uint8(uint16(v)^fcs)] 49 | } 50 | fcs ^= 0xffff 51 | return fcs 52 | } 53 | 54 | //Crc16Update crc16 update 55 | func Crc16Update(fcs uint16, data []byte) uint16 { 56 | for _, v := range data { 57 | fcs = (fcs >> 8) ^ crc16CcittTable[uint8(uint16(v)^fcs)] 58 | } 59 | return fcs 60 | } 61 | 62 | //GetCs get cs sum 63 | func GetCs(data []byte) uint8 { 64 | var cs uint8 = 0 65 | 66 | for _, v := range data { 67 | cs += v 68 | } 69 | return cs 70 | } 71 | 72 | //GetCrc16 get crc16 73 | func GetCrc16(data []byte, crc uint16) uint16 { 74 | for _, v := range data { 75 | crc ^= uint16(v) << 8 76 | for i := 0; i < 10; i++ { 77 | crc <<= 1 78 | if crc&0x8000 != 0 { 79 | crc ^= 0x1021 80 | } 81 | } 82 | } 83 | return crc 84 | } 85 | 86 | //MemEqual memequal 87 | func MemEqual(data []byte, c uint8) bool { 88 | for _, v := range data { 89 | if v != c { 90 | return false 91 | } 92 | } 93 | return true 94 | } 95 | 96 | //MemMatch xxx 97 | func MemMatch() { 98 | 99 | } 100 | 101 | //Hex2Str bytes to hex string 102 | func Hex2Str(data []byte) string { 103 | str := make([]byte, len(data)*2) 104 | 105 | for i, v := range data { 106 | str[i*2+0] = (v >> 4) + '0' 107 | str[i*2+1] = (uint8(v) & 0x0f) + '0' 108 | } 109 | 110 | for i := 0; i < len(data)*2; i++ { 111 | if str[i] > '9' { 112 | str[i] += 7 113 | } 114 | } 115 | 116 | return string(str) 117 | } 118 | 119 | //Str2hex str to bytes 120 | func Str2hex(txt string) []byte { 121 | var pos int 122 | var str []byte 123 | 124 | for _, v := range txt { 125 | if (v >= '0') && (v <= '9') { 126 | v = v - '0' 127 | } else if (v >= 'a') && (v <= 'f') { 128 | v = v - 'a' + 10 129 | } else if (v >= 'A') && (v <= 'F') { 130 | v = v - 'A' + 10 131 | } else { 132 | continue 133 | } 134 | if pos&1 == 1 { 135 | str[pos>>1] = (str[pos>>1] << 4) | uint8(v) 136 | } else { 137 | str = append(str, byte(v)) 138 | } 139 | pos++ 140 | } 141 | 142 | return str 143 | } 144 | 145 | //IsPrint is print 146 | func IsPrint(c uint8) bool { 147 | if (c >= 32) && (c <= 126) { 148 | return true 149 | } 150 | return false 151 | } 152 | 153 | //PrintBuf printbuf 154 | func PrintBuf(offset uint32, data []byte) { 155 | var lineBytes uint8 156 | var cnt uint8 157 | var bytes [16]uint8 158 | 159 | if data == nil { 160 | return 161 | } 162 | for i, v := range data { 163 | if lineBytes == 0 { 164 | fmt.Printf("%08X: ", offset+uint32(i)) 165 | } 166 | fmt.Printf(" ") 167 | 168 | bytes[lineBytes] = v 169 | fmt.Printf("%02X", bytes[lineBytes]) 170 | lineBytes++ 171 | 172 | if 16 == lineBytes { 173 | fmt.Printf(" *") 174 | for j := 0; j < 16; j++ { 175 | if IsPrint(bytes[j]) { 176 | fmt.Printf("%c", bytes[j]) 177 | } else { 178 | fmt.Printf(".") 179 | } 180 | } 181 | fmt.Printf("*\n") 182 | lineBytes = 0 183 | } 184 | } 185 | 186 | if 0 != lineBytes { 187 | cnt = 16 - lineBytes 188 | cnt = (cnt << 1) + cnt 189 | for i := 0; i < int(cnt); i++ { 190 | fmt.Printf(" ") 191 | } 192 | fmt.Printf(" *") 193 | for i := 0; i < int(lineBytes); i++ { 194 | if IsPrint(bytes[i]) { 195 | fmt.Printf("%c", bytes[i]) 196 | } else { 197 | fmt.Printf(".") 198 | } 199 | } 200 | fmt.Printf("*\n") 201 | } 202 | } 203 | 204 | //PrintBuffer printbuffer 205 | func PrintBuffer(format string, data []byte) { 206 | if len(format) != 0 { 207 | fmt.Printf(format) 208 | } 209 | 210 | for _, v := range data { 211 | fmt.Printf("%02X ", v) 212 | } 213 | fmt.Println("") 214 | } 215 | 216 | func getTick() int64 { 217 | return time.Now().UnixNano() / 1e6 218 | } 219 | --------------------------------------------------------------------------------