├── README.md ├── errors.go ├── example └── main.go ├── gameserver └── gameserver.go ├── gate └── gate.go ├── libs └── wordfilter │ ├── README.md │ ├── dictionary.txt │ ├── wordfilter.go │ └── wordfilter.txt ├── log └── log.go ├── message └── message.go ├── network ├── agent.go ├── tcpClient.go ├── tcpConn.go ├── tcpServer.go ├── wsClient.go ├── wsConn.go └── wsServer.go ├── newTea ├── newTea.go └── tpl │ ├── config.go │ ├── game.go │ ├── gate.go │ ├── handle.go │ ├── log.go │ ├── msg.go │ ├── protocol.go │ ├── register.go │ └── router.go ├── options.go ├── protocol ├── common.go ├── json.go ├── processor.go └── protobuf.go ├── recordfile ├── example_test.go ├── recordfile.go └── test.txt └── util.go /README.md: -------------------------------------------------------------------------------- 1 | ## Tea 游戏服务器框架 2 | --- 3 | 4 | 交流QQ群:376389675 5 | 6 | Tea [V1.0](https://github.com/k4s/tea/tree/v1.0): 7 | * 像开发Web一样简单去开发Game. 8 | * 每个用户走在单个goroutine上,更加适合多核支持并发处理. 9 | 10 | Tea [master](https://github.com/k4s/tea/tree/master): 11 | * 支持多网关,多游戏服 分布式处理. 12 | * 单路复用. 13 | * 支持v1.0,leaf 游戏逻辑对接. 14 | 15 | 16 | 17 | #### 基于Tea的开发: 18 | 19 | 1.新建生成项目工具newTea: 20 | ``` 21 | cd github.com/k4s/tea/newTea 22 | go install 23 | ``` 24 | 25 | 2.生成网关: 26 | ``` 27 | cd $GOPATH 28 | newTea gate appname 29 | cd appname 30 | ``` 31 | 32 | 3.生成游戏服: 33 | ``` 34 | cd $GOPATH 35 | newTea gameserver appname 36 | cd appname 37 | ``` 38 | 39 | 4.配置[config]目录,选择一种msg协议作为通讯协议,对应的[protocol/process.go]: 40 | ``` 41 | Protocol = "json" 42 | ``` 43 | 44 | 5.在[hamdle]编写对应msg的处理函数. 45 | ```go 46 | func InfoHandle(msg *message.Message, agent network.Agent) { 47 | jsonMsg, err := protocol.Processor.Unmarshal(msg.Body) 48 | if err != nil { 49 | fmt.Println(err) 50 | } 51 | m := jsonMsg.(*ms.Hello) 52 | fmt.Println("game:", m) 53 | reMsg := ms.Hello{ 54 | Name: "kkk", 55 | } 56 | agent.EndHandle(msg, reMsg) 57 | } 58 | ``` 59 | 60 | 6.在[register]做通讯消息注册 61 | ``` 62 | protocol.Processor.Register(&msg.Hello{}) 63 | ``` 64 | 7.在[router]做路由映射. 65 | ``` 66 | protocol.Processor.SetHandler(&msg.Hello{}, handle.InfoHandle) 67 | ``` 68 | 69 | 70 | 8.分别执行网关和游戏服: 71 | ``` 72 | cd appname 73 | go run main.go 74 | ``` 75 | 76 | 77 | #### unity 引擎游戏客户端测试 78 | 79 | socket and websocket with json demo: 80 | 81 | [github.com/k4s/teaUnity](http://github.com/k4s/teaUnity) 82 | 83 | 84 | 85 | 86 | #### go 客户端测试 87 | 88 | [https://github.com/k4s/tea/example](https://github.com/k4s/tea/tree/master/example) -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package tea 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // error codes. 8 | var ( 9 | ErrBadAddr = errors.New("invalid address") 10 | ErrBadHeader = errors.New("invalid header received") 11 | ErrBadVersion = errors.New("invalid protocol version") 12 | ErrTooShort = errors.New("message is too short") 13 | ErrTooLong = errors.New("message is too long") 14 | ErrClosed = errors.New("connection closed") 15 | ErrConnRefused = errors.New("connection refused") 16 | ErrSendTimeout = errors.New("send time out") 17 | ErrRecvTimeout = errors.New("receive time out") 18 | ErrProtoState = errors.New("incorrect protocol state") 19 | ErrProtoOp = errors.New("invalid operation for protocol") 20 | ErrBadTran = errors.New("invalid or unsupported transport") 21 | ErrBadProto = errors.New("invalid or unsupported protocol") 22 | ErrPipeFull = errors.New("pipe full") 23 | ErrPipeEmpty = errors.New("pipe empty") 24 | ErrBadOption = errors.New("invalid or unsupported option") 25 | ErrBadValue = errors.New("invalid option value") 26 | ErrGarbled = errors.New("message garbled") 27 | ErrAddrInUse = errors.New("address in use") 28 | ErrBadProperty = errors.New("invalid property name") 29 | ErrTLSNoConfig = errors.New("missing TLS configuration") 30 | ErrTLSNoCert = errors.New("missing TLS certificates") 31 | ) 32 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "net" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | conn, err := net.Dial("tcp", "127.0.0.1:4444") 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | // Hello 消息(JSON 格式) 17 | data := []byte(`{"Hello": {"Name": "tea"}}`) 18 | 19 | // len + data 20 | m := make([]byte, 4+len(data)) 21 | 22 | // 默认使用大端序 23 | binary.LittleEndian.PutUint32(m, uint32(len(data))) 24 | // binary.BigEndian.PutUint32(m, uint32(len(data))) 25 | 26 | copy(m[4:], data) 27 | 28 | // 发送消息 29 | conn.Write(m) 30 | 31 | var buf = make([]byte, 32) 32 | n, err := conn.Read(buf) 33 | if err != nil { 34 | fmt.Println("read error:", err) 35 | } else { 36 | fmt.Printf("read % bytes, content is %s\n", n, string(buf[:n])) 37 | } 38 | fmt.Println(string(buf)) 39 | 40 | fmt.Println(binary.LittleEndian.Uint32(buf)) 41 | fmt.Println(len(buf)) 42 | time.Sleep(time.Second * 3) 43 | } 44 | -------------------------------------------------------------------------------- /gameserver/gameserver.go: -------------------------------------------------------------------------------- 1 | package gameserver 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "time" 7 | 8 | . "github.com/k4s/tea" 9 | "github.com/k4s/tea/log" 10 | "github.com/k4s/tea/network" 11 | "github.com/k4s/tea/protocol" 12 | ) 13 | 14 | type Gameserver struct { 15 | TCPAddr string 16 | opts Options 17 | CloseChan chan bool 18 | IsClose chan bool 19 | Processor protocol.Processor 20 | } 21 | 22 | func NewGameserver(addr string, processor protocol.Processor) *Gameserver { 23 | gate := &Gameserver{ 24 | TCPAddr: addr, 25 | opts: make(Options), 26 | CloseChan: make(chan bool), 27 | IsClose: make(chan bool), 28 | Processor: processor, 29 | } 30 | return gate 31 | } 32 | 33 | func (g *Gameserver) SetOpts(opts Options) { 34 | g.opts = opts 35 | } 36 | 37 | //GameRun multi game run 38 | func GameRun(gameserver []*Gameserver) { 39 | for _, g := range gameserver { 40 | log.Release("Gameserver Server running by %s", g.TCPAddr) 41 | g.Start() 42 | } 43 | c := make(chan os.Signal, 1) 44 | signal.Notify(c, os.Interrupt, os.Kill) 45 | sig := <-c 46 | for _, g := range gameserver { 47 | g.Stop() 48 | } 49 | log.Release("Gameserver closing by (signal: %v)", sig) 50 | } 51 | 52 | func (g *Gameserver) Start() { 53 | var tcpClient *network.TCPClient 54 | if g.TCPAddr != "" { 55 | tcpClient = new(network.TCPClient) 56 | tcpClient.Addr = g.TCPAddr 57 | tcpClient.NewAgent = func(conn *network.TCPConn, withID bool) network.Agent { 58 | a := network.NewAgent(conn, withID) 59 | return a 60 | } 61 | tcpClient.SetOpts(g.opts) 62 | tcpClient.SetWithID(true) 63 | } 64 | if tcpClient != nil { 65 | tcpClient.Start() 66 | go g.WaitAgent(tcpClient) 67 | 68 | } 69 | <-g.CloseChan 70 | if tcpClient != nil { 71 | tcpClient.Close() 72 | } 73 | g.IsClose <- true 74 | } 75 | 76 | func (g *Gameserver) Stop() { 77 | g.CloseChan <- true 78 | <-g.IsClose 79 | } 80 | 81 | func (g *Gameserver) WaitAgent(client *network.TCPClient) { 82 | var agent network.Agent 83 | for { 84 | agent = client.GetAgent() 85 | if agent != nil { 86 | log.Debug("get a agent") 87 | break 88 | } 89 | time.Sleep(time.Millisecond * 300) 90 | } 91 | go g.RunAgent(agent) 92 | } 93 | 94 | func (g *Gameserver) RunAgent(agent network.Agent) { 95 | for { 96 | msg := agent.RecvMsg() 97 | g.Processor.Route(msg, agent) 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /gate/gate.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "sync" 7 | "sync/atomic" 8 | 9 | . "github.com/k4s/tea" 10 | "github.com/k4s/tea/log" 11 | "github.com/k4s/tea/network" 12 | ) 13 | 14 | type Gate struct { 15 | clientAddr string 16 | serverAddr string 17 | cNum uint32 18 | cAgents map[uint32]network.Agent 19 | sAgent map[network.Agent]struct{} 20 | sync.Mutex 21 | opts Options 22 | CloseChan chan bool 23 | IsClose chan bool 24 | } 25 | 26 | func NewGate(cAddr, sAddr string) *Gate { 27 | gate := &Gate{ 28 | clientAddr: cAddr, 29 | serverAddr: sAddr, 30 | cAgents: make(map[uint32]network.Agent), 31 | cNum: 0, 32 | sAgent: make(map[network.Agent]struct{}), 33 | opts: make(Options), 34 | CloseChan: make(chan bool), 35 | IsClose: make(chan bool), 36 | } 37 | return gate 38 | } 39 | 40 | func (g *Gate) SetOpts(opts Options) { 41 | g.opts = opts 42 | } 43 | 44 | func (g *Gate) Run() { 45 | log.Release("Gateway External Server Running : %s", g.clientAddr) 46 | log.Release("Gateway Internal Server Running : %s", g.serverAddr) 47 | g.Start() 48 | c := make(chan os.Signal, 1) 49 | signal.Notify(c, os.Interrupt, os.Kill) 50 | sig := <-c 51 | g.Stop() 52 | log.Release("Gateway closing by (signal: %v)", sig) 53 | } 54 | 55 | func (g *Gate) Start() { 56 | var CServer *network.TCPServer 57 | var SServer *network.TCPServer 58 | if g.clientAddr != "" { 59 | CServer = new(network.TCPServer) 60 | CServer.Addr = g.clientAddr 61 | CServer.NewAgent = func(conn *network.TCPConn, withID bool) network.Agent { 62 | a := network.NewAgent(conn, withID) 63 | return a 64 | } 65 | CServer.SetOpts(g.opts) 66 | 67 | } 68 | if g.serverAddr != "" { 69 | SServer = new(network.TCPServer) 70 | SServer.Addr = g.serverAddr 71 | SServer.NewAgent = func(conn *network.TCPConn, withID bool) network.Agent { 72 | a := network.NewAgent(conn, withID) 73 | return a 74 | } 75 | CServer.SetOpts(g.opts) 76 | SServer.SetWithID(true) 77 | } 78 | if CServer != nil { 79 | CServer.Start() 80 | 81 | go g.waitSession(CServer) 82 | } 83 | if SServer != nil { 84 | SServer.Start() 85 | 86 | go g.waitAgent(SServer) 87 | } 88 | <-g.CloseChan 89 | if CServer != nil { 90 | CServer.Close() 91 | } 92 | if SServer != nil { 93 | SServer.Close() 94 | } 95 | g.IsClose <- true 96 | } 97 | func (g *Gate) Stop() { 98 | g.CloseChan <- true 99 | <-g.IsClose 100 | } 101 | 102 | func (g *Gate) waitSession(server *network.TCPServer) { 103 | for { 104 | agent := server.GetAgent() 105 | for { 106 | atomic.AddUint32(&g.cNum, 1) 107 | if _, ok := g.cAgents[g.cNum]; !ok { 108 | g.Lock() 109 | g.cAgents[g.cNum] = agent 110 | g.Unlock() 111 | agent.SetID(g.cNum) 112 | go g.stopSession(agent) 113 | break 114 | } 115 | } 116 | 117 | go g.runSession(agent) 118 | } 119 | } 120 | 121 | func (g *Gate) stopSession(agent network.Agent) { 122 | agent.IsClose() 123 | g.Lock() 124 | delete(g.cAgents, agent.GetID()) 125 | g.Unlock() 126 | } 127 | 128 | func (g *Gate) runSession(agent network.Agent) { 129 | for { 130 | msg := agent.RecvMsg() 131 | msg.ConnID = agent.GetID() 132 | 133 | //gate To gmaeserver 134 | for a := range g.sAgent { 135 | a.WriteMsg(msg) 136 | break 137 | } 138 | msg.Free() 139 | 140 | } 141 | } 142 | 143 | func (g *Gate) waitAgent(server *network.TCPServer) { 144 | for { 145 | agent := server.GetAgent() 146 | g.Lock() 147 | g.sAgent[agent] = struct{}{} 148 | g.Unlock() 149 | go g.stopAgent(agent) 150 | go g.runAgent(agent) 151 | } 152 | } 153 | func (g *Gate) stopAgent(agent network.Agent) { 154 | agent.IsClose() 155 | g.Lock() 156 | delete(g.sAgent, agent) 157 | g.Unlock() 158 | } 159 | 160 | func (g *Gate) runAgent(agent network.Agent) { 161 | for { 162 | msg := agent.RecvMsg() 163 | if a, ok := g.cAgents[msg.ConnID]; ok { 164 | a.WriteMsg(msg) 165 | msg.Free() 166 | } 167 | 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /libs/wordfilter/README.md: -------------------------------------------------------------------------------- 1 | ###Wordfilter (敏感文字过滤) 2 | ```go 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/k4s/tea/libs/Wordfilter" 9 | ) 10 | 11 | func main() { 12 | fmt.Println("中国共产党习近平主席总书记万岁,fuckyou!") 13 | //过滤前 14 | //中国共产党习近平主席总书记万岁,fuckyou! 15 | 16 | fmt.Println(Wordfilter.Wordfilter("中国共产党习近平主席总书记万岁.fuckyou!")) 17 | //过滤后 18 | //**********总书记万岁.**! 19 | } 20 | 21 | ``` -------------------------------------------------------------------------------- /libs/wordfilter/wordfilter.go: -------------------------------------------------------------------------------- 1 | package Wordfilter 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "os" 7 | "strings" 8 | 9 | "github.com/huichen/sego" 10 | ) 11 | 12 | var segmenter sego.Segmenter 13 | var wordfilterMap = make(map[string]bool) 14 | 15 | var gopath string = os.Getenv("GOPATH") 16 | 17 | func init() { 18 | segmenter.LoadDictionary(gopath + "/src/github.com/k4s/tea/libs/wordfilter/dictionary.txt") 19 | wordlist := loadword() 20 | for _, v := range wordlist { 21 | wordfilterMap[v] = true 22 | } 23 | } 24 | 25 | //对外过滤接口 26 | func Wordfilter(words string) string { 27 | text := []byte(words) 28 | segments := segmenter.Segment(text) 29 | wordslice := sego.SegmentsToSlice(segments, true) 30 | for _, word := range wordslice { 31 | if _, ok := wordfilterMap[word]; ok { 32 | words = strings.Replace(words, word, stringToStar(word), -1) 33 | } 34 | } 35 | return words 36 | } 37 | 38 | //加载敏感词 39 | func loadword() []string { 40 | f, err := os.Open(gopath + "/src/github.com/k4s/tea/libs/wordfilter/wordfilter.txt") 41 | if err != nil { 42 | log.Fatal("%v", err) 43 | } 44 | defer f.Close() 45 | fd, err := ioutil.ReadAll(f) 46 | wordlist := strings.Split(string(fd), ",") 47 | return wordlist 48 | } 49 | 50 | //文字转换为"*" 51 | func stringToStar(word string) string { 52 | var star string 53 | for i := 0; i < len(word)/3; i++ { 54 | star += "*" 55 | } 56 | return star 57 | } 58 | -------------------------------------------------------------------------------- /libs/wordfilter/wordfilter.txt: -------------------------------------------------------------------------------- 1 | 毛泽东,周恩来,刘少奇,朱德,彭德怀,林彪,刘伯承,陈毅,贺龙,聂荣臻,徐向前,罗荣桓,叶剑英,李大钊,陈独秀,孙中山,孙文,孙逸仙,邓小平,陈云,江泽民,李鹏,朱镕基,李瑞环,尉健行,李岚清,胡锦涛,罗干,温家宝,吴邦国,曾庆红,贾庆林,黄菊,吴官正,李长春,吴仪,回良玉,曾培炎,周永康,曹刚川,唐家璇,华建敏,陈至立,陈良宇,张德江,张立昌,俞正声,王乐泉,刘云山,王刚,王兆国,刘淇,贺国强,郭伯雄,胡耀邦,王乐泉,王兆国,周永康,李登辉,连战,陈水扁,宋楚瑜,吕秀莲,郁慕明,蒋介石,蒋中正,蒋经国,马英九,习近平,李克强,彭丽媛,吴帮国,无帮国,无邦国,无帮过,瘟家宝,假庆林,甲庆林,假青林,离长春,习远平,袭近平,李磕墙,贺过墙,和锅枪,粥永康,轴永康,肘永康,周健康,粥健康,周小康,姜异康,孙政才,令计划,陆,昊,范长龙,许其亮,习近平,马凯,王岐山,王沪宁,刘云山,刘延东,刘奇葆,许其亮,孙春兰,孙政才,李克强,李建国,李源潮,汪洋,张春贤,张高丽,张德江,孟建柱,赵乐际,胡春华,俞正声,栗战书,郭金龙,韩正,刘奇葆,赵乐际,栗战书,杜青林,赵洪祝,杨晶,胡锦涛,江泽民,吴邦国,温家宝,贾庆林,李长春,习近平,李克强,贺国强,周永康,王刚,王乐泉,王兆国,王岐山,回良玉,刘淇,刘云山,刘延东,李源潮,汪洋,张高丽,张德江,俞正声,徐才厚,郭伯雄,李鹏,万里,乔石,朱镕基,李瑞环,宋平,尉健行,李岚清,曾庆红,吴官正,罗干,范长龙,许其亮,何勇,令计划,王沪宁,于广洲,马凯,马飚,马兴瑞,马晓天,王君,王侠,王珉,王勇,王晨,王毅,王三运,王万宾,王玉普,王正伟,王东明,王光亚,王伟光,王安顺,王志刚,王岐山,王沪宁,王国生,王学军,王建平,王胜俊,王洪尧,王宪魁,王冠中,王家瑞,王教成,王新宪,王儒林,支树平,尤权,车俊,尹蔚民,巴音朝鲁,巴特尔,卢展工,叶小文,田中,田修思,白玛赤林,白春礼,令计划,吉炳轩,朱小丹,朱福熙,全哲洙,刘鹏,刘源,刘鹤,刘云山,刘亚洲,刘成军,刘伟平,刘延东,刘奇葆,刘晓江,刘家义,刘粤军,刘福连,许达哲,许其亮,许耀元,孙怀山,孙建国,孙春兰,孙政才,孙思敬,苏树林,杜青林,杜金才,杜恒岩,李伟,李斌,李从军,李东生,李立国,李纪恒,李克强,李学勇,李建华,李建国,李鸿忠,李源潮,杨晶,杨传堂,杨金山,杨栋梁,杨洁篪,杨焕宁,肖钢,肖捷,吴昌德,吴胜利,吴爱英,吴新雄,何毅亭,冷溶,汪洋,汪永清,沈跃跃,沈德咏,宋大涵,宋秀岩,张阳,张茅,张毅,张又侠,张仕波,张庆伟,张庆黎,张志军,张国清,张宝顺,张春贤,张高丽,张海阳,张裔炯,张德江,陆昊,陈希,陈雷,陈全国,陈求发,陈宝生,陈政高,陈敏尔,努尔?白克力,苗圩,林军,林左鸣,尚福林,罗志军,罗保铭,周济,周强,周本顺,周生贤,郑卫平,房峰辉,孟学农,孟建柱,项俊波,赵实,赵正永,赵乐际,赵克石,赵克志,赵宗岐,赵洪祝,胡泽君,胡春华,俞正声,姜大明,姜异康,骆惠宁,秦光荣,袁纯清,袁贵仁,耿惠昌,聂卫国,栗战书,贾廷安,夏宝龙,铁凝,徐守盛,徐绍史,徐粉林,高虎城,郭声琨,郭金龙,郭庚茂,郭树清,黄兴国,黄奇帆,黄树贤,曹建明,戚建国,常万全,鹿心社,彭勇,彭清华,蒋定之,蒋建国,蒋洁敏,韩正,韩长赋,焦焕成,谢伏瞻,强卫,楼继伟,解振华,褚益民,蔡武,蔡名照,蔡英挺,蔡赴朝,雒树刚,魏亮,魏凤和,于春生,马勇霞,王伟,王炜,王长河,王东峰,王立英,王仲田,王华庆,王会生,王岐山,王怀臣,王忠民,王和民,王宜林,王晓龙,王家胜,王宾宜,王森泰,王瑞生,丹珠昂奔,尹晋华,石生龙,叶青纯,申维辰,付建华,冯惠敏,宁高宁,弘强,曲青山,曲淑辉,吕建成,任泽民,多杰热旦,刘滨,刘长银,刘生杰,刘向松,刘金国,刘建华, 刘晓滨,刘赐贵,江必新,安立敏,苏波,杜金才,杜金富,李宁,李刚,李熙,李五四,李书磊,李玉赋,李兆前,李法泉,李建波,李适时,李秋芳,李家祥,杨立顺,杨志今,杨明生,杨晓渡,肖亚庆,吴刚,吴玉良,吴杰明,岑旭,邱学强,何平,余欣荣,辛维光 汪民,宋明昌,宋爱荣,宋璇涛,张力,张军,张勇,张立军,张纪南,张昌平,张晓兰, 张晓刚,陈伦,陈大卫,陈文清,陈训秋,陈建民,陈绪国,陈新权,苗华,金书波,周英,周泽民,周福启,郑国光,赵洪祝,胡玉敏,胡问鸣,侯凯,侯长安,侯贺华,俞贵麟,姚增科,袁彦鹏,耿文清,耿燎原,柴绍良,徐敬业,郭永平,郭向远,黄先耀,黄建国,黄建盛,黄树贤,黄晓薇,黄殿中,曹培玺,崔少鹏,梁滨,董力,韩亨林,谢杭生,谢国明, 强卫东,臧献甫,熊维平,黎晓宏,布什,布莱尔,小泉,纯一郎,萨马兰奇,安南,阿拉法特,普京,默克尔,克林顿,里根,尼克松,林肯,杜鲁门,赫鲁晓夫,列宁,斯大林,马克思,恩格斯,金正日,金日成,萨达姆,胡志明,西哈努克,希拉克,撒切尔,阿罗约,曼德拉,卡斯特罗,富兰克林,华盛顿,艾森豪威尔,拿破仑,亚历山大,路易,拉姆斯菲尔德,劳拉,鲍威尔,奥巴马,本拉登,奥马尔,柴玲,达赖喇嘛,江青,张春桥,姚文元,王洪文,东条英机,希特勒,墨索里尼,冈村秀树,冈村宁次,高丽朴,赵紫阳,王丹,沃尔开西,李洪志,李大师,赖昌星,马加爵,班禅,额尔德尼,山本五十六,阿扁,阿扁万岁,热那亚,热比娅,六四,六四运动,美国之音,密宗,民国,民进党,民运,民主,民主潮,摩门教,纳粹,南华早报,南蛮,明慧网,起义,亲民党,瘸腿帮,人民报,法轮功,法轮大法,打倒共产党,台独万岁,圣战,示威,台独,台独分子,台联,台湾民国,台湾岛国,台湾国,台湾独立,太子党,天安门事件,屠杀,小泉,新党,新疆独立,新疆分裂,新疆国,疆独,西藏独立,西藏分裂,西藏国,藏独,藏青会,藏妇会,学潮,学运,一党专政,一中一台,两个中国,一贯道,游行,造反,真善忍,镇压,政变,政治,政治反对派,政治犯,中共,共产党,反党,反共,政府,民主党,中国之春,转法轮,自焚,共党,共匪,苏家屯,基地组织,塔利班,东亚病夫,支那,高治联,高自联,专政,专制,世界维吾尔大会,核工业基地,核武器,铀,原子弹,氢弹,导弹,核潜艇,大参考,小参考,国内动态清样,多维,河殇,摩门教,穆罕默德,圣战,耶和华,耶稣,伊斯兰,真主安拉,白莲教,天主教,基督教,东正教,大法,法轮,法轮功,瘸腿帮,真理教,真善忍,转法轮,自焚,走向圆满,黄大仙,跳大神,神汉,神婆,真理教,大卫教,阎王,黑白无常,牛头马面,藏独,高丽棒子,疆独,蒙古鞑子,台独,台独分子,台联,台湾民国,西藏独立,新疆独立,南蛮,老毛子,回民吃猪肉,谋杀,杀人,吸毒,贩毒,赌博,拐卖,走私,卖淫,造反,强奸,轮奸,抢劫,先奸后杀,下注,抽头,坐庄,赌马,赌球,筹码,老虎机,轮盘赌,安非他命,大麻,可卡因,海洛因,冰毒,摇头丸,杜冷丁,鸦片,罂粟,迷幻药,白粉,嗑药,吸毒,AIDS,aids,Aids,DICK,dick,Dick,penis,sex,SM,屙,爱滋,淋病,梅毒,爱液,屄,逼,臭机八,臭鸡巴,吹喇叭,吹箫,催情药,屌,肛交,肛门,龟头,黄色,机八,机巴,鸡八,鸡巴,机掰,机巴,鸡叭,鸡鸡,鸡掰,鸡奸,妓女,奸,茎,精液,精子,尻,口交,滥交,乱交,轮奸,卖淫,屁眼,嫖娼,强奸,强奸犯,情色,肉棒,乳房,乳峰,乳交,乳头,乳晕,三陪,色情,射精,手淫,威而钢,威而柔,伟哥,性高潮,性交,性虐,性欲,穴,颜射,阳物,一夜情,阴部,阴唇,阴道,阴蒂,阴核,阴户,阴茎,阴门,淫,淫秽,淫乱,淫水,淫娃,淫液,淫汁,淫穴,淫洞,援交妹,做爱,梦遗,阳痿,早泄,奸淫,性欲,性交,Bitch,bt,cao,FUCK,Fuck,fuck,kao,NMD,NND,sb,shit,SHIT,SUCK,Suck,tmd,TMD,tnnd,K他命,白痴,笨蛋,屄,变态,婊子,操她妈,操妳妈,操你,操你妈,操他妈,草你,肏,册那,侧那,测拿,插,蠢猪,荡妇,发骚,废物,干她妈,干妳,干妳娘,干你,干你妈,干你妈B,干你妈b,干你妈逼,干你娘,干他妈,狗娘养的,滚,鸡奸,贱货,贱人,烂人,老母,老土,妈比,妈的,马的,妳老母的,妳娘的,你妈逼,破鞋,仆街,去她妈,去妳的,去妳妈,去你的,去你妈,去死,去他妈,日,日你,赛她娘,赛妳娘,赛你娘,赛他娘,骚货,傻B,傻比,傻子,上妳,上你,神经病,屎,屎妳娘,屎你娘,他妈的,王八蛋,我操,我日,乡巴佬,猪猡,屙,干,尿,掯,屌,操,骑你,湿了,操你,操他,操她,骑你,骑他,骑她,欠骑,欠人骑,来爽我,来插我,干你,干他,干她,干死,干爆,干机,FUCK,机叭,臭鸡,臭机,烂鸟,览叫,阳具,肉棒,肉壶,奶子,摸咪咪,干鸡,干入,小穴,强奸,插你,插你,爽你,爽你,干干,干X,我操,他干,干它,干牠,干您,干汝,干林,操林,干尼,操尼,我咧干,干勒,干我,干到,干啦,干爽,欠干,狗干,我干,来干,轮干,轮流干,干一干,援交,骑你,我操,轮奸,鸡奸,奸暴,再奸,我奸,奸你,奸你,奸他,奸她,奸一奸,淫水,淫湿,鸡歪,仆街,臭西,尻,遗精,烂逼,大血比,叼你妈,靠你妈,草你,干你,日你,插你,奸你,戳你,逼你老母,挨球,我日你,草拟妈,卖逼,狗操卖逼,奸淫,日死,奶子,阴茎,奶娘,他娘,她娘,骚B,你妈了妹,逼毛,插你妈,叼你,渣波波,嫩b,weelaa,缔顺,帝顺,蒂顺,系统消息,午夜,看下,草泥马,法克鱿,雅蠛蝶,潜烈蟹,菊花蚕,尾申鲸,吉跋猫,搞栗棒,吟稻雁,达菲鸡,SM,ML,3P,群P,马勒戈壁,双飞,fuck,共产党,urban,我操,cao,他妈的,TMD,鸡巴,煞笔,傻B,法轮功,江泽民,胡锦涛,温家宝,urban-rivals,rivals,我日,UR,ur,性交,口交,婊子,妓女,她妈,牛逼,牛B,牛比,煞笔,傻逼,傻B,操你妈,装逼,装B,日你妈,不玩了,删号,妈的,妈逼,草你妈,T.M.D,JB,jb,,,出售账号,出售此号,卖号,U/R,U-R,j8,吗的,8仙,狗日,出售神符,色情,黄色,藏独,台独,法轮大法,鸡巴,毛泽东,NPC,*法*轮*功*,*李*洪*志*阿扁,阿扁万岁,阿拉,阿拉法特,挨球,安南,安全局,澳洲光明网,八九,八九风波,办理文凭,办理证件,包皮,保钓,保监会,保密局,鸨,鲍岳桥,暴动,暴乱,暴徒,北京之春,贝肉,本拉登,本?拉登,苯比,笨屄,笨逼,屄,屄毛,逼毛,逼你老母,逼样,比毛,婊,婊子,宾周,冰毒,波霸,博讯,薄一波,布莱尔,布雷尔,布什,布什,财政部,参事室,藏独,藏独,藏独,操,操GM,操Gm,操gM,操gm,操XX,操逼,操比,操蛋,操你,操你八辈祖宗,操你妈,操你妈屄,操他,曹刚川,草的你妈,草妈,草你妈,草拟妈,肏,测绘局,插GM,插Gm,插gM,插gm,插妳,插你,插你妈,插深些,产权局,朝鲜,车臣,车仑,陈功,陈良宇,陈水扁,陈希同,陈晓宁,陈毅,陈至立,成人电影,成人片,吃大便,吃屎,迟浩田,赤匪,抽插,抽你丫的,臭化西,出售假币,出售枪支,出售手枪,吹喇叭,吹箫,春药,蠢猪,戳你,粗制吗啡,催情药,达赖,达赖喇嘛,打炮,,大B,大逼,大便,大波波,大麻,大麻树脂,大麻油,大师,戴维教,大学骚乱,大血B,大血比,呆卵,戴海静,戴红,戴晶,戴维教,党主席,荡妇,档案局,盗窃犯,盗窃犯,道教,邓小平,帝国主义,电监会,叼你,叼你妈,屌,屌7,屌鸠,屌毛,屌妳,屌七,屌西,钓鱼台,丁关根,丁子霖,东北独立,东升,东条英机,东突,东突暴动和独立,东突组织,东亚病夫,董建华,董贱华,董文华,懂文华,独立,独立台湾会,恩格斯,二B,二屄,二逼,二乙基酰胺发抡,发抡功,发伦,发伦功,发轮,发论,发论公,发论功,发骚,法(轮)功,法*轮*功,法功,法愣,法仑,法轮,法轮大法,法轮功,法西斯,法制办,反动,反革命,反共,反华,反恐委员会,反日,反政府,分裂祖国,佛教,佛展千手法,佛祖,斧头镰刀,阝月,傅鹏,傅作义,干GM,干Gm,干gM,干gm,干拎娘,干妳,干妳老母,干妳妈,干妳娘,干你,干你妈,干你妈b,干你妈逼,干你娘,干七八,干死你,肛,肛交,肛门,港澳办,高俊,高丽棒子,高校暴乱,高校群体事件,高校骚乱,睾,睾丸,膏药旗,弓虽女干,公安,公安部,公安局,共产党,共产主义,共匪,共狗,狗b,狗操,狗卵,狗娘,狗屁,狗日,狗日的,狗屎,观世音,官逼民反,官商勾结,龟儿子,龟公,龟孙子,龟头,鬼村,滚,郭伯雄,国安局,国防部,国防科工委,国管局,国际法院,国家民委,国家主席,国家主要部委,国民党,国民党万岁,海洛因,海洋局,何候华,贺国强,贺龙,黑社会,黑手党,黑手党,黑手党,红卫兵,洪兴,洪志,后庭,胡XX,胡紧涛,胡紧掏,胡紧套,胡锦涛,胡锦淘,胡乔木,胡耀邦,胡主席,花柳,华国锋,华建敏,换妻,黄 菊,黄菊,黄色电影,黄色小电影,回教,回良玉,回民暴动,回族人吃猪肉,昏药,火棒,机八,机巴,鸡八,鸡巴,鸡叭,鸡芭,鸡掰,鸡奸,基地组织,基督,基督教,激情电影,激情小电影,鸡,计牌软件,计生委,妓,妓女,妓院,贾庆林,奸,奸夫淫妇,奸你,奸淫,贱,贱逼,贱货,贱人,江Core,江八,江八点,江独裁,江核心,江青,江戏子,江择民,江泽民,江贼民,江折民,江猪,江猪媳,江主席,僵贼民,疆独,蒋介石,蒋经国,蒋中正,酱猪媳,交通部,姣西,叫床,叫鸡,叫小姐,教育部,她妈的金日成,金正日,禁书,经济社会理事会,经社理事会,精液,精子,警匪一家,敬国神社,靖国神社,静坐,纠察员,鸠,鸠屎,军长发威,军国主义,军妓,尻,靠,靠你妈,靠腰,可待因,可卡叶,可卡因,克林顿,恐怖份子,恐怖主义,口交,寇晓伟,狂操,狂操你全家,拉登,拉姆斯菲尔德,懒教,烂B,烂屄,烂逼,烂比,烂屌,烂货,劳+教+所,劳动保障部,老逼,老毛子,老母,黎阳评,李长春,李登辉,李弘旨,李红志,李宏旨,李宏志,李洪志,李岚清,李鹏,李鹏*,李瑞环,李山,李铁映,李先念,连战,联大,联合国,联合国大会,联易,联易互动,粮食局,两腿之间,列宁,林彪,林业局,刘 淇,刘军,刘淇,刘少奇,刘云山,流氓,六.四,六。四,六?四,六合彩,六四,六-四,六四事件,六四真相,龙新民,吕秀莲,旅游局,卵,轮功,轮奸,罗 干,罗干,骡干,妈逼,妈比,妈卖妈屁,妈批,妈祖,妈B,妈的,麻醉钢枪,麻醉枪,麻醉药,麻醉乙醚,马克思,马卖马屁,马英九,吗啡,吗啡碱,吗啡片,买财富,买卖枪支,麦角酸,卖.国,卖B,卖ID,卖QQ,卖逼,卖比,卖财富,卖党求荣,卖国,卖国求荣,卖号,卖卡,卖软件,卖淫,毛XX,毛厕洞,毛一鲜,毛泽东,毛贼东,毛主席,梅花网,美国,美国佬,美国之音,美利坚,蒙尘药,蒙独,蒙古达子,蒙古独立,迷魂药,迷奸药,迷歼药,迷药,密洞,密宗,民航局,民进党,民运,民政部,明慧网,摩门教,莫索里尼,穆罕默德,穆斯林,乳头,奶子,妳老母的,妳妈的,妳马的,妳娘的,南联盟,南蛮子,南蛮子,嫩B,嫩b,伱妈,你爸,你大爷,你二大爷,你老母,你老味,你姥,你姥姥的,你妈,你妈逼,你妈的,你娘,你爷爷的,鸟GM,鸟Gm,鸟gM,鸟gm,鸟你,牛逼,牛比,农业部,虐待,拍肩神药,喷你,彭真,皮条,屁眼,嫖客,苹果日报,破坏,破鞋,仆街,普京,气象局,钱其琛,枪决女犯,枪决现场,枪支弹药,强奸,强奸犯,强卫,强效失意药,强硬发言,抢劫,乔石,侨办,切七,窃听器,窃听器材,亲民党,青天白日,情色,去你妈的,去死,全国人大,瘸腿帮,人大,人大代表,人代会,人弹,人民,人民大会堂,人民广场,人民日报,人民银行,人体炸弹,日GM,日Gm,日gM,日gm,日X妈,日本RING,日本鬼子,日你,日你妈,日你娘,日他娘,肉棒,肉壁,肉洞,肉缝,肉棍,肉棍子,肉穴,乳,乳波臀浪,乳房,乳交,乳头,撒尿,萨达姆,塞白,塞你爸,塞你公,塞你老母,塞你老师,塞你母,塞你娘,三个呆婊,三个代婊,三级片,三民主义,三陪,三陪女,三去车仑,三唑仑,骚,骚B,骚逼,骚货,骚,色情,色情电影,色情服务,色情小电影,杀人犯,傻B,傻屄,傻逼,傻比,傻吊,傻卵,傻子,煞逼,商务部,上妳,上你,社科院,射精,身份生成器,神经病,神通加持法,生鸦片,圣女峰,十八摸,十年动乱石进,食捻屎,食屎,驶你爸,驶你公,驶你老母,驶你老师,驶你母,驶你娘,是鸡,手淫,受虐狂,售ID,售号,售软件,双峰微颤,氵去,水利部,水去车仑,税务总局,司法部,私服,私/服,私\\服,私服,私-服,私—服,斯大林,死gd,死GD,死gm,死GM,死全家,四川独立,四人帮,宋楚瑜,宋祖英,孙文,孙逸仙,孙中山,他爹,他妈,他妈的,他马的,他母亲,他祖宗,台办,台独,台联,台湾党,台湾帝国,台湾独立,台湾共产党,台湾共和国,台湾狗,台湾国,台湾民国,太监,太子党,唐家璇,天皇陛下,田纪云,舔西,投毒杀人,透视软件,推油,外 挂,外挂,外/挂,外\\挂,外_挂,外挂,外-挂,外—挂,外汇局,外交部,外专局,晚年周恩来,万税,王八蛋,王宝森,王刚,王昊,王乐泉,王岐山,王太华,王兆国,王震,网管,威而钢,威而柔,卫生部,尉健行,温加宝,温家宝,温家保,温馨,温总理,文化部,文物局,倭国,倭寇,我操,我操你,我干,我妳老爸,我日,我日你,无界浏览器,吴 仪,吴邦国,吴官正,吴仪,五星红旗,西藏独立,西藏天葬,希拉克,希特勒,希望之声,洗脑班,系统,系统公告,系统讯息,鲜族,乡巴佬,想上你,小鸡鸡,小泉,小泉纯一郎,小日本,小肉粒,小乳头,小穴,邪教,新疆独立,兴奋剂,性爱,性交,性虐待,性无能,性欲,徐光春,学潮,血逼,血腥图片,鸦片,鸦片液,鸦片渣,烟草局,严方军,阳精,阳具,摇头丸,摇头玩,耶和华,耶苏,耶稣,叶剑英,夜情,一党专制,一贯道,一国两制,一夜情,一中一台,伊拉克,伊朗,伊斯兰,以茎至洞,抑制剂,阴部,阴唇,阴道,阴蒂,阴核,阴户,阴茎,阴毛,阴水,阴小撕大,淫,淫荡,淫秽,淫货,淫贱,淫叫,淫毛,淫靡,淫水,淫娃,淫语连连,淫欲,英雄纪念碑,硬挺,邮政局,游戏发奖员,游戏宫理员,游戏管理员,游行,俞正声,舆论钳制,玉杵,欲火焚身,原子能机构,援交,远程偷拍,曰GM,曰Gm,曰gM,曰gm,曰你,月经,月经不调,月经,扎卡维是英雄,杂种,造反,曾培炎,曾庆红,扎卡维,张朝阳,张潮阳,张德江,张磊,张立昌,张小平,赵紫阳,侦探设备,真理教,中国恐怖组织,中华民国,中南海,中宣部,周恩来,周永康,周总理,朱德,朱容鸡,朱容基,朱熔基,朱镕基,朱总理,猪操,猪容基,主席,转法轮,转法轮,装屄,装逼,追查国际,子女任职名单,自焚,自杀手册,自杀指南,自制手枪,自治机关,宗教,总局,总理,作爱,坐台的,做爱,共产党,江泽民,胡锦涛,温家宝,严方军,屄毛,操逼毛,东突,骚货,婊子,妓女,他妈,她妈,牛逼,牛比,牛B,煞笔,傻逼,傻B,你妈,操你妈,装逼,装B,日你妈,不玩了,删号,卖号,垃圾游戏,烂游戏,删号,妈的,妈逼,草你妈,T.M.D,JB,jb,淘宝,出售账号,出售此号,卖号,U/R,U-R,j8,吗的,8仙,狗日,出售神符,色情,黄色,h站,龙虎,虎门,龙虎门,WEB牌戰,WEB战牌,战牌,8仙,UR,ur,UR,街头对抗,藏独,台独,法轮大法,混沌决,ur,UR,urban,鸡巴,坐台的,作爱,总理,宗教,自治机关,自制手枪,你大爷,你二大爷,你老母,你老味,你姥,你姥姥的,你妈,你妈逼,你妈的,你娘,你爷爷的,鸟GM,鸟Gm,鸟gM,鸟gm,鸟你,牛逼,牛比,农业部,虐待,拍肩神药,喷你,彭真,皮条,屁眼,嫖客,苹果日报,破坏,破鞋,仆街,普京,气象局,钱其琛,枪决女犯,枪决现场,枪支弹药,强奸,强奸犯,强卫,强效失意药,强硬发言,抢劫,乔石,侨办,切七,窃听器,窃听器材,亲民党,青天白日,情色,去你妈的,去死,全国人大,瘸腿帮,人大,人大代表,人代会,人弹,人民,人民大会堂,人民广场,人民日报,人民银行,人体炸弹,日GM,日Gm,日gM,日gm,日X妈,日本RING,日本鬼子,日你,日你妈,日你娘,日他娘,肉棒,肉壁,肉洞,肉缝,肉棍,肉棍子,肉穴,乳,乳波臀浪,乳房,乳交,乳头,撒尿,萨达姆,塞白,塞你爸,塞你公,塞你老母,塞你老师,塞你母,塞你娘,三个呆婊,三个代婊,三级片,三民主义,三陪,三陪女,三去车仑,三唑仑,骚,骚B,骚逼,骚货,骚,色情,色情电影,色情服务,色情小电影,杀人犯,傻B,傻屄,傻逼,傻比,傻吊,傻卵,傻子,煞逼,商务部,上妳,上你,社.会.正.义.力.量,社保基金会,社会主义,社科院,射精,身份生成器,神经病,神通加持法,审计署,升达毕业证,生春袋,生孩子没屁眼,生鸦片,圣女峰,湿透的内裤,十八摸,十年动乱,十五周年,石进,食捻屎,食屎,驶你爸,驶你公,驶你老母,驶你老师,驶你母,驶你娘,世界日报,是鸡,手机复制,手淫,受虐狂,售ID,售号,售软件,双峰微颤,氵去,水利部,水去车仑,税务总局,司法部,私服,私/服,私\\服,私服,私-服,私—服,斯大林,死gd,死GD,死gm,死GM,死全家,四川独立,四人帮,宋楚瑜,宋祖英,孙文,孙逸仙,孙中山,他爹,他妈,他妈的,他马的,他母亲,他祖宗,台办,台独,台联,台湾党,台湾帝国,台湾独立,台湾共产党,台湾共和国,台湾狗,台湾国,台湾民国,太监,太子党,唐家璇,特别公告,特码,体育总局,天安门,天安门档案,天安门录像带,天安门事件,天安门屠杀,天安门一代,天鹅之旅,天皇,天皇陛下,田纪云,舔西,铁道部,统计局,曾庆红,扎卡维,扎卡维是英雄,张朝阳,张潮阳,张德江,张磊,张立昌,张小平,赵紫阳,侦探设备,真理教,真善忍,镇压,正见网,正清网,正悟网,证监会,政变,政协,值勤,值勤账号,指导员,质检局,致幻剂,中共,中共中央,中国,中国共产党,中国恐怖组织,中华民国,中华人民共和国,中科院,中南海,中宣部,中央,中央电视台,中央政治局,中医药局,周恩来,周永康,周总理,朱德,朱容鸡,朱容基,朱熔基,朱镕基,朱总理,猪操,猪容基,主席,转法轮,转法轮,装屄,装逼,追查国际,子女任职名单,自焚,自杀手册,自杀指南,自由亚洲电台,自由之门,自制手枪,自治机关,广电,@sshole,a$$hole,a$shole,Admin,as$hole,ASS,asshole,bastard,bbscity,beijingspring,bignews,bitch,Bitch,bjzc,boxun,bt,butthead,butthole,cctv,CCTV,cdjp,chengmingmag,chinesenewsweek,cunt,dajiyuan,damm,damn,dick,Dick,DICK,epochtimes,F.L.G,falun,fawanghuihui,fgmtv,flg,FLG,fofg,fosaon,fu(,fuc,Fuck,fuck,FUCK,FUCKYOU,fuckyou,fuk,fv(,fvc,gamemaster,GAMEMASTER,gameMASTER,GAMEmaster,GAME master,game MASTER,GAME MASTER,game master,GameMaste,GameMaster,GAMEMASTER,gc365,globalrescue,Gm,gM,gm,minghui,mingpaonews,minhui,NMD,NND,nnd,on9,ON9,orgasmus,peacehall,penis,phuc,piss,PUSSY,pussy,renminbao,ri,SB,sb,screw,secretchina,sega,sex,sf,sh!t,shengda,SHIT,shit,shyt,SM,snatch,soundofhope,SUCK,suck,Suck,TMD,tmd,TNND,tnnd,WG,wg,WG,Wg,wG,wg,xinguangming,xinsheng,yuanming,zhengjian,zhengqing,zhengwunet,zhongguohun -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "os" 8 | "path" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // levels 14 | const ( 15 | debugLevel = 0 16 | releaseLevel = 1 17 | errorLevel = 2 18 | fatalLevel = 3 19 | ) 20 | 21 | const ( 22 | printDebugLevel = "[debug ] " 23 | printReleaseLevel = "[release] " 24 | printErrorLevel = "[error ] " 25 | printFatalLevel = "[fatal ] " 26 | ) 27 | 28 | type Logger struct { 29 | level int 30 | baseLogger *log.Logger 31 | baseFile *os.File 32 | } 33 | 34 | func New(strLevel string, pathname string, flag int) (*Logger, error) { 35 | // level 36 | var level int 37 | switch strings.ToLower(strLevel) { 38 | case "debug": 39 | level = debugLevel 40 | case "release": 41 | level = releaseLevel 42 | case "error": 43 | level = errorLevel 44 | case "fatal": 45 | level = fatalLevel 46 | default: 47 | return nil, errors.New("unknown level: " + strLevel) 48 | } 49 | 50 | // logger 51 | var baseLogger *log.Logger 52 | var baseFile *os.File 53 | if pathname != "" { 54 | now := time.Now() 55 | 56 | filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log", 57 | now.Year(), 58 | now.Month(), 59 | now.Day(), 60 | now.Hour(), 61 | now.Minute(), 62 | now.Second()) 63 | 64 | file, err := os.Create(path.Join(pathname, filename)) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | baseLogger = log.New(file, "", flag) 70 | baseFile = file 71 | } else { 72 | baseLogger = log.New(os.Stdout, "", flag) 73 | } 74 | 75 | // new 76 | logger := new(Logger) 77 | logger.level = level 78 | logger.baseLogger = baseLogger 79 | logger.baseFile = baseFile 80 | 81 | return logger, nil 82 | } 83 | 84 | // It's dangerous to call the method on logging 85 | func (logger *Logger) Close() { 86 | if logger.baseFile != nil { 87 | logger.baseFile.Close() 88 | } 89 | 90 | logger.baseLogger = nil 91 | logger.baseFile = nil 92 | } 93 | 94 | func (logger *Logger) doPrintf(level int, printLevel string, format string, a ...interface{}) { 95 | if level < logger.level { 96 | return 97 | } 98 | if logger.baseLogger == nil { 99 | panic("logger closed") 100 | } 101 | 102 | format = printLevel + format 103 | logger.baseLogger.Output(3, fmt.Sprintf(format, a...)) 104 | 105 | if level == fatalLevel { 106 | os.Exit(1) 107 | } 108 | } 109 | 110 | func (logger *Logger) Debug(format string, a ...interface{}) { 111 | logger.doPrintf(debugLevel, printDebugLevel, format, a...) 112 | } 113 | 114 | func (logger *Logger) Release(format string, a ...interface{}) { 115 | logger.doPrintf(releaseLevel, printReleaseLevel, format, a...) 116 | } 117 | 118 | func (logger *Logger) Error(format string, a ...interface{}) { 119 | logger.doPrintf(errorLevel, printErrorLevel, format, a...) 120 | } 121 | 122 | func (logger *Logger) Fatal(format string, a ...interface{}) { 123 | logger.doPrintf(fatalLevel, printFatalLevel, format, a...) 124 | } 125 | 126 | var gLogger, _ = New("debug", "", log.LstdFlags) 127 | 128 | // It's dangerous to call the method on logging 129 | func Export(logger *Logger) { 130 | if logger != nil { 131 | gLogger = logger 132 | } 133 | } 134 | 135 | func Debug(format string, a ...interface{}) { 136 | gLogger.doPrintf(debugLevel, printDebugLevel, format, a...) 137 | } 138 | 139 | func Release(format string, a ...interface{}) { 140 | gLogger.doPrintf(releaseLevel, printReleaseLevel, format, a...) 141 | } 142 | 143 | func Error(format string, a ...interface{}) { 144 | gLogger.doPrintf(errorLevel, printErrorLevel, format, a...) 145 | } 146 | 147 | func Fatal(format string, a ...interface{}) { 148 | gLogger.doPrintf(fatalLevel, printFatalLevel, format, a...) 149 | } 150 | 151 | func Close() { 152 | gLogger.Close() 153 | } 154 | -------------------------------------------------------------------------------- /message/message.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | //Message封装了来回交换的messages 9 | type Message struct { 10 | Header []byte 11 | ConnID uint32 12 | Body []byte 13 | bsize int 14 | refcnt int32 15 | pool *sync.Pool 16 | } 17 | 18 | type msgCacheInfo struct { 19 | maxbody int 20 | pool *sync.Pool 21 | } 22 | 23 | func newMsg(sz int) *Message { 24 | m := &Message{} 25 | m.Header = make([]byte, 0, 4) 26 | m.Body = make([]byte, 0, sz) 27 | m.bsize = sz 28 | return m 29 | } 30 | 31 | // 按size切割多个Message的池 32 | var messageCache = []msgCacheInfo{ 33 | { 34 | maxbody: 64, 35 | pool: &sync.Pool{ 36 | New: func() interface{} { return newMsg(64) }, 37 | }, 38 | }, { 39 | maxbody: 128, 40 | pool: &sync.Pool{ 41 | New: func() interface{} { return newMsg(128) }, 42 | }, 43 | }, { 44 | maxbody: 256, 45 | pool: &sync.Pool{ 46 | New: func() interface{} { return newMsg(256) }, 47 | }, 48 | }, { 49 | maxbody: 512, 50 | pool: &sync.Pool{ 51 | New: func() interface{} { return newMsg(512) }, 52 | }, 53 | }, { 54 | maxbody: 1024, 55 | pool: &sync.Pool{ 56 | New: func() interface{} { return newMsg(1024) }, 57 | }, 58 | }, { 59 | maxbody: 4096, 60 | pool: &sync.Pool{ 61 | New: func() interface{} { return newMsg(4096) }, 62 | }, 63 | }, { 64 | maxbody: 8192, 65 | pool: &sync.Pool{ 66 | New: func() interface{} { return newMsg(8192) }, 67 | }, 68 | }, { 69 | maxbody: 65536, 70 | pool: &sync.Pool{ 71 | New: func() interface{} { return newMsg(65536) }, 72 | }, 73 | }, 74 | } 75 | 76 | //Free 在message上减少引用计数,如果没有进一步的引用,则释放它的资源 77 | //这样做可以让资源在不使用GC的情况下回收,这对性能有相当大的好处 78 | func (m *Message) Free() { 79 | if v := atomic.AddInt32(&m.refcnt, -1); v > 0 { 80 | return 81 | } 82 | for i := range messageCache { 83 | if m.bsize == messageCache[i].maxbody { 84 | messageCache[i].pool.Put(m) 85 | return 86 | } 87 | } 88 | } 89 | 90 | //Dup 增加消息上的引用计数 91 | func (m *Message) Dup() *Message { 92 | atomic.AddInt32(&m.refcnt, 1) 93 | return m 94 | } 95 | 96 | //NewMessage是获得新Message的方式 97 | //使用池缓存,极大地减少了垃圾收集器的负载 98 | func NewMessage(sz int) *Message { 99 | var m *Message 100 | for i := range messageCache { 101 | if sz < messageCache[i].maxbody { 102 | m = messageCache[i].pool.Get().(*Message) 103 | break 104 | } 105 | } 106 | if m == nil { 107 | m = newMsg(sz) 108 | } 109 | 110 | m.refcnt = 1 111 | return m 112 | } 113 | -------------------------------------------------------------------------------- /network/agent.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "reflect" 7 | 8 | "github.com/k4s/tea/log" 9 | "github.com/k4s/tea/message" 10 | ) 11 | 12 | type Agent interface { 13 | Run() 14 | WriteMsg(*message.Message) 15 | RecvMsg() *message.Message 16 | SetID(uint32) 17 | GetID() uint32 18 | UserData() interface{} 19 | SetUserData(data interface{}) 20 | EndHandle(*message.Message, interface{}) 21 | SetOnCloseFunc(func(Agent)) 22 | IsClose() bool 23 | Close() 24 | } 25 | 26 | type agent struct { 27 | conn Conn 28 | connID uint32 29 | recvMsg chan *message.Message 30 | withID bool 31 | userData interface{} 32 | closeFunc func(Agent) 33 | isClose chan bool 34 | } 35 | 36 | func NewAgent(conn Conn, withID bool) *agent { 37 | return &agent{conn: conn, 38 | withID: withID, 39 | isClose: make(chan bool), 40 | recvMsg: make(chan *message.Message, 100), 41 | } 42 | } 43 | 44 | func (a *agent) Run() { 45 | for { 46 | var msg *message.Message 47 | var err error 48 | if a.withID { 49 | msg, err = a.conn.RecvMsgWithID() 50 | } else { 51 | msg, err = a.conn.RecvMsg() 52 | } 53 | 54 | if err != nil { 55 | a.isClose <- true 56 | log.Debug("read message: %v", err) 57 | break 58 | } 59 | fmt.Println("agent:RUN:", string(msg.Body)) 60 | a.recvMsg <- msg 61 | 62 | } 63 | } 64 | 65 | func (a *agent) SetOnCloseFunc(f func(Agent)) { 66 | a.closeFunc = f 67 | } 68 | 69 | func (a *agent) onClose() { 70 | if a.closeFunc != nil { 71 | a.closeFunc(a) 72 | } 73 | } 74 | 75 | func (a *agent) SetID(id uint32) { 76 | a.connID = id 77 | } 78 | 79 | func (a *agent) GetID() uint32 { 80 | return a.connID 81 | } 82 | 83 | func (a *agent) WriteMsg(msg *message.Message) { 84 | a.conn.SendMsg(msg) 85 | } 86 | 87 | //Endhandle free old msg,and write msg to gate 88 | func (a *agent) EndHandle(oldMsg *message.Message, msg interface{}) { 89 | defer oldMsg.Free() 90 | data, err := json.Marshal(msg) 91 | if err != nil { 92 | log.Error("marshal message %v error: %v", reflect.TypeOf(msg), err) 93 | } 94 | sz := len(data) 95 | var newMsg *message.Message 96 | if oldMsg.ConnID != 0 { 97 | newMsg = message.NewMessage(int(sz+8)) 98 | newMsg.ConnID = oldMsg.ConnID 99 | newMsg.Body = newMsg.Body[0:sz] 100 | } else { 101 | newMsg = message.NewMessage(int(sz+4)) 102 | newMsg.Body = newMsg.Body[0:sz] 103 | } 104 | newMsg.Body = data 105 | a.WriteMsg(newMsg) 106 | } 107 | 108 | func (a *agent) RecvMsg() *message.Message { 109 | return <-a.recvMsg 110 | } 111 | 112 | //Close will exec onClosefunc and close net.conn 113 | func (a *agent) Close() { 114 | a.onClose() 115 | a.conn.Close() 116 | } 117 | 118 | func (a *agent) IsClose() bool { 119 | return <-a.isClose 120 | } 121 | 122 | func (a *agent) UserData() interface{} { 123 | return a.userData 124 | } 125 | 126 | func (a *agent) SetUserData(data interface{}) { 127 | a.userData = data 128 | } 129 | -------------------------------------------------------------------------------- /network/tcpClient.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | "time" 7 | 8 | . "github.com/k4s/tea" 9 | "github.com/k4s/tea/log" 10 | ) 11 | 12 | type TCPClient struct { 13 | sync.Mutex 14 | Addr string 15 | 16 | conns ConnSet 17 | //connections mutex 18 | mutexConns sync.Mutex 19 | //writing waitGroup 20 | wgConns sync.WaitGroup 21 | 22 | NewAgent func(*TCPConn, bool) Agent 23 | agent Agent 24 | 25 | isClose bool 26 | withID bool 27 | opts Options 28 | // msgParser *MsgParser 29 | } 30 | 31 | func (client *TCPClient) Start() { 32 | client.init() 33 | 34 | go client.connect() 35 | } 36 | 37 | func (client *TCPClient) init() { 38 | client.Lock() 39 | defer client.Unlock() 40 | client.opts = make(Options) 41 | for n, v := range client.opts { 42 | switch n { 43 | case OptionMinMsgLen, 44 | OptionMaxMsgLen, 45 | OptionConnNum, 46 | OptionMsgNum, 47 | OptionConnInterval, 48 | OptionLittleEndian: 49 | client.opts.SetOption(n, v) 50 | } 51 | } 52 | 53 | if _, err := client.opts.GetOption(OptionConnNum); err != nil { 54 | client.opts.SetOption(OptionConnNum, 1) 55 | } 56 | 57 | if _, err := client.opts.GetOption(OptionConnInterval); err != nil { 58 | client.opts.SetOption(OptionConnInterval, 3*time.Second) 59 | } 60 | 61 | if _, err := client.opts.GetOption(OptionMsgNum); err != nil { 62 | client.opts.SetOption(OptionMsgNum, 100) 63 | } 64 | 65 | if _, err := client.opts.GetOption(OptionLittleEndian); err != nil { 66 | client.opts.SetOption(OptionLittleEndian, true) 67 | } 68 | 69 | if client.NewAgent == nil { 70 | log.Fatal("NewAgent must not be nil") 71 | } 72 | if client.conns != nil { 73 | log.Fatal("client is running") 74 | } 75 | 76 | client.conns = make(ConnSet) 77 | client.isClose = false 78 | 79 | } 80 | 81 | func (client *TCPClient) dial() net.Conn { 82 | for { 83 | conn, err := net.Dial("tcp", client.Addr) 84 | if err == nil || client.isClose { 85 | return conn 86 | } 87 | log.Release("connect to %v error: %v", client.Addr, err) 88 | 89 | connInterval, ok := client.opts.GetOption(OptionConnInterval) 90 | if ok != nil { 91 | connInterval = 3 * time.Second 92 | } 93 | time.Sleep(connInterval.(time.Duration)) 94 | continue 95 | } 96 | } 97 | 98 | func (client *TCPClient) connect() { 99 | 100 | conn := client.dial() 101 | if conn == nil { 102 | return 103 | } 104 | 105 | client.Lock() 106 | if client.isClose { 107 | client.Unlock() 108 | conn.Close() 109 | return 110 | } 111 | client.conns[conn] = struct{}{} 112 | client.Unlock() 113 | 114 | var tcpConn *TCPConn 115 | if client.withID { 116 | tcpConn = newAgentTCPConn(conn, client.opts) 117 | } else { 118 | tcpConn = newSessionTCPConn(conn, client.opts) 119 | } 120 | agent := client.NewAgent(tcpConn, client.withID) 121 | client.agent = agent 122 | agent.Run() 123 | 124 | // cleanup 125 | tcpConn.Close() 126 | client.Lock() 127 | delete(client.conns, conn) 128 | client.Unlock() 129 | // agent.OnClose() 130 | agent.Close() 131 | } 132 | 133 | func (client *TCPClient) SetWithID(isbool bool) { 134 | client.withID = isbool 135 | } 136 | 137 | func (client *TCPClient) SetOpts(opts Options) { 138 | client.opts = opts 139 | } 140 | 141 | func (client *TCPClient) GetAgent() Agent { 142 | return client.agent 143 | } 144 | 145 | func (client *TCPClient) Close() { 146 | client.Lock() 147 | client.isClose = true 148 | for conn := range client.conns { 149 | conn.Close() 150 | } 151 | client.conns = nil 152 | client.Unlock() 153 | 154 | } 155 | -------------------------------------------------------------------------------- /network/tcpConn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | "net" 7 | "sync" 8 | 9 | . "github.com/k4s/tea" 10 | "github.com/k4s/tea/message" 11 | ) 12 | 13 | type ConnSet map[net.Conn]struct{} 14 | 15 | type Conn interface { 16 | SendMsg(*message.Message) error 17 | RecvMsg() (*message.Message, error) 18 | RecvMsgWithID() (*message.Message, error) 19 | Send([]byte) error 20 | Recv() ([]byte, error) 21 | Close() error 22 | IsOpen() bool 23 | } 24 | 25 | type TCPConn struct { 26 | sync.Mutex 27 | c net.Conn 28 | writeMsg chan *message.Message 29 | isOpen bool 30 | opts Options 31 | } 32 | 33 | func newAgentTCPConn(c net.Conn, opts Options) *TCPConn { 34 | tcpConn := new(TCPConn) 35 | tcpConn.c = c 36 | tcpConn.writeMsg = make(chan *message.Message, 100) 37 | tcpConn.opts = make(Options) 38 | 39 | for n, v := range opts { 40 | switch n { 41 | case OptionMinMsgLen, 42 | OptionMaxMsgLen, 43 | OptionMsgNum, 44 | OptionLittleEndian: 45 | tcpConn.opts.SetOption(n, v) 46 | } 47 | } 48 | if _, err := tcpConn.opts.GetOption(OptionMaxRW); err != nil { 49 | tcpConn.opts.SetOption(OptionMaxRW, 4096) 50 | } 51 | 52 | go func() { 53 | for m := range tcpConn.writeMsg { 54 | if m == nil { 55 | break 56 | } 57 | err := tcpConn.doWriteMsgWithID(m) 58 | if err != nil { 59 | break 60 | } 61 | } 62 | 63 | tcpConn.Close() 64 | 65 | }() 66 | 67 | return tcpConn 68 | } 69 | 70 | func newSessionTCPConn(c net.Conn, opts Options) *TCPConn { 71 | tcpConn := new(TCPConn) 72 | tcpConn.c = c 73 | tcpConn.writeMsg = make(chan *message.Message, 100) 74 | tcpConn.opts = make(Options) 75 | // tcpConn.msgParser = msgParser 76 | for n, v := range opts { 77 | switch n { 78 | case OptionMinMsgLen, 79 | OptionMaxMsgLen, 80 | OptionMsgNum, 81 | OptionLittleEndian: 82 | tcpConn.opts.SetOption(n, v) 83 | } 84 | } 85 | if _, err := tcpConn.opts.GetOption(OptionMaxRW); err != nil { 86 | tcpConn.opts.SetOption(OptionMaxRW, 4096) 87 | } 88 | 89 | go func() { 90 | for m := range tcpConn.writeMsg { 91 | if m == nil { 92 | break 93 | } 94 | err := tcpConn.doWriteMsg(m) 95 | if err != nil { 96 | break 97 | } 98 | } 99 | 100 | tcpConn.Close() 101 | 102 | }() 103 | 104 | return tcpConn 105 | } 106 | 107 | func (c *TCPConn) RecvMsg() (*message.Message, error) { 108 | var sz uint32 109 | var err error 110 | var msg *message.Message 111 | 112 | isLittleEndian, err := c.opts.GetOption(OptionLittleEndian) 113 | 114 | if err == nil && isLittleEndian.(bool) { 115 | if err = binary.Read(c.c, binary.LittleEndian, &sz); err != nil { 116 | return nil, err 117 | } 118 | } else { 119 | if err = binary.Read(c.c, binary.BigEndian, &sz); err != nil { 120 | return nil, err 121 | } 122 | } 123 | maxRW, err := c.opts.GetOption(OptionMaxRW) 124 | if err == nil { 125 | if sz < 0 || (maxRW.(uint32) > 0 && sz > maxRW.(uint32)) { 126 | return nil, ErrTooLong 127 | } 128 | } 129 | msg = message.NewMessage(int(sz)) 130 | msg.Body = msg.Body[0:sz] 131 | if _, err = io.ReadFull(c.c, msg.Body); err != nil { 132 | msg.Free() 133 | return nil, err 134 | } 135 | return msg, nil 136 | } 137 | 138 | func (c *TCPConn) RecvMsgWithID() (*message.Message, error) { 139 | var sz uint32 140 | var connID uint32 141 | var err error 142 | var msg *message.Message 143 | 144 | isLittleEndian, err := c.opts.GetOption(OptionLittleEndian) 145 | 146 | if err == nil && isLittleEndian.(bool) { 147 | if err = binary.Read(c.c, binary.LittleEndian, &sz); err != nil { 148 | return nil, err 149 | } 150 | if err = binary.Read(c.c, binary.LittleEndian, &connID); err != nil { 151 | return nil, err 152 | } 153 | } else { 154 | if err = binary.Read(c.c, binary.BigEndian, &sz); err != nil { 155 | return nil, err 156 | } 157 | if err = binary.Read(c.c, binary.BigEndian, &connID); err != nil { 158 | return nil, err 159 | } 160 | } 161 | maxRW, err := c.opts.GetOption(OptionMaxRW) 162 | if err == nil { 163 | if sz < 0 || (maxRW.(uint32) > 0 && sz > maxRW.(uint32)) { 164 | return nil, ErrTooLong 165 | } 166 | } 167 | msg = message.NewMessage(int(sz)) 168 | msg.ConnID = connID 169 | msg.Body = msg.Body[0 : sz-4] 170 | if _, err = io.ReadFull(c.c, msg.Body); err != nil { 171 | msg.Free() 172 | return nil, err 173 | } 174 | return msg, nil 175 | } 176 | 177 | func (c *TCPConn) doWriteMsg(msg *message.Message) error { 178 | sz := uint32(len(msg.Body)) 179 | m := make([]byte, sz+4) 180 | 181 | isLittleEndian, err := c.opts.GetOption(OptionLittleEndian) 182 | if err == nil && isLittleEndian.(bool) { 183 | binary.LittleEndian.PutUint32(m, sz) 184 | } else { 185 | binary.BigEndian.PutUint32(m, sz) 186 | } 187 | copy(m[4:], msg.Body) 188 | if _, err := c.c.Write(m); err != nil { 189 | return err 190 | } 191 | msg.Free() 192 | return nil 193 | } 194 | 195 | func (c *TCPConn) doWriteMsgWithID(msg *message.Message) error { 196 | sz := uint32(len(msg.Body) + 4) 197 | m := make([]byte, sz+4) 198 | isLittleEndian, err := c.opts.GetOption(OptionLittleEndian) 199 | if err == nil && isLittleEndian.(bool) { 200 | binary.LittleEndian.PutUint32(m, sz) 201 | binary.LittleEndian.PutUint32(m[4:], msg.ConnID) 202 | } else { 203 | binary.BigEndian.PutUint32(m, sz) 204 | binary.BigEndian.PutUint32(m[4:], msg.ConnID) 205 | } 206 | copy(m[8:], msg.Body) 207 | if _, err := c.c.Write(m); err != nil { 208 | return err 209 | } 210 | msg.Free() 211 | return nil 212 | } 213 | 214 | func (c *TCPConn) SendMsg(msg *message.Message) error { 215 | c.writeMsg <- msg 216 | return nil 217 | } 218 | 219 | func (c *TCPConn) Send(b []byte) error { 220 | msg := message.NewMessage(len(b)) 221 | msg.Body = append(msg.Body, b...) 222 | return c.SendMsg(msg) 223 | } 224 | 225 | func (c *TCPConn) Recv() ([]byte, error) { 226 | msg, err := c.RecvMsg() 227 | if err != nil { 228 | return nil, err 229 | } 230 | b := make([]byte, 0, len(msg.Body)) 231 | b = append(b, msg.Body...) 232 | msg.Free() 233 | return b, nil 234 | } 235 | 236 | func (c *TCPConn) IsOpen() bool { 237 | return c.isOpen 238 | } 239 | 240 | func (c *TCPConn) Close() error { 241 | c.Lock() 242 | if c.isOpen { 243 | c.isOpen = false 244 | for { 245 | if len(c.writeMsg) == 0 { 246 | break 247 | } else { 248 | m := <-c.writeMsg 249 | m.Free() 250 | } 251 | } 252 | close(c.writeMsg) 253 | } 254 | c.Unlock() 255 | return c.c.Close() 256 | } 257 | -------------------------------------------------------------------------------- /network/tcpServer.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | "time" 7 | 8 | . "github.com/k4s/tea" 9 | "github.com/k4s/tea/log" 10 | ) 11 | 12 | type TCPServer struct { 13 | //server address:"127.0.0.1:8080" 14 | Addr string 15 | 16 | conns ConnSet 17 | //connections mutex 18 | mutexConns sync.Mutex 19 | //listen waitGroup 20 | wglisten sync.WaitGroup 21 | //writing waitGroup 22 | wgConns sync.WaitGroup 23 | 24 | listener net.Listener 25 | NewAgent func(*TCPConn, bool) Agent 26 | Agents chan Agent 27 | 28 | withID bool 29 | opts Options 30 | } 31 | 32 | func (server *TCPServer) Start() { 33 | server.init() 34 | go server.run() 35 | } 36 | 37 | func (server *TCPServer) init() { 38 | listener, err := net.Listen("tcp", server.Addr) 39 | if err != nil { 40 | log.Fatal("%v", err) 41 | } 42 | server.Agents = make(chan Agent, 100) 43 | server.opts = make(Options) 44 | for n, v := range server.opts { 45 | switch n { 46 | case OptionMinMsgLen, 47 | OptionMaxMsgLen, 48 | OptionConnNum, 49 | OptionMsgNum, 50 | OptionLittleEndian: 51 | server.opts.SetOption(n, v) 52 | } 53 | } 54 | 55 | if _, err := server.opts.GetOption(OptionConnNum); err != nil { 56 | server.opts.SetOption(OptionConnNum, 100) 57 | } 58 | 59 | if _, err := server.opts.GetOption(OptionMsgNum); err != nil { 60 | server.opts.SetOption(OptionMsgNum, 100) 61 | } 62 | 63 | if _, err := server.opts.GetOption(OptionLittleEndian); err != nil { 64 | server.opts.SetOption(OptionLittleEndian, true) 65 | } 66 | 67 | if server.NewAgent == nil { 68 | log.Fatal("NewAgent must not be nil") 69 | } 70 | server.listener = listener 71 | server.conns = make(ConnSet) 72 | 73 | } 74 | 75 | func (server *TCPServer) run() { 76 | server.wglisten.Add(1) 77 | defer server.wglisten.Done() 78 | 79 | var tempDelay time.Duration 80 | for { 81 | conn, err := server.listener.Accept() 82 | if err != nil { 83 | if ne, ok := err.(net.Error); ok && ne.Temporary() { 84 | if tempDelay == 0 { 85 | tempDelay = 5 * time.Millisecond 86 | } else { 87 | tempDelay *= 2 88 | } 89 | if max := 1 * time.Second; tempDelay > max { 90 | tempDelay = max 91 | } 92 | log.Release("accept error: %v; retrying in %v", err, tempDelay) 93 | time.Sleep(tempDelay) 94 | continue 95 | } 96 | return 97 | } 98 | tempDelay = 0 99 | 100 | server.mutexConns.Lock() 101 | connNum, _ := server.opts.GetOption(OptionConnNum) 102 | if len(server.conns) >= connNum.(int) { 103 | server.mutexConns.Unlock() 104 | conn.Close() 105 | log.Debug("too many connections") 106 | continue 107 | } 108 | 109 | //Collect all the connections 110 | server.conns[conn] = struct{}{} 111 | server.mutexConns.Unlock() 112 | 113 | server.wgConns.Add(1) 114 | 115 | var tcpConn *TCPConn 116 | if server.withID { 117 | tcpConn = newAgentTCPConn(conn, server.opts) 118 | } else { 119 | tcpConn = newSessionTCPConn(conn, server.opts) 120 | } 121 | 122 | agent := server.NewAgent(tcpConn, server.withID) 123 | 124 | go func() { 125 | server.Agents <- agent 126 | agent.Run() 127 | agent.Close() 128 | 129 | server.mutexConns.Lock() 130 | delete(server.conns, conn) 131 | server.mutexConns.Unlock() 132 | 133 | server.wgConns.Done() 134 | }() 135 | } 136 | 137 | } 138 | 139 | func (server *TCPServer) SetWithID(isbool bool) { 140 | server.withID = isbool 141 | } 142 | 143 | func (server *TCPServer) SetOpts(opts Options) { 144 | server.opts = opts 145 | } 146 | 147 | func (server *TCPServer) GetAgent() Agent { 148 | return <-server.Agents 149 | } 150 | 151 | func (server *TCPServer) Close() { 152 | server.listener.Close() 153 | server.wglisten.Wait() 154 | 155 | server.mutexConns.Lock() 156 | for conn := range server.conns { 157 | conn.Close() 158 | } 159 | server.conns = nil 160 | server.mutexConns.Unlock() 161 | server.wgConns.Wait() 162 | } 163 | -------------------------------------------------------------------------------- /network/wsClient.go: -------------------------------------------------------------------------------- 1 | package network 2 | -------------------------------------------------------------------------------- /network/wsConn.go: -------------------------------------------------------------------------------- 1 | package network 2 | -------------------------------------------------------------------------------- /network/wsServer.go: -------------------------------------------------------------------------------- 1 | package network 2 | -------------------------------------------------------------------------------- /newTea/newTea.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "fmt" 10 | 11 | "github.com/k4s/tea/log" 12 | "github.com/k4s/tea/newTea/tpl" 13 | ) 14 | 15 | var appname string 16 | var crupath, _ = os.Getwd() 17 | 18 | func main() { 19 | if len(os.Args) != 3 { 20 | log.Debug("newTea gate appname Or newTea gameserver appname") 21 | return 22 | } 23 | switch os.Args[1] { 24 | case "gate": 25 | initVar(os.Args[2]) 26 | newGate() 27 | case "gameserver": 28 | initVar(os.Args[2]) 29 | appname = os.Args[2] 30 | newGameserver() 31 | } 32 | } 33 | 34 | func newGate() { 35 | log.Debug("[tea] Create a tea Gateway named `%s` in the `%s` path.", appname, crupath) 36 | if isExist(crupath) { 37 | log.Debug("[tea] The project path has conflic, do you want to build in: %s\n", crupath) 38 | log.Debug("[tea] Do you want to overwrite it? [(yes|no) or (y|n)] ") 39 | if !askForConfirmation() { 40 | log.Fatal("[tea] New Gateway Cancel...") 41 | return 42 | } 43 | } 44 | log.Debug("[tea] Start create Gateway...") 45 | 46 | //生成配置文件 47 | makedir("config") 48 | writefile(crupath+"/config/config.go", replaceAppname(tpl.GateConfigStr)) 49 | writefile(crupath+"/config/config.json", replaceAppname(tpl.GateConfigJson)) 50 | 51 | //生成main文件 52 | writefile(crupath+"/main.go", replaceAppname(tpl.GateMainStr)) 53 | 54 | log.Debug("[tea] Create gate successful") 55 | 56 | if err := os.Chdir(crupath); err != nil { 57 | log.Fatal("[tea] Create gateway fail: %v", err) 58 | } 59 | 60 | } 61 | 62 | func newGameserver() { 63 | log.Debug("[tea] Create a tea Gameserver named `%s` in the `%s` path.", appname, crupath) 64 | if isExist(crupath) { 65 | log.Debug("[tea] The project path has conflic, do you want to build in: %s\n", crupath) 66 | log.Debug("[tea] Do you want to overwrite it? [(yes|no) or (y|n)] ") 67 | if !askForConfirmation() { 68 | log.Fatal("[tea] New Gameserver Cancel...") 69 | return 70 | } 71 | } 72 | log.Debug("[tea] Start create Gameserver...") 73 | 74 | //生成配置文件 75 | makedir("config") 76 | writefile(crupath+"/config/config.go", replaceAppname(tpl.GameConfigStr)) 77 | writefile(crupath+"/config/config.json", replaceAppname(tpl.GameConfigJson)) 78 | 79 | //生成游戏逻辑文件 80 | makedir("handle") 81 | writefile(crupath+"/handle/handle.go", replaceAppname(tpl.HandleStr)) 82 | 83 | //生成日志文件 84 | makedir("log/logdata") 85 | writefile(crupath+"/log/log.go", replaceAppname(tpl.LogStr)) 86 | 87 | //生成消息文件 88 | makedir("msg") 89 | writefile(crupath+"/msg/msg.go", replaceAppname(tpl.MsgStr)) 90 | 91 | //生成协议文件 92 | makedir("protocol") 93 | writefile(crupath+"/protocol/process.go", replaceAppname(tpl.ProtocolStr)) 94 | 95 | //生成注册文件 96 | makedir("register") 97 | writefile(crupath+"/register/register.go", replaceAppname(tpl.RegisterStr)) 98 | 99 | //生成路由文件 100 | makedir("router") 101 | writefile(crupath+"/router/router.go", replaceAppname(tpl.RouterStr)) 102 | 103 | //生成main文件 104 | writefile(crupath+"/main.go", replaceAppname(tpl.GameMainStr)) 105 | 106 | log.Debug("[tea] Create gameserver successful") 107 | 108 | if err := os.Chdir(crupath); err != nil { 109 | log.Fatal("[tea] Create gameserver fail: %v", err) 110 | } 111 | 112 | } 113 | 114 | func initVar(args string) { 115 | var dir string 116 | dir, appname = filepath.Split(args) 117 | if dir != "" { 118 | crupath = filepath.Join(dir, appname) 119 | } else { 120 | crupath = filepath.Join(crupath, appname) 121 | } 122 | 123 | var err error 124 | crupath = strings.TrimSpace(crupath) 125 | crupath, err = filepath.Abs(crupath) 126 | if err != nil { 127 | log.Fatal("[tea] Create project fail: %s", err) 128 | } 129 | crupath = strings.Replace(crupath, `\`, `/`, -1) 130 | crupath = strings.TrimRight(crupath, "/") + "/" 131 | } 132 | 133 | /** 134 | * 判断文件是否存在 存在返回 true 不存在返回false 135 | */ 136 | func checkFileIsExist(filename string) bool { 137 | var exist = true 138 | if _, err := os.Stat(filename); os.IsNotExist(err) { 139 | exist = false 140 | } 141 | return exist 142 | } 143 | 144 | /** 145 | * 生成目录 146 | */ 147 | func makedir(dirname string) { 148 | err := os.MkdirAll(crupath+dirname, os.ModePerm) 149 | if err != nil { 150 | log.Fatal("[tea] create has a error :%s", err) 151 | return 152 | } 153 | } 154 | 155 | /** 156 | * 写文件 157 | */ 158 | func writefile(filename string, writeStr string) { 159 | var f *os.File 160 | var err error 161 | 162 | if isExist(filename) { //如果文件存在 163 | f, err = os.OpenFile(filename, os.O_RDWR, 0666) //打开文件 164 | if err != nil { 165 | log.Fatal("[tea] create has a error :%s", err) 166 | return 167 | } 168 | } else { 169 | f, err = os.Create(filename) //创建文件 170 | if err != nil { 171 | log.Fatal("[tea] create has a error :%s", err) 172 | return 173 | } 174 | } 175 | _, err = io.WriteString(f, writeStr) //写入文件(字符串) 176 | if err != nil { 177 | log.Fatal("[tea] create has a error :%s", err) 178 | return 179 | } 180 | } 181 | 182 | /** 183 | *判断当前路径是否存在 184 | */ 185 | func isExist(path string) bool { 186 | _, err := os.Stat(path) 187 | return err == nil || os.IsExist(err) 188 | } 189 | 190 | /** 191 | *替换appname 192 | */ 193 | func replaceAppname(inStr string) string { 194 | return strings.Replace(inStr, "<>", appname, -1) 195 | } 196 | 197 | // askForConfirmation uses Scanln to parse user input. A user must type in "yes" or "no" and 198 | // then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as 199 | // confirmations. If the input is not recognized, it will ask again. The function does not return 200 | // until it gets a valid response from the user. Typically, you should use fmt to print out a question 201 | // before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)") 202 | func askForConfirmation() bool { 203 | var response string 204 | _, err := fmt.Scanln(&response) 205 | if err != nil { 206 | log.Fatal(err.Error()) 207 | } 208 | okayResponses := []string{"y", "Y", "yes", "Yes", "YES"} 209 | nokayResponses := []string{"n", "N", "no", "No", "NO"} 210 | if containsString(okayResponses, response) { 211 | return true 212 | } else if containsString(nokayResponses, response) { 213 | return false 214 | } else { 215 | fmt.Println("Please type yes or no and then press enter:") 216 | return askForConfirmation() 217 | } 218 | } 219 | 220 | func containsString(slice []string, element string) bool { 221 | for _, elem := range slice { 222 | if elem == element { 223 | return true 224 | } 225 | } 226 | return false 227 | } 228 | -------------------------------------------------------------------------------- /newTea/tpl/config.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var GateConfigJson string = `{ 4 | "Protocal": "json", 5 | "MaxConnNum": 100, 6 | "MaxRW": 2000, 7 | "MaxMsgLen": 8192, 8 | "ClientAddr": "127.0.0.1:4444", 9 | "GameServeraddr": "127.0.0.1:5555", 10 | "HTTPTimeout": 10, 11 | "LittleEndian": true 12 | } 13 | ` 14 | 15 | var GateConfigStr string = `package config 16 | 17 | import ( 18 | "path/filepath" 19 | 20 | "github.com/k4s/tea" 21 | ) 22 | 23 | func init() { 24 | configFilePath, _ := filepath.Abs("./config/config.json") 25 | err := tea.InitConfigFile(configFilePath, &Appconfig) 26 | if err != nil { 27 | panic(err) 28 | } 29 | } 30 | 31 | //Appconfig 应用配置 32 | var Appconfig struct { 33 | Protocol string ` + "`" + `json:"Protocal"` + "`" + ` 34 | MaxRW int ` + "`" + `json:"MaxRW"` + "`" + ` 35 | MaxConnNum int ` + "`" + `json:"MaxConnNum"` + "`" + ` 36 | MaxMsgLen int ` + "`" + `json:"MaxMsgLen"` + "`" + ` 37 | ClientAddr string ` + "`" + `json:"ClientAddr"` + "`" + ` 38 | GameServeraddr string ` + "`" + `json:"GameServeraddr"` + "`" + ` 39 | HTTPTimeout int ` + "`" + `json:"HTTPTimeout"` + "`" + ` 40 | LittleEndian bool ` + "`" + `json:"LittleEndian"` + "`" + ` 41 | } 42 | 43 | func GetOpts() tea.Options { 44 | var opts = make(tea.Options) 45 | opts.SetOption(tea.OptionConnNum, Appconfig.MaxConnNum) 46 | opts.SetOption(tea.OptionMaxRW, Appconfig.MaxRW) 47 | opts.SetOption(tea.OptionMaxMsgLen, Appconfig.MaxMsgLen) 48 | opts.SetOption(tea.OptionLittleEndian, Appconfig.LittleEndian) 49 | return opts 50 | } 51 | 52 | ` 53 | var GameConfigJson string = `{ 54 | "Protocal": "json", 55 | "MaxConnNum": 100, 56 | "MaxRW": 2000, 57 | "MaxMsgLen": 8192, 58 | "GateTCPAddr": [ 59 | "127.0.0.1:5555" 60 | ], 61 | "GateWSAddr": [ 62 | "127.0.0.1:5555" 63 | ], 64 | "HTTPTimeout": 10, 65 | "LittleEndian": true, 66 | "MysqlAddr": "root:@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=true" 67 | } 68 | ` 69 | 70 | var GameConfigStr string = `package config 71 | 72 | import ( 73 | "path/filepath" 74 | 75 | "github.com/k4s/tea" 76 | ) 77 | 78 | func init() { 79 | configFilePath, _ := filepath.Abs("./config/config.json") 80 | err := tea.InitConfigFile(configFilePath, &Appconfig) 81 | if err != nil { 82 | panic(err) 83 | } 84 | } 85 | 86 | //Appconfig 应用配置 87 | var Appconfig struct { 88 | Protocol string ` + "`" + `json:"Protocal"` + "`" + ` 89 | MaxRW int ` + "`" + `json:"MaxRW"` + "`" + ` 90 | MaxConnNum int ` + "`" + `json:"MaxConnNum"` + "`" + ` 91 | MaxMsgLen int ` + "`" + `json:"MaxMsgLen"` + "`" + ` 92 | GateTCPAddr []string ` + "`" + `json:"GateTCPAddr"` + "`" + ` 93 | GateWSAddr []string ` + "`" + `json:"GateWSAddr"` + "`" + ` 94 | HTTPTimeout int ` + "`" + `json:"HTTPTimeout"` + "`" + ` 95 | LittleEndian bool ` + "`" + `json:"LittleEndian"` + "`" + ` 96 | MysqlAddr string ` + "`" + `json:"MysqlAddr"` + "`" + ` 97 | } 98 | 99 | func GetOpts() tea.Options { 100 | var opts = make(tea.Options) 101 | opts.SetOption(tea.OptionConnNum, Appconfig.MaxConnNum) 102 | opts.SetOption(tea.OptionMaxRW, Appconfig.MaxRW) 103 | opts.SetOption(tea.OptionMaxMsgLen, Appconfig.MaxMsgLen) 104 | opts.SetOption(tea.OptionLittleEndian, Appconfig.LittleEndian) 105 | return opts 106 | } 107 | ` 108 | -------------------------------------------------------------------------------- /newTea/tpl/game.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var GameMainStr string = `package main 4 | 5 | import ( 6 | "<>/config" 7 | "<>/protocol" 8 | . "<>/register" 9 | . "<>/router" 10 | 11 | "github.com/k4s/tea/gameserver" 12 | ) 13 | 14 | func init() { 15 | InitRegister() 16 | InitRouter() 17 | } 18 | 19 | func main() { 20 | games := make([]*gameserver.Gameserver, 0) 21 | for _, addr := range config.Appconfig.GateTCPAddr { 22 | game := gameserver.NewGameserver(addr, protocol.Processor) 23 | games = append(games, game) 24 | } 25 | gameserver.GameRun(games) 26 | }` 27 | -------------------------------------------------------------------------------- /newTea/tpl/gate.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var GateMainStr string = `package main 4 | 5 | import ( 6 | "<>/config" 7 | 8 | "github.com/k4s/tea/gate" 9 | ) 10 | 11 | func main() { 12 | gate := gate.NewGate(config.Appconfig.ClientAddr, config.Appconfig.GameServeraddr) 13 | gate.SetOpts(config.GetOpts()) 14 | gate.Run() 15 | } 16 | 17 | ` 18 | -------------------------------------------------------------------------------- /newTea/tpl/handle.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var HandleStr string = `package handle 4 | 5 | import ( 6 | "fmt" 7 | ms "<>/msg" 8 | "<>/protocol" 9 | 10 | "github.com/k4s/tea/message" 11 | "github.com/k4s/tea/network" 12 | ) 13 | 14 | func InfoHandle(msg *message.Message, agent network.Agent) { 15 | jsonMsg, err := protocol.Processor.Unmarshal(msg.Body) 16 | if err != nil { 17 | fmt.Println(err) 18 | } 19 | m := jsonMsg.(*ms.Hello) 20 | fmt.Println("game:", m) 21 | reMsg := ms.Hello{ 22 | Name: "kkk", 23 | } 24 | agent.EndHandle(msg, reMsg) 25 | }` 26 | -------------------------------------------------------------------------------- /newTea/tpl/log.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var LogStr string = `package log 4 | 5 | import ( 6 | "github.com/k4s/tea/log" 7 | "github.com/robfig/cron" 8 | ) 9 | 10 | func init() { 11 | c := cron.New() 12 | c.AddFunc("0 0 0 * * ?", func() { 13 | var err error 14 | Logger, err = log.New("debug", "./log/logdata/", 0) 15 | if err != nil { 16 | log.Debug("%v", err) 17 | } 18 | }) 19 | c.Start() 20 | } 21 | 22 | var Logger, _ = log.New("debug", "./log/logdata/", 0) 23 | 24 | var gLogger, _ = log.New("debug", "", 0) 25 | 26 | func Debug(format string, a ...interface{}) { 27 | gLogger.Debug(format, a...) 28 | } 29 | 30 | func Release(format string, a ...interface{}) { 31 | gLogger.Release(format, a...) 32 | } 33 | 34 | func Error(format string, a ...interface{}) { 35 | gLogger.Error(format, a...) 36 | } 37 | 38 | func Fatal(format string, a ...interface{}) { 39 | gLogger.Fatal(format, a...) 40 | } 41 | 42 | func Close() { 43 | gLogger.Close() 44 | } 45 | 46 | ` 47 | -------------------------------------------------------------------------------- /newTea/tpl/msg.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var MsgStr string = `package msg 4 | 5 | //测试 6 | type Hello struct { 7 | Name string 8 | } 9 | ` 10 | -------------------------------------------------------------------------------- /newTea/tpl/protocol.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var ProtocolStr string = `package protocol 4 | 5 | import ( 6 | "log" 7 | 8 | "<>/config" 9 | 10 | "github.com/k4s/tea/protocol" 11 | ) 12 | 13 | var Processor protocol.Processor 14 | 15 | func init() { 16 | switch config.Appconfig.Protocol { 17 | case "json": 18 | Processor = protocol.NewJson() 19 | case "protobuf": 20 | Processor = protocol.NewProto() 21 | default: 22 | log.Fatal("unknown Protocol: %v", config.Appconfig.Protocol) 23 | } 24 | 25 | } 26 | 27 | ` 28 | -------------------------------------------------------------------------------- /newTea/tpl/register.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var RegisterStr string = `package Register 4 | 5 | import ( 6 | "<>/msg" 7 | "<>/protocol" 8 | ) 9 | 10 | func InitRegister() { 11 | protocol.Processor.Register(&msg.Hello{}) 12 | }` 13 | -------------------------------------------------------------------------------- /newTea/tpl/router.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | var RouterStr string = `package router 4 | 5 | import ( 6 | "<>/handle" 7 | "<>/msg" 8 | "<>/protocol" 9 | ) 10 | 11 | func InitRouter() { 12 | protocol.Processor.SetHandler(&msg.Hello{}, handle.InfoHandle) 13 | } 14 | ` 15 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package tea 2 | 3 | type Options map[string]interface{} 4 | 5 | const ( 6 | 7 | //OptionKeepAlive用于设置TCP KeepAlive 8 | //Value是一个布尔值,默认是true 9 | OptionKeepAlive = "KeepAlive" 10 | 11 | //OptionNoDelay 没有延迟 12 | //Value是一个布尔值,默认是true 13 | OptionNoDelay = "No-Delay" 14 | 15 | //连接间隔 16 | OptionConnInterval = "Conn-Interval" 17 | 18 | //最大连接数 19 | OptionConnNum = "Conn-Number" 20 | 21 | //消息最大缓存数 22 | OptionMsgNum = "Msg-Number" 23 | 24 | //消息长度 25 | // OptioneMsgLen = "Msg-lenght" 26 | 27 | //最小消息长度 28 | OptionMinMsgLen = "Msg-Min-lenght" 29 | 30 | //最大消息长度 31 | OptionMaxMsgLen = "Msg-Max-lenght" 32 | 33 | //最大读写 34 | OptionMaxRW = "Max-ReadWrite" 35 | //小端 36 | OptionLittleEndian = "LittleEndian" 37 | ) 38 | 39 | func (opts Options) SetOption(n string, v interface{}) error { 40 | switch n { 41 | case OptionNoDelay: 42 | fallthrough 43 | case OptionKeepAlive, OptionLittleEndian: 44 | switch v := v.(type) { 45 | case bool: 46 | opts[n] = v 47 | return nil 48 | default: 49 | return ErrBadValue 50 | } 51 | case OptionMinMsgLen, OptionMaxMsgLen: 52 | switch v := v.(type) { 53 | case uint32: 54 | opts[n] = v 55 | return nil 56 | default: 57 | return ErrBadValue 58 | } 59 | case OptionConnNum, OptionConnInterval, OptionMsgNum: 60 | switch v := v.(type) { 61 | case int: 62 | opts[n] = v 63 | return nil 64 | default: 65 | return ErrBadValue 66 | } 67 | } 68 | return ErrBadValue 69 | } 70 | 71 | func (opts Options) GetOption(n string) (interface{}, error) { 72 | v, ok := opts[n] 73 | if ok { 74 | return v, nil 75 | } 76 | return nil, ErrBadValue 77 | } 78 | -------------------------------------------------------------------------------- /protocol/common.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/k4s/tea/message" 7 | "github.com/k4s/tea/network" 8 | ) 9 | 10 | type MsgInfo struct { 11 | msgType reflect.Type 12 | msgHandler MsgHandler 13 | } 14 | 15 | type MsgHandler func(*message.Message, network.Agent) 16 | -------------------------------------------------------------------------------- /protocol/json.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "reflect" 8 | 9 | "github.com/k4s/tea/log" 10 | "github.com/k4s/tea/message" 11 | "github.com/k4s/tea/network" 12 | ) 13 | 14 | // -------------------- 15 | // len | json message | 16 | // -------------------- 17 | type JsonProcessor struct { 18 | msgInfo map[string]*MsgInfo 19 | } 20 | 21 | func NewJson() *JsonProcessor { 22 | p := new(JsonProcessor) 23 | p.msgInfo = make(map[string]*MsgInfo) 24 | return p 25 | } 26 | 27 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 28 | func (p *JsonProcessor) Register(msg interface{}) { 29 | msgType := reflect.TypeOf(msg) 30 | if msgType == nil || msgType.Kind() != reflect.Ptr { 31 | log.Fatal("json message pointer required") 32 | } 33 | msgID := msgType.Elem().Name() 34 | 35 | if msgID == "" { 36 | log.Fatal("unnamed json message") 37 | } 38 | if _, ok := p.msgInfo[msgID]; ok { 39 | log.Fatal("message %v is already registered", msgID) 40 | } 41 | 42 | i := new(MsgInfo) 43 | i.msgType = msgType 44 | p.msgInfo[msgID] = i 45 | } 46 | 47 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 48 | func (p *JsonProcessor) SetHandler(msg interface{}, msgHandler MsgHandler) { 49 | msgType := reflect.TypeOf(msg) 50 | if msgType == nil || msgType.Kind() != reflect.Ptr { 51 | log.Fatal("json message pointer required") 52 | } 53 | 54 | msgID := msgType.Elem().Name() 55 | i, ok := p.msgInfo[msgID] 56 | if !ok { 57 | log.Fatal("message %v not registered", msgID) 58 | } 59 | 60 | i.msgHandler = msgHandler 61 | } 62 | 63 | // goroutine safe 64 | func (p *JsonProcessor) Route(msg *message.Message, agent network.Agent) error { 65 | m, err := p.Unmarshal(msg.Body) 66 | if err != nil { 67 | return fmt.Errorf("json message Unmarshal error: %v", err) 68 | } 69 | msgType := reflect.TypeOf(m) 70 | if msgType == nil || msgType.Kind() != reflect.Ptr { 71 | return errors.New("json message pointer required") 72 | } 73 | msgID := msgType.Elem().Name() 74 | i, ok := p.msgInfo[msgID] 75 | if !ok { 76 | return fmt.Errorf("message %v not registered", msgID) 77 | } 78 | 79 | if i.msgHandler != nil { 80 | i.msgHandler(msg, agent) 81 | } 82 | return nil 83 | } 84 | 85 | // goroutine safe 86 | func (p *JsonProcessor) Unmarshal(data []byte) (interface{}, error) { 87 | fmt.Println("Unmarshal:", string(data)) 88 | var m map[string]json.RawMessage 89 | err := json.Unmarshal(data, &m) 90 | if err != nil { 91 | return nil, err 92 | } 93 | if len(m) != 1 { 94 | return nil, errors.New("invalid json data") 95 | } 96 | 97 | for msgID, data := range m { 98 | i, ok := p.msgInfo[msgID] 99 | if !ok { 100 | return nil, fmt.Errorf("message %v not registered", msgID) 101 | } 102 | 103 | // msg 104 | msg := reflect.New(i.msgType.Elem()).Interface() 105 | return msg, json.Unmarshal(data, msg) 106 | } 107 | 108 | panic("bug") 109 | } 110 | 111 | // goroutine safe 112 | func (p *JsonProcessor) Marshal(msg interface{}) ([][]byte, error) { 113 | msgType := reflect.TypeOf(msg) 114 | if msgType == nil || msgType.Kind() != reflect.Ptr { 115 | return nil, errors.New("json message pointer required") 116 | } 117 | msgID := msgType.Elem().Name() 118 | if _, ok := p.msgInfo[msgID]; !ok { 119 | return nil, fmt.Errorf("message %v not registered", msgID) 120 | } 121 | 122 | // data 123 | m := map[string]interface{}{msgID: msg} 124 | data, err := json.Marshal(m) 125 | return [][]byte{data}, err 126 | } 127 | -------------------------------------------------------------------------------- /protocol/processor.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "github.com/k4s/tea/message" 5 | "github.com/k4s/tea/network" 6 | ) 7 | 8 | type Processor interface { 9 | // must goroutine safe 10 | Route(msg *message.Message, agent network.Agent) error 11 | //must goroutine safe 12 | SetHandler(msg interface{}, msgHandler MsgHandler) 13 | //must goroutine safe 14 | Register(msg interface{}) 15 | // must goroutine safe 16 | Unmarshal(data []byte) (interface{}, error) 17 | // must goroutine safe 18 | Marshal(msg interface{}) ([][]byte, error) 19 | } 20 | -------------------------------------------------------------------------------- /protocol/protobuf.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "math" 8 | "reflect" 9 | 10 | "github.com/golang/protobuf/proto" 11 | "github.com/k4s/tea/log" 12 | "github.com/k4s/tea/message" 13 | "github.com/k4s/tea/network" 14 | ) 15 | 16 | // ----------------------------- 17 | // len | id | protobuf message | 18 | // ----------------------------- 19 | type ProtoProcessor struct { 20 | littleEndian bool 21 | msgInfo []*MsgInfo 22 | msgID map[reflect.Type]uint16 23 | } 24 | 25 | func NewProto() *ProtoProcessor { 26 | p := new(ProtoProcessor) 27 | p.littleEndian = false 28 | p.msgID = make(map[reflect.Type]uint16) 29 | return p 30 | } 31 | 32 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 33 | func (p *ProtoProcessor) SetByteOrder(littleEndian bool) { 34 | p.littleEndian = littleEndian 35 | } 36 | 37 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 38 | func (p *ProtoProcessor) Register(msg interface{}) { 39 | msgType := reflect.TypeOf(msg) 40 | if msgType == nil || msgType.Kind() != reflect.Ptr { 41 | log.Fatal("protobuf message pointer required") 42 | } 43 | if _, ok := p.msgID[msgType]; ok { 44 | log.Fatal("message %s is already registered", msgType) 45 | } 46 | if len(p.msgInfo) >= math.MaxUint16 { 47 | log.Fatal("too many protobuf messages (max = %v)", math.MaxUint16) 48 | } 49 | 50 | i := new(MsgInfo) 51 | i.msgType = msgType 52 | p.msgInfo = append(p.msgInfo, i) 53 | p.msgID[msgType] = uint16(len(p.msgInfo) - 1) 54 | } 55 | 56 | // It's dangerous to call the method on routing or marshaling (unmarshaling) 57 | func (p *ProtoProcessor) SetHandler(msg interface{}, msgHandler MsgHandler) { 58 | msgType := reflect.TypeOf(msg) 59 | 60 | id, ok := p.msgID[msgType] 61 | if !ok { 62 | log.Fatal("message %s not registered", msgType) 63 | } 64 | 65 | p.msgInfo[id].msgHandler = msgHandler 66 | } 67 | 68 | // goroutine safe 69 | func (p *ProtoProcessor) Route(msg *message.Message, agent network.Agent) error { 70 | m, err := p.Unmarshal(msg.Body) 71 | if err != nil { 72 | return fmt.Errorf("json message Unmarshal error: %v", err) 73 | } 74 | msgType := reflect.TypeOf(m) 75 | id, ok := p.msgID[msgType] 76 | if !ok { 77 | return fmt.Errorf("message %s not registered", msgType) 78 | } 79 | 80 | i := p.msgInfo[id] 81 | if i.msgHandler != nil { 82 | i.msgHandler(msg, agent) 83 | } 84 | 85 | return nil 86 | } 87 | 88 | // goroutine safe 89 | func (p *ProtoProcessor) Unmarshal(data []byte) (interface{}, error) { 90 | fmt.Println(string(data)) 91 | if len(data) < 2 { 92 | return nil, errors.New("protobuf data too short") 93 | } 94 | 95 | // id 96 | var id uint16 97 | if p.littleEndian { 98 | id = binary.LittleEndian.Uint16(data) 99 | } else { 100 | id = binary.BigEndian.Uint16(data) 101 | } 102 | 103 | // msg 104 | if id >= uint16(len(p.msgInfo)) { 105 | return nil, fmt.Errorf("message id %v not registered", id) 106 | } 107 | msg := reflect.New(p.msgInfo[id].msgType.Elem()).Interface() 108 | return msg, proto.UnmarshalMerge(data[2:], msg.(proto.Message)) 109 | } 110 | 111 | // goroutine safe 112 | func (p *ProtoProcessor) Marshal(msg interface{}) ([][]byte, error) { 113 | msgType := reflect.TypeOf(msg) 114 | 115 | // id 116 | _id, ok := p.msgID[msgType] 117 | if !ok { 118 | err := fmt.Errorf("message %s not registered", msgType) 119 | return nil, err 120 | } 121 | 122 | id := make([]byte, 2) 123 | if p.littleEndian { 124 | binary.LittleEndian.PutUint16(id, _id) 125 | } else { 126 | binary.BigEndian.PutUint16(id, _id) 127 | } 128 | 129 | // data 130 | data, err := proto.Marshal(msg.(proto.Message)) 131 | return [][]byte{id, data}, err 132 | } 133 | 134 | // goroutine safe 135 | func (p *ProtoProcessor) Range(f func(id uint16, t reflect.Type)) { 136 | for id, i := range p.msgInfo { 137 | f(uint16(id), i.msgType) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /recordfile/example_test.go: -------------------------------------------------------------------------------- 1 | package recordfile_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/name5566/leaf/recordfile" 6 | ) 7 | 8 | func Example() { 9 | type Record struct { 10 | // index 0 11 | IndexInt int "index" 12 | // index 1 13 | IndexStr string "index" 14 | _Number int32 15 | Str string 16 | Arr1 [2]int 17 | Arr2 [3][2]int 18 | Arr3 []int 19 | St struct { 20 | Name string "name" 21 | Num int "num" 22 | } 23 | } 24 | 25 | rf, err := recordfile.New(Record{}) 26 | if err != nil { 27 | return 28 | } 29 | 30 | err = rf.Read("test.txt") 31 | if err != nil { 32 | return 33 | } 34 | 35 | for i := 0; i < rf.NumRecord(); i++ { 36 | r := rf.Record(i).(*Record) 37 | fmt.Println(r.IndexInt) 38 | } 39 | 40 | r := rf.Index(2).(*Record) 41 | fmt.Println(r.Str) 42 | 43 | r = rf.Indexes(0)[2].(*Record) 44 | fmt.Println(r.Str) 45 | 46 | r = rf.Indexes(1)["three"].(*Record) 47 | fmt.Println(r.Str) 48 | fmt.Println(r.Arr1[1]) 49 | fmt.Println(r.Arr2[2][0]) 50 | fmt.Println(r.Arr3[0]) 51 | fmt.Println(r.St.Name) 52 | 53 | // Output: 54 | // 1 55 | // 2 56 | // 3 57 | // cat 58 | // cat 59 | // book 60 | // 6 61 | // 4 62 | // 6 63 | // name5566 64 | } 65 | -------------------------------------------------------------------------------- /recordfile/recordfile.go: -------------------------------------------------------------------------------- 1 | package recordfile 2 | 3 | import ( 4 | "encoding/csv" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "os" 9 | "reflect" 10 | "strconv" 11 | ) 12 | 13 | // var Comma = '\t' 14 | 15 | var Comma = ',' 16 | var Comment = '#' 17 | 18 | type Index map[interface{}]interface{} 19 | 20 | type RecordFile struct { 21 | Comma rune 22 | Comment rune 23 | typeRecord reflect.Type 24 | records []interface{} 25 | indexes []Index 26 | } 27 | 28 | func New(st interface{}) (*RecordFile, error) { 29 | typeRecord := reflect.TypeOf(st) 30 | if typeRecord == nil || typeRecord.Kind() != reflect.Struct { 31 | return nil, errors.New("st must be a struct") 32 | } 33 | 34 | for i := 0; i < typeRecord.NumField(); i++ { 35 | f := typeRecord.Field(i) 36 | 37 | kind := f.Type.Kind() 38 | switch kind { 39 | case reflect.Bool: 40 | case reflect.Int: 41 | case reflect.Int8: 42 | case reflect.Int16: 43 | case reflect.Int32: 44 | case reflect.Int64: 45 | case reflect.Uint: 46 | case reflect.Uint8: 47 | case reflect.Uint16: 48 | case reflect.Uint32: 49 | case reflect.Uint64: 50 | case reflect.Float32: 51 | case reflect.Float64: 52 | case reflect.String: 53 | case reflect.Struct: 54 | case reflect.Array: 55 | case reflect.Slice: 56 | default: 57 | return nil, fmt.Errorf("invalid type: %v %s", 58 | f.Name, kind) 59 | } 60 | 61 | tag := f.Tag 62 | if tag == "index" { 63 | switch kind { 64 | case reflect.Struct, reflect.Array, reflect.Slice: 65 | return nil, fmt.Errorf("could not index %s field %v %v", 66 | kind, i, f.Name) 67 | } 68 | } 69 | } 70 | 71 | rf := new(RecordFile) 72 | rf.typeRecord = typeRecord 73 | 74 | return rf, nil 75 | } 76 | 77 | func (rf *RecordFile) Read(name string) error { 78 | file, err := os.Open(name) 79 | if err != nil { 80 | return err 81 | } 82 | defer file.Close() 83 | 84 | if rf.Comma == 0 { 85 | rf.Comma = Comma 86 | } 87 | if rf.Comment == 0 { 88 | rf.Comment = Comment 89 | } 90 | reader := csv.NewReader(file) 91 | reader.Comma = rf.Comma 92 | reader.Comment = rf.Comment 93 | lines, err := reader.ReadAll() 94 | if err != nil { 95 | return err 96 | } 97 | 98 | typeRecord := rf.typeRecord 99 | 100 | // make records 101 | records := make([]interface{}, len(lines)-1) 102 | 103 | // make indexes 104 | indexes := []Index{} 105 | for i := 0; i < typeRecord.NumField(); i++ { 106 | tag := typeRecord.Field(i).Tag 107 | if tag == "index" { 108 | indexes = append(indexes, make(Index)) 109 | } 110 | } 111 | 112 | for n := 1; n < len(lines); n++ { 113 | value := reflect.New(typeRecord) 114 | records[n-1] = value.Interface() 115 | record := value.Elem() 116 | 117 | line := lines[n] 118 | if len(line) != typeRecord.NumField() { 119 | return fmt.Errorf("line %v, field count mismatch: %v %v", 120 | n, len(line), typeRecord.NumField()) 121 | } 122 | 123 | iIndex := 0 124 | 125 | for i := 0; i < typeRecord.NumField(); i++ { 126 | f := typeRecord.Field(i) 127 | 128 | // records 129 | strField := line[i] 130 | field := record.Field(i) 131 | if !field.CanSet() { 132 | continue 133 | } 134 | 135 | var err error 136 | 137 | kind := f.Type.Kind() 138 | if kind == reflect.Bool { 139 | var v bool 140 | v, err = strconv.ParseBool(strField) 141 | if err == nil { 142 | field.SetBool(v) 143 | } 144 | } else if kind == reflect.Int || 145 | kind == reflect.Int8 || 146 | kind == reflect.Int16 || 147 | kind == reflect.Int32 || 148 | kind == reflect.Int64 { 149 | var v int64 150 | v, err = strconv.ParseInt(strField, 0, f.Type.Bits()) 151 | if err == nil { 152 | field.SetInt(v) 153 | } 154 | } else if kind == reflect.Uint || 155 | kind == reflect.Uint8 || 156 | kind == reflect.Uint16 || 157 | kind == reflect.Uint32 || 158 | kind == reflect.Uint64 { 159 | var v uint64 160 | v, err = strconv.ParseUint(strField, 0, f.Type.Bits()) 161 | if err == nil { 162 | field.SetUint(v) 163 | } 164 | } else if kind == reflect.Float32 || 165 | kind == reflect.Float64 { 166 | var v float64 167 | v, err = strconv.ParseFloat(strField, f.Type.Bits()) 168 | if err == nil { 169 | field.SetFloat(v) 170 | } 171 | } else if kind == reflect.String { 172 | field.SetString(strField) 173 | } else if kind == reflect.Struct || 174 | kind == reflect.Array || 175 | kind == reflect.Slice { 176 | err = json.Unmarshal([]byte(strField), field.Addr().Interface()) 177 | } 178 | 179 | if err != nil { 180 | return fmt.Errorf("parse field (row=%v, col=%v) error: %v", 181 | n, i, err) 182 | } 183 | 184 | // indexes 185 | if f.Tag == "index" { 186 | index := indexes[iIndex] 187 | iIndex++ 188 | if _, ok := index[field.Interface()]; ok { 189 | return fmt.Errorf("index error: duplicate at (row=%v, col=%v)", 190 | n, i) 191 | } 192 | index[field.Interface()] = records[n-1] 193 | } 194 | } 195 | } 196 | 197 | rf.records = records 198 | rf.indexes = indexes 199 | 200 | return nil 201 | } 202 | 203 | func (rf *RecordFile) Record(i int) interface{} { 204 | return rf.records[i] 205 | } 206 | 207 | func (rf *RecordFile) NumRecord() int { 208 | return len(rf.records) 209 | } 210 | 211 | func (rf *RecordFile) Indexes(i int) Index { 212 | if i >= len(rf.indexes) { 213 | return nil 214 | } 215 | return rf.indexes[i] 216 | } 217 | 218 | func (rf *RecordFile) Index(i interface{}) interface{} { 219 | index := rf.Indexes(0) 220 | if index == nil { 221 | return nil 222 | } 223 | return index[i] 224 | } 225 | -------------------------------------------------------------------------------- /recordfile/test.txt: -------------------------------------------------------------------------------- 1 | 数字索引 字符串索引 数字类型 字符串类型 数组类型 嵌套数组 变长数组 结构体类型 2 | 1 one 0 knife "[1, 2]" "[[0,1], [1,2], [2,3]]" "[1, 2, 3]" "{""name"": ""name5566"", ""num"": 1}" 3 | 2 two 0 cat "[3, 4]" "[[1,2], [2,3], [3,4]]" "[4, 5]" "{""name"": ""name5566"", ""num"": 2}" 4 | 3 three 0 book "[5, 6]" "[[2,3], [3,4], [4,5]]" [6] "{""name"": ""name5566"", ""num"": 3}" 5 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package tea 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "runtime" 8 | "strings" 9 | "time" 10 | 11 | "github.com/k4s/tea/message" 12 | ) 13 | 14 | func mkTimer(deadline time.Duration) <-chan time.Time { 15 | 16 | if deadline == 0 { 17 | return nil 18 | } 19 | 20 | return time.After(deadline) 21 | } 22 | 23 | var debug = true 24 | 25 | func debugf(format string, args ...interface{}) { 26 | if debug { 27 | _, file, line, ok := runtime.Caller(1) 28 | if !ok { 29 | file = "" 30 | line = 0 31 | } else { 32 | if i := strings.LastIndex(file, "/"); i >= 0 { 33 | file = file[i+1:] 34 | } 35 | } 36 | fmt.Printf("DEBUG: %s:%d [%s]: %s\n", file, line, 37 | time.Now().String(), fmt.Sprintf(format, args...)) 38 | } 39 | } 40 | 41 | func DrainChannel(ch chan<- *message.Message, expire time.Time) bool { 42 | var dur = time.Millisecond * 10 43 | 44 | for { 45 | if len(ch) == 0 { 46 | return true 47 | } 48 | now := time.Now() 49 | if now.After(expire) { 50 | return false 51 | } 52 | 53 | dur = expire.Sub(now) 54 | if dur > time.Millisecond*10 { 55 | dur = time.Millisecond * 10 56 | } 57 | time.Sleep(dur) 58 | } 59 | } 60 | 61 | func InitConfigFile(filePath string, config interface{}) error { 62 | content, err := ioutil.ReadFile(filePath) 63 | if err != nil { 64 | return err 65 | } 66 | err = json.Unmarshal(content, config) 67 | if err != nil { 68 | return err 69 | } 70 | return nil 71 | } 72 | --------------------------------------------------------------------------------