linux tcp server开源,GitHub - 06linux/cellnet: 高性能,简单,方便的开源服务器网络库...
cellnet
cellnet是一個高性能,簡單,方便的開源服務器網絡庫
自由混合編碼,業務代碼無需調整。
TCP和html5的應用都可以直接使用cellnet迅速搭建服務器框架。
與Java的Netty或Mina網絡庫類似的Handler機制將給予強大定制功能。
特性
數據協議
支持混合編碼收發
與Unity3D+Lua使用sproto通信
與其他語言編寫的服務器使用protobuf
與web服務器使用json通信
無論使用何種協議, 使用cellnet響應/收發消息的寫法都保持一致
傳輸協議支持:
TCP(基于Type-Length-Value私有協議)
WebSocket
基于handler處理鏈
自定義, 組裝收發流程, 包頭等
支持專有日志調試, 方便查看處理流程
隊列及IO
支持多個隊列, 實現單線程/多線程收發處理消息
發送時自動合并封包(性能效果決定于實際請求和發送比例)
RPC
異步/同步遠程過程調用
消息日志
可以方便的通過日志查看收發消息的每一個字段消息
第三方庫依賴
github.com/davyxu/golog
github.com/davyxu/goobjfmt
編碼包可選支持
github.com/golang/protobuf
github.com/davyxu/gosproto
websocket可選支持
github.com/gorilla/websocket
獲取+編譯
go get -u -v github.com/davyxu/cellnet
例子主要采用了protobuf做編碼,因此需要安裝protobuf支持
go get -v github.com/golang/protobuf
性能測試
命令行: go test -v github.com/davyxu/cellnet/benchmark/io
平臺: Windows 7 x64/CentOS 6.5 x64
測試用例: localhost 1000連接 同時對服務器進行實時PingPong測試
配置1: i7 6700 3.4GHz 8核
IOPS: 12.5w
配置2: i5 4590 3.3GHz 4核
IOPS: 10.1w
樣例
func server() {
queue := cellnet.NewEventQueue()
p := socket.NewAcceptor(queue).Start("127.0.0.1:7201")
cellnet.RegisterMessage(p, "gamedef.TestEchoACK", func(ev *cellnet.Event) {
msg := ev.Msg.(*gamedef.TestEchoACK)
log.Debugln("server recv:", msg.Content)
ev.Send(&gamedef.TestEchoACK{
Content: msg.String(),
})
})
queue.StartLoop()
}
func client() {
queue := cellnet.NewEventQueue()
p := socket.NewConnector(queue).Start("127.0.0.1:7301")
cellnet.RegisterMessage(p, "gamedef.TestEchoACK", func(ev *cellnet.Event) {
msg := ev.Msg.(*gamedef.TestEchoACK)
log.Debugln("client recv:", msg.Content)
})
cellnet.RegisterMessage(p, "coredef.SessionConnected", func(ev *cellnet.Event) {
log.Debugln("client connected")
ev.Send(&gamedef.TestEchoACK{
Content: "hello",
})
})
cellnet.RegisterMessage(p, "coredef.SessionConnectFailed", func(ev *cellnet.Event) {
msg := ev.Msg.(*coredef.SessionConnectFailed)
log.Debugln(msg.Reason)
})
queue.StartLoop()
}
目錄功能
benchmark 性能測試用例
proto cellnet內部的proto
binary 內部系統消息,rpc消息
pb protobuf消息(內部測試用)
sproto sproto的消息(內部測試用)
protoc-gen-msg protobuf的protoc插件, 消息id生成, 使用pb編碼時使用
objprotogen binary格式的消息綁定工具, 使用binary編碼時使用
rpc 異步遠程過程調用封裝
socket 套接字,連接管理等封裝
example 例子
chat 聊天服務器例子
echo_websocket websocket服務器例子
tests 測試用例
timer 計時器接口
util 工具庫
運行聊天例子
運行 服務器
cd examples/chat/server
go run main.go
運行 客戶端
cd examples/chat/client
go run main.go
隨后, 在命令行中輸入hello后打回車, 就可以看到服務器返回
sid1 say: hello
協議生成
這個例子中實現了最簡單的客戶端與服務器通信
通信使用examples/chat/proto/chat.proto的protobuf協議定義
修改協議后, 可使用GenerateProto.bat進行協議生成
生成代碼時, 會編譯protoc-gen-go和protoc-gen-msg兩個插件
配合protobuf的協議編譯器protoc.exe, 分別生成chatproto目錄下的chat.pb.go和msgid.go兩個文件
術語及概念
隊列
隊列在cellnet中使用cellnet.Queue接口, 底層由帶緩沖的channel實現
在cellnet中, 隊列根據實際邏輯需要定制數量. 但一般情況下, 僅需要1個隊列
使用下列代碼使用隊列
queue := cellnet.NewEventQueue()
// 啟動隊列
queue.StartLoop()
// 這里添加隊列使用代碼
// 等待隊列結束, 調用queue.StopLoop(0)將退出阻塞
queue.Wait()
當在多線程環境中, 需要將邏輯排隊執行時, 我們只需要這樣寫:
queue.Post(func() {
fmt.Println("hello")
})
我們使用的消息, 在底層就是通過queue.Post() 傳入我們給定的回調進行處理的
提示
隊列對于使用cellnet的服務器程序不是必須的.不使用隊列時, 所有消息的處理將是并發在多線程下
偵聽和接受連接
使用如下代碼創建一個接受器(Acceptor)
queue := cellnet.NewEventQueue()
peer := socket.NewAcceptor(queue)
peer.Start("127.0.0.1:8801")
底層將自動完成偵聽, 接受連接, 錯誤拋出, 斷開處理等復雜操作
提示
cellnet將系統事件, 錯誤和用戶消息均視為消息, 并可以被注冊回調后處理, 接口都是統一的
接受器可接收的系統消息可以用下面代碼注冊并響應:
cellnet.RegisterMessage(peer, "coredef.SessionAccepted", func(ev *cellnet.Event) {
// 其他會話連接時
})
cellnet.RegisterMessage(peer, "coredef.SessionAcceptFailed", func(ev *cellnet.Event) {
// 其他會話連接失敗時
})
發起連接
使用如下代碼創建一個連接器(Connector)
queue := cellnet.NewEventQueue()
peer := socket.NewConnector(queue)
peer.Start("127.0.0.1:8801")
底層將自動完成連接; 如果發生斷開, 可以通過如下代碼設置自動重連
// 設置連接超時2秒后自動重連
peer.(socket.Connector).SetAutoReconnectSec(2)
連接器也可以接收系統事件, 如:
cellnet.RegisterMessage(peer, "coredef.SessionConnectFailed", func(ev *cellnet.Event) {
// 會話連接失敗
})
提示:
端
cellnet中, Connector和Acceptor被統稱為Peer, 即"端", 當連接器和接受器建立連接后, 兩個"端"的概念, 接口和使用均是相同的
會話連接(Session)
建立連接后, 這個連接在cellnet中稱為Session
Session ID
每個會話擁有一個64位ID, cellnet底層保證在一個Peer中不會重復
可以通過Session.ID() 獲得ID
獲得Session
Session可以通過以下途徑獲得:
通過cellnet.RegisterMessage注冊回調后, 通過回調參數*cellnet.Event中的Ses獲得
如果Peer是Connector, 可以通過如下代碼獲得連接器上默認連接
ses := peer.(socket.Connector).DefaultSession()
Peer可以通過SessionAccessor接口以多種方式獲得Session, 如:
// 通過Session.ID獲得
GetSession(int64) Session
// 遍歷這個Peer的所有Session
VisitSession(func(Session) bool)
接收消息
消息通過cellnet.Event傳遞
如需獲得消息, 我們使用如下代碼獲得消息
msg := ev.Msg.(*MsgPackage.YourMsgType)
接收系統事件
如需接收Session連接斷開事件, 使用如下代碼
cellnet.RegisterMessage(peer, "coredef.SessionClosed", func(ev *cellnet.Event) {
// 會話斷開時
})
會話發送消息
一般情況下, 我們的消息使用結構體實現. 使用protobuf工具鏈, sproto工具鏈可以直接生成這些結構體.
通過Session.Send()發送一個結構體指針, 如:
ses.Send(&chatproto.ChatREQ{
Content: str,
})
提示
使用Event.Send方式回消息
cellnet.RegisterMessage(peer, "chatproto.ChatACK", func(ev *cellnet.Event) {
msg := ev.Msg.(*chatproto.ChatACK)
// 使用這種方法回應消息與rpc系統統一, 便于底層優化
ev.Send( msg )
})
不要緩存Event
Event是消息處理的上下文, 不建議緩存Event
FAQ
這個代碼的入口在哪里? 怎么編譯為exe?
本代碼是一個網絡庫, 需要根據需求, 整合邏輯
支持WebSocket么?
支持!
本網絡庫的Websocket基于第三方整合, 包格式基于文本: 包名\n+json內容
參見examples/websocket
混合編碼有何用途?
在與多種語言寫成的服務器進行通信時, 可以使用不同的編碼,
最終在邏輯層都是統一的結構能讓邏輯編寫更加方便, 無需關注底層處理細節
內建支持的二進制協議能與其他語言寫成的網絡庫互通么?
完全支持, 但內建二進制協議支持更適合網關與后臺服務器.
不建議與客戶端通信中使用, 二進制協議不會忽略使用默認值的字段
我能通過Handler處理鏈進行怎樣的擴展?
封包需要加密, 統計, 預處理時, 可以使用Handler. 每個Handler建議無狀態,
需要存儲的數據, 可以通過Event中的Tag進行擴展
如何查看Handler處理流程?
在程序啟動時, 調用如下代碼
cellnet.EnableHandlerLog = true
可在日志中看到如下日志格式
[DEBUG] cellnet 2000/00/00 01:02:03 9 Event_Connected [svc->agent] SesID: 1 MsgID: 3551021301(coredef.SessionConnected) {} Tag: TransmitTag: Raw: (0)[]
9 表示一個Event處理序號, 同一序號表示1個處理流程, 例如1個接收/發送流程
Event_Connected 表示事件名
[svc->agent] 表示peer的名稱
表示Handler的名稱, 通過反射取得
SesID 表示 會話ID, 由SessionManager分配
MsgID 表示消息號, 后面括號中是對應的消息名, 如果未在系統中注冊, 顯示為空, 后續是消息內容
TransmitTag, Tag 附屬上下文內容
Raw, 表示消息的原始二進制信息
所有的例子都是單線程的, 能編寫多線程的邏輯么?
完全可以, cellnet并沒有全局的隊列, 只需在Acceptor和Connector創建時,
傳入不同的隊列, socket收到的消息就會被放到這個隊列中
傳入空隊列時, 使用并發方式(io線程)調用處理回調
消息日志為什么與處理函數日志順序不統一?
由于消息日志反應的是收到消息的日志, 因此必須放置在io線程中處理. 而單線程邏輯與io線程分別在不同的線程. 日志順序錯位是正常的
如果需要順序日志: 可以在進程啟動時, 調用runtime.GOMAXPROCS(1), 將go的線程調度默認為1CPU
cellnet有網關和db支持么?
cellnet專注于服務器底層.你可以根據自己需要編寫網關及db支持
cellnet的私有tcp封包格式是怎樣的?
功能
類型
備注
序號
uint16
初始為1, 接收一次自增1
消息ID
uint32
包.消息名 的hash值(util.StringHash)
包體大小
uint32
包體大小
包體
[]byte
包體內容, 長度為包體大小指定, 變長
怎樣定制私有tcp封包?
使用cellnet.Peer下組合接口的HandlerChainManager.SetReadWriteChain進行設置, 寫法如
self.SetReadWriteChain(func() *cellnet.HandlerChain {
return cellnet.NewHandlerChain(
cellnet.NewFixedLengthFrameReader(10),
NewPrivatePacketReader(),
)
}, func() *cellnet.HandlerChain {
return cellnet.NewHandlerChain(NewPrivatePacketWriter(),
cellnet.NewFixedLengthFrameWriter(),
)
})
HandlerChainManager擁有讀寫鏈和收發鏈, 處理流程如下:
讀鏈->接收鏈->邏輯處理->發送鏈->寫鏈
哪里有cellnet的完整例子?
CellOrigin是基于cellnet開發的一套Unity3D客戶端服務器框架
https://github.com/davyxu/cellorigin
版本歷史
2017.8 v3版本 詳細請查看
2017.1 v2版本 詳細請查看
2015.8v1版本
貢獻者
按貢獻時間排序,越靠前表示越新的貢獻
superikw(https://github.com/superikw), 測試出一個websocket接口并發發送問題
chuan.li(https://github.com/blade-226), 提供一個沒有在io線程編碼的bug
Chris Lonng(https://github.com/lonnng), 提供一個最大封包約束造成服務器間連接斷開的bug
viwii(viwii@sina.cn), 提供一個可能造成死鎖的bug
備注
感覺不錯請star, 謝謝!
開源討論群: 527430600
總結
以上是生活随笔為你收集整理的linux tcp server开源,GitHub - 06linux/cellnet: 高性能,简单,方便的开源服务器网络库...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 申请信用卡需本人签字 申请信用卡不是本人
- 下一篇: 电视画面天花板终于来了!索尼画谛A95K