diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e9258a0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 JayL gitdig.com@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5a90bb2 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +tcpserver +=== + +## Installation + +```` +$: go get github.com/x-mod/tcpserver +```` + +## Quick Start + +```` +import ( + "net" + "log" + "context" + "github.com/x-mod/tcpserver" +) + +func EchoHandler(ctx context.Context, con net.Conn) error { + //TODO LOGIC + return nil +} + +func main() { + srv := tcpserver.NewServer( + tcpserver.Network("tcp"), + tcpserver.Address("127.0.0.1:8080"), + tcpserver.Handler(EchoHandler), + ) + if err := srv.Serve(context.TODO()); err != nil { + log.Println("tcpserver failed:", err) + } +} +```` + +More Details, Pls check the [example](example/server.go). diff --git a/example/server.go b/example/server.go new file mode 100644 index 0000000..81030ac --- /dev/null +++ b/example/server.go @@ -0,0 +1,46 @@ +package main + +import ( + "context" + "log" + "net" + "net/textproto" + "strings" + + "github.com/x-mod/routine" + "github.com/x-mod/tcpserver" +) + +func main() { + srv := tcpserver.NewServer( + tcpserver.Network("tcp"), + tcpserver.Address("127.0.0.1:8080"), + tcpserver.Handler(EchoHandler), + ) + if err := routine.Main(routine.ExecutorFunc(srv.Serve)); err != nil { + log.Println("tcpserver failed:", err) + } +} + +func EchoHandler(ctx context.Context, con net.Conn) error { + defer con.Close() + + c := textproto.NewConn(con) + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + line, err := c.ReadLine() + if err != nil { + return err + } + if strings.HasPrefix(line, "quit") { + return nil + } + if err := c.PrintfLine(line); err != nil { + return err + } + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7bd16db --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/x-mod/tcpserver + +go 1.12 + +require github.com/x-mod/routine v1.1.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c452494 --- /dev/null +++ b/go.sum @@ -0,0 +1,40 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cfssl v0.0.0-20190328212615-ea569c5aa1be/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rakyll/hey v0.1.1/go.mod h1:sX26N/FW0nkvNZFzqnBp13eDIZNs5gJXSljSHj0vOXA= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/x-mod/cronexpr v1.0.0/go.mod h1:p2v5cgbUf0RY9jS/KOriOiKIGxEdHRqPOkFxOFvmLco= +github.com/x-mod/errors v0.1.0 h1:uuN9qGgq9vEHrlMqdsSC7kyE88q8Uv/jfOwRfRPLLQM= +github.com/x-mod/errors v0.1.0/go.mod h1:d+HrEt85NDHN0I4yJ9pQjy1JZEhPsSNou3sIFu5vXOk= +github.com/x-mod/routine v1.1.0 h1:peyTprIvl3xScEtr2NcW+cNcLEm8FMovUpYMNoUyvBE= +github.com/x-mod/routine v1.1.0/go.mod h1:ep1ekcf9g36JOjz82/PmDYcKzYiNKFlkv5P1A4CZz2g= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/tcpserver.go b/tcpserver.go new file mode 100644 index 0000000..4430735 --- /dev/null +++ b/tcpserver.go @@ -0,0 +1,87 @@ +package tcpserver + +import ( + "context" + "log" + "net" +) + +//ConnectionHandler connection handler definition +type ConnectionHandler func(ctx context.Context, conn net.Conn) error + +//Server represent tcpserver +type Server struct { + network string + address string + handler ConnectionHandler +} + +//Network option for listener +func Network(inet string) ServerOpt { + return func(srv *Server) { + if len(inet) != 0 { + srv.network = inet + } + } +} + +//Address option for listener +func Address(addr string) ServerOpt { + return func(srv *Server) { + if len(addr) != 0 { + srv.address = addr + } + } +} + +//Handler option for connection +func Handler(h ConnectionHandler) ServerOpt { + return func(srv *Server) { + if h != nil { + srv.handler = h + } + } +} + +//ServerOpt typedef +type ServerOpt func(*Server) + +//NewServer create a new tcpserver +func NewServer(opts ...ServerOpt) *Server { + serv := &Server{} + for _, opt := range opts { + opt(serv) + } + return serv +} + +//Serve tcpserver serving +func (srv *Server) Serve(ctx context.Context) error { + ln, err := net.Listen(srv.network, srv.address) + if err != nil { + return err + } + log.Println("tcpserver serving at ", srv.network, srv.address) + + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + con, err := ln.Accept() + if err != nil { + if ne, ok := err.(net.Error); ok && ne.Temporary() { + log.Printf("warning: accept temp err: %v", ne) + continue + } + return err + } + + go func() { + if err := srv.handler(ctx, con); err != nil { + log.Printf("connection %s handle failed: %s\n", con.RemoteAddr().String(), err) + } + }() + } + } +}