{"id":18602780,"url":"https://github.com/obgnail/net-link","last_synced_at":"2026-04-25T23:36:54.921Z","repository":{"id":41276756,"uuid":"508984613","full_name":"obgnail/net-link","owner":"obgnail","description":"封装 net，提供简单易用接口。使得业务层能面向 session 编程，同时支持 HiJack 获取底层 connection。","archived":false,"fork":false,"pushed_at":"2022-07-11T02:44:38.000Z","size":7,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-18T01:41:56.497Z","etag":null,"topics":["golang","network","network-library","network-manager","protocol","session"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obgnail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-30T07:52:14.000Z","updated_at":"2023-03-15T14:43:55.000Z","dependencies_parsed_at":"2022-09-10T16:51:38.651Z","dependency_job_id":null,"html_url":"https://github.com/obgnail/net-link","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obgnail%2Fnet-link","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obgnail%2Fnet-link/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obgnail%2Fnet-link/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obgnail%2Fnet-link/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obgnail","download_url":"https://codeload.github.com/obgnail/net-link/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254582909,"owners_count":22095519,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","network","network-library","network-manager","protocol","session"],"created_at":"2024-11-07T02:12:30.533Z","updated_at":"2026-04-25T23:36:49.857Z","avatar_url":"https://github.com/obgnail.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# net-link\n\n封装 net，提供简单易用接口。\n\n```go\nfunc main() {\n  // json data echo server\n\terr := net_link.ListenAndAccept(\"127.0.0.1:10086\", codec.Json(), net_link.HandlerFunc(\n\t\tfunc(s *net_link.Session) error {\n      \n\t\t\treceiver := new(demo.Student)\n\t\t\tif err := s.Read(receiver); err != nil {\n\t\t\t\treturn errors.Trace(err)\n\t\t\t}\n      \n\t\t\tif !reflect.DeepEqual(receiver, msg) {\n\t\t\t\tpanic(\"read err\")\n\t\t\t}\n      \n\t\t\tif err := s.SyncWrite(receiver); err != nil {\n\t\t\t\treturn errors.Trace(err)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t))\n}\n```\n\n\n\n## Server接口\n\n```go\ntype Handler interface {\n\thandler(s *Session) error\n}\n\n// 同步Listen\nListenAndAccept(addr string, protocol Protocol, handler Handler) error\n\n// 异步Dial\nAsyncDial(laddr, raddr string, protocol Protocol, handler Handler) error\n\n// 同步Dial\nSyncDial(laddr, raddr string, protocol Protocol, handler Handler) error\n\n// 异步处理Conn\nAsyncServeConn(conn net.Conn, protocol Protocol, handler Handler) error\n\n// 同步处理Conn\nSyncServeConn(conn net.Conn, protocol Protocol, handler Handler) error\n```\n\n\n\n## Session接口\n\nSession 所有接口皆是线程安全的。\n\n```go\ntype Session struct {}\n\n// Protocol:codec的工厂函数, codec会负责解析io\nfunc NewSession(rw io.ReadWriter, protocol Protocol, sendChanSize int) (*Session, error) \n\n// 返回原生io\n// HiJack返回的io已经脱离了Session的控制，不再线程安全，如果需要并发读写，需要自行加解锁\nfunc (s *Session) HiJack() io.ReadWriter\n\n// 同步读入\nfunc (s *Session) Read(msg interface{}) error\n\n// 异步写入\nfunc (s *Session) AsyncWrite(msg interface{}) error\n\n// 同步写入\nfunc (s *Session) SyncWrite(msg interface{}) error\n\n// 会话ID\nfunc (s *Session) ID() uint64\n\n// session是否关闭\nfunc (s *Session) IsClosed() bool\n\n// 关闭session\n// 可重复调用。但是不建议调用此函数，session退出、异常时会自动调用\nfunc (s *Session) Close() error\n```\n\n\n\n## 解耦\n\n将网络服务解耦成五部分：io、codec、session、handler、server。\n\n```mermaid\ngraph LR\n\t\tsubgraph server\n\t\tsubgraph handler\n\t\tsubgraph session\n    subgraph codec\n        io\n        end\n        end\n        end\n        end\n```\n\n1. io：TCP/UDP/UnixSocket/bufio…\n2. codec 内嵌 io：编解码器。提供编解码能力。\n3. session 内嵌 codec：会话。提供线程安全的读写操作。\n4. handler 内嵌 session：处理函数。\n5. server 内嵌 handler：由 handler 组成的服务。\n\n\n\n### io\n\n数据流。\n\n```\nrw io.ReadWriter\n```\n\nio 必须实现 io.Reader 和 io.Writer 接口，但是并不要求必须实现 io.Closer 接口（如果没有实现，将自动封装成 nopCloser）。这样就可以支持 bufio 等没有 close 函数的 io。\n\n\n\n### codec\n\n编解码器。内嵌 io 对象，编解码 io 流。\n\n```go\ntype Codec interface {\n\tio.Closer\n\tRead(receiver interface{}) error\n\tWrite(send interface{}) error\n  // 返回原生io\n\tHiJack() io.ReadWriter\n}\n\ntype Protocol interface {\n\t// 并不要求io实现closer接口\n\tNewCodec(rw io.ReadWriter) (Codec, error)\n}\n```\n\ncodec 除了提供 Read、Write 函数，还有一个 HiJack 函数负责返回原生 io。\n\nProtocol 是 Codec 的工厂函数。\n\n\n\n### Session\n\nsession 内嵌 codec，提供线程安全的读写操作。\n\n实现面向 Session 编程。\n\n提供 HiJack 函数以获取底层 connection，保留对连接的控制。\n\n\n\n### handler\n\n负责具体业务，负责操作 Session。\n\n```go\ntype Handler interface {\n\thandler(s *Session) error\n}\n\ntype HandlerFunc func(*Session) error\n\nfunc (f HandlerFunc) handler(s *Session) error {\n\treturn f(s)\n}\n```\n\n\n\n### server\n\n```go\nerr := ListenAndAccept(\"127.0.0.1:10086\", codec.Json(), func(s *Session) error {\n  // do something\n}\n```\n\n\n\n## example\n\n```go\n// server\nfunc main() {\n\tlog.Info(\"---- server start ----\")\n  \n\terr := net_link.ListenAndAccept(\"127.0.0.1:10086\", codec.Byte(), net_link.HandlerFunc(\n\t\tfunc(s *net_link.Session) error {\n      \n\t\t\treceiver := make([]byte, 9)\n\t\t\tif err := s.Read(receiver); err != nil {\n\t\t\t\treturn errors.Trace(err)\n\t\t\t}\n      \n\t\t\tif !bytes.Equal(receiver, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) {\n\t\t\t\tpanic(\"read err\")\n\t\t\t}\n      \n\t\t\ttcpConn := s.HiJack()\n\t\t\tfmt.Println(\"remote addr:\", tcpConn.(net.Conn).RemoteAddr())\n      \n\t\t\tif _, err := tcpConn.Write([]byte{2, 3, 4}); err != nil {\n\t\t\t\treturn errors.Trace(err)\n\t\t\t}\n      \n\t\t\treturn nil\n\t\t},\n\t))\n  \n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n```\n\n```go\n// client\nfunc main() {\n\tlog.Info(\"---- client start ----\")\n  \n\terr := net_link.DialSync(\"127.0.0.1:0\", \"127.0.0.1:10086\", codec.Byte(), net_link.HandlerFunc(\n\t\tfunc(s *net_link.Session) error {\n      \n\t\t\tmsg := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}\n\t\t\tif err := s.SyncWrite(msg); err != nil {\n\t\t\t\treturn errors.Trace(err)\n\t\t\t}\n      \n\t\t\tnewMsg := make([]byte, 3)\n\t\t\tif err := s.Read(newMsg); err != nil {\n\t\t\t\treturn errors.Trace(err)\n\t\t\t}\n      \n\t\t\tif !reflect.DeepEqual(newMsg, []byte{2, 3, 4}) {\n\t\t\t\tpanic(\"read err\")\n\t\t\t}\n      \n\t\t\treturn nil\n\t\t},\n\t))\n\tif err != nil \u0026\u0026 !strings.Contains(err.Error(), \"EOF\") {\n\t\tpanic(err)\n\t}\n}\n```\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobgnail%2Fnet-link","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobgnail%2Fnet-link","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobgnail%2Fnet-link/lists"}