├── .gitignore ├── LICENSE ├── README.md ├── client.go ├── common.go ├── demo.go ├── demo_test.go └── server.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/* 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ovenvan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chitchat 2 | -- import "github.com/ovenvan/chitchat" 3 | 4 | What's this: This is a **lightweight**, 5 | support for **high-concurrency** communication Library framework, 6 | based on golang socket. 7 | 8 | What's demo.go/demo_test.go: This is an simple Demo that tells you how to use this framework. 9 | 10 | *** 11 | ## Usage 12 | 13 | Whether for server or client, it only takes three steps to get it worked properly: 14 | 1. Tell it the address of the registration (listening); 15 | 2. Tell it how to process the data after reading it; 16 | 3. Correct handling Error notifications. 17 | 18 | #### For Server: 19 | 20 | ```go 21 | type Server interface { 22 | Listen() error 23 | Cut() error 24 | CloseRemote(string) error 25 | RangeRemoteAddr() []string 26 | GetLocalAddr() string 27 | SetDeadLine(time.Duration, time.Duration) 28 | ErrChan() <-chan Errsocket 29 | Write(interface{}) error 30 | } 31 | ``` 32 | 33 | **func** `NewServer` 34 | ```go 35 | func NewServer( 36 | ipaddrsocket string, 37 | delim byte, readfunc ServerReadFunc, additional interface{}) Server 38 | ``` 39 | The method of registering the server with the framework. 40 | All you need to do is to tell the framework the 41 | 1. IP address(Addr:Port) being monitored, 42 | 2. the delimiter of one sentence, 43 | 3. the Read method, 44 | 4. and the additional information (could be nil). 45 | 46 | if the delimiter is 0, then the framework will NOT send sentence to readfunc until EOF. 47 | 48 | **func** `Listen` 49 | ```go 50 | func Listen() error 51 | ``` 52 | The method does not block the process, 53 | it asynchronously initiates the relevant goroutine to ensure that the framework works properly. 54 | At this point, Server is ready to start normally. 55 | 56 | **func** `Cut` 57 | ```go 58 | func Cut() error 59 | ``` 60 | Terminates the listening service. 61 | 62 | **func** `CloseRemote` 63 | ```go 64 | func CloseRemote(remoteAddr string) error 65 | ``` 66 | Closes a connection for a specific IP. If the connection to the IP does not exist, an error will be returned. 67 | 68 | **func** `RangeRemoteAddr` 69 | ```go 70 | func RangeRemoteAddr() []string 71 | ``` 72 | Returns all the connections. 73 | 74 | **func** `ErrChan` 75 | ```go 76 | func ErrChan() <-chan Errsocket 77 | ``` 78 | Returns the channel allows consumers to receive errors. 79 | 80 | ...(some other funcs defined in `server interface`) 81 | 82 | 83 | #### For Client: 84 | ```go 85 | type Client interface { 86 | Dial() error 87 | Close() 88 | SetDeadLine(time.Duration) 89 | ErrChan() <-chan Errsocket 90 | Write(interface{}) error 91 | GetRemoteAddr() string 92 | GetLocalAddr() string 93 | } 94 | ``` 95 | 96 | **func** `NewClient` 97 | ```go 98 | func NewClient( 99 | ipremotesocket string, 100 | delim byte, readfunc ClientReadFunc, additional interface{}) Client { 101 | ``` 102 | Same as `NewServer`. however, the readfunc can be set nil. 103 | 104 | **func** `Dial() error` 105 | ```go 106 | func Dial() error 107 | ``` 108 | Allows clients to connect to the remote server. You cannot manually select your own IP address for the time being, 109 | but you can obtain it through the GetLocalAddr () method. This method does not block the process. 110 | 111 | **func** `SetDeadLine(time.Duration)` 112 | ```go 113 | func SetDeadLine(dDDL time.Duration) 114 | ``` 115 | This function works before Dial(). It specifies the maximum time for the connection. 116 | 117 | ## How to write READ FUNC? 118 | 119 | When the framework reads the separator, he hands over what he has previously read to the user-defined read func. 120 | 121 | For Server and Client, their ReadFunc is the same, however with different names. 122 | ```go 123 | type ServerReadFunc func([]byte, ReadFuncer) error 124 | type ClientReadFunc func([]byte, ReadFuncer) error 125 | ``` 126 | If you need to use additional parameters, you can get it through t.Addon (). 127 | 128 | This parameter was already given (additional) when the Server (Client) was initialized. 129 | If the time is set to a null value, additional parameters cannot be obtained by the Addon method at this time. 130 | 131 | ## Limitations of Write 132 | The Write method encodes any transmitted data through JSON. 133 | You can decode it through the **Unmarshal()** function when you need it. 134 | The Write method automatically supplements the separator for you, so you don't need to artificially add delimiters. 135 | 136 | However If the Write method provided by the framework does not suit you, you can replace it with the ' SetWriteFunc () ' function. 137 | 138 | ## Demo 139 | A simple heartbeat detection package. 140 | 141 | ## For More info 142 | [如何使用golang - chitchat](https://www.jianshu.com/p/956c04a9310b) 143 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package chitchat 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type ClientReadFunc func([]byte, ReadFuncer) error 11 | 12 | type Client interface { 13 | Dial() error 14 | Close() 15 | SetDeadLine(time.Duration) 16 | ErrChan() <-chan Errsocket 17 | Write(interface{}) error 18 | GetRemoteAddr() string 19 | GetLocalAddr() string 20 | } 21 | 22 | type client struct { 23 | ipaddr string 24 | dialDDL time.Duration 25 | delimiter byte 26 | 27 | readfunc ClientReadFunc 28 | //What's rcmu: func CLOSE can be used inside readfunc, in such case, return err cannot be sent to user without rcmu. 29 | //Why not server: func CUT cannot be used inside readfunc. 30 | rcmu *sync.Mutex 31 | 32 | eDer 33 | eDerfunc eDinitfunc 34 | 35 | conn net.Conn 36 | cancelfunc context.CancelFunc 37 | 38 | //tempory used for readfunc 39 | additional interface{} 40 | } 41 | 42 | type hConnerClient struct { 43 | conn net.Conn 44 | d byte 45 | rcmu *sync.Mutex //lock for readfunc and close 46 | readfunc ClientReadFunc 47 | eD *eDer 48 | } 49 | 50 | func NewClient( 51 | ipremotesocket string, 52 | delim byte, readfunc ClientReadFunc, additional interface{}) Client { 53 | 54 | c := &client{ 55 | ipaddr: ipremotesocket, 56 | dialDDL: 0, 57 | delimiter: delim, 58 | readfunc: readfunc, 59 | eDer: eDer{ 60 | eU: make(chan Errsocket), 61 | closed: false, 62 | mu: new(sync.Mutex), 63 | pmu: nil, 64 | }, 65 | rcmu: new(sync.Mutex), 66 | additional: additional, 67 | } 68 | c.eDerfunc = errDiversion(&c.eDer) 69 | return c 70 | } 71 | 72 | func (t *client) Dial() error { 73 | var ( 74 | err error 75 | ctx, cfunc = context.WithCancel(context.Background()) 76 | eC = make(chan Errsocket) 77 | ) 78 | t.cancelfunc = cfunc 79 | 80 | if t.dialDDL == 0 { 81 | t.conn, err = net.Dial("tcp", t.ipaddr) 82 | } else { 83 | t.conn, err = net.DialTimeout("tcp", t.ipaddr, t.dialDDL) 84 | } 85 | if err != nil { 86 | return err 87 | } 88 | go t.eDerfunc(eC) 89 | go handleConnClient(&hConnerClient{ 90 | conn: t.conn, 91 | d: t.delimiter, 92 | readfunc: t.readfunc, 93 | rcmu: t.rcmu, 94 | //mu: t.mu, 95 | eD: &t.eDer, 96 | }, eC, ctx, t) 97 | 98 | return nil 99 | } 100 | 101 | func handleConnClient(h *hConnerClient, eC chan Errsocket, ctx context.Context, client *client) { 102 | //fmt.Println("Start hCC:", h.conn.LocalAddr(), "->", h.conn.RemoteAddr()) 103 | //defer fmt.Println("->hCC quit", h.conn.LocalAddr(), "->", h.conn.RemoteAddr()) 104 | strReqChan := make(chan []byte) 105 | defer func() { 106 | if !h.eD.closed { 107 | h.eD.mu.Lock() 108 | h.eD.closed = true 109 | close(h.eD.eU) 110 | h.eD.mu.Lock() 111 | } 112 | err := h.conn.Close() 113 | <-strReqChan 114 | if err != nil { 115 | h.eD.mu.Lock() 116 | eC <- Errsocket{err, h.conn.RemoteAddr().String()} 117 | } 118 | close(eC) 119 | }() 120 | 121 | go read(&reader{ 122 | conn: h.conn, 123 | d: h.d, 124 | mu: h.eD.mu, 125 | strReqChan: strReqChan, 126 | }, eC) 127 | 128 | for { 129 | select { 130 | case <-ctx.Done(): //quit manually 131 | return 132 | case strReq, ok := <-strReqChan: //read a data slice successfully 133 | if !ok { 134 | return //EOF && d!=0 135 | } 136 | if h.readfunc != nil { 137 | h.rcmu.Lock() 138 | err := h.readfunc(strReq, client) 139 | if err != nil { 140 | h.eD.mu.Lock() 141 | eC <- Errsocket{err, h.conn.RemoteAddr().String()} 142 | } 143 | h.rcmu.Unlock() 144 | } 145 | } 146 | } 147 | } 148 | 149 | func (t *client) Close() { 150 | go func() { 151 | t.rcmu.Lock() 152 | t.mu.Lock() 153 | t.closed = true 154 | close(t.eU) 155 | t.mu.Unlock() 156 | t.rcmu.Unlock() 157 | t.cancelfunc() 158 | }() 159 | } 160 | 161 | func (t *client) SetDeadLine(dDDL time.Duration) { 162 | t.dialDDL = dDDL 163 | } 164 | 165 | func (t *client) ErrChan() <-chan Errsocket { 166 | return t.eU 167 | } 168 | 169 | func (t *client) GetRemoteAddr() string { 170 | if t.conn == nil { 171 | return "" 172 | } 173 | return t.conn.RemoteAddr().String() 174 | } 175 | func (t *client) GetLocalAddr() string { 176 | if t.conn == nil { 177 | return "" 178 | } 179 | return t.conn.LocalAddr().String() 180 | } 181 | 182 | func (t *client) GetConn() net.Conn { 183 | return t.conn 184 | } 185 | 186 | func (t *client) Addon() interface{} { 187 | return t.additional 188 | } 189 | 190 | func (t *client) Write(i interface{}) error { 191 | return writeFunc(t.conn, i, t.delimiter) 192 | } 193 | -------------------------------------------------------------------------------- /common.go: -------------------------------------------------------------------------------- 1 | package chitchat 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "net" 8 | "sync" 9 | ) 10 | 11 | var closedchan = make(chan struct{}) 12 | var writeFunc = Write 13 | 14 | type ReadFuncer interface { 15 | GetRemoteAddr() string 16 | GetLocalAddr() string 17 | GetConn() net.Conn 18 | Close() 19 | Write(interface{}) error 20 | Addon() interface{} 21 | } 22 | 23 | func init() { 24 | close(closedchan) 25 | } 26 | 27 | type wf func(net.Conn, interface{}, byte) error 28 | 29 | type eDinitfunc func(eC chan Errsocket) 30 | 31 | type eDer struct { 32 | eU chan Errsocket 33 | //why it's a value: this flag will only be modified by CUT, and only used by eD. 34 | //*eDer.closed is a pointer. closed flag will not be copied. 35 | //Warning: DONOT copy closed flag separately. 36 | closed bool 37 | //why it's a pointer: mutex will be copied separately. make sure the replicas are the same. 38 | mu *sync.Mutex 39 | pmu *sync.Mutex 40 | } 41 | 42 | type Errsocket struct { 43 | Err error 44 | RemoteAddr string 45 | } 46 | 47 | type reader struct { 48 | conn net.Conn 49 | d byte 50 | mu *sync.Mutex 51 | strReqChan chan<- []byte 52 | } 53 | 54 | func errDiversion(eD *eDer) func(eC chan Errsocket) { 55 | //when upstream channel(uC) is closed(detected by closed flag), 56 | //following data will be received but discarded 57 | //when eC channel has closed, this goroutine will exit 58 | return func(eC chan Errsocket) { 59 | //fmt.Println("Start eD") 60 | //defer fmt.Println("->eD quit") 61 | for { 62 | err, ok := <-eC 63 | if !ok { 64 | return // consider multi-conn in one uC, i cannot close uC now 65 | } 66 | if !eD.closed { 67 | if eD.pmu != nil { 68 | eD.pmu.Lock() //send to upstream channel 69 | } 70 | eD.eU <- err 71 | } 72 | eD.mu.Unlock() 73 | } 74 | } 75 | } 76 | 77 | /* 78 | server: 79 | Delimiter with remoted closed connection: EOF warning. 80 | Delimiter with local closed connection: used of a closed network warning. 81 | Delimiter with healthy connection: waiting for delimiter to DO readfunc 82 | No delimiter with remoted closed connection: DO readfunc with string read. 83 | No delimiter with local closed: DO NOTHING.(Strange) 84 | No delimiter with healthy connection: waiting for closed. 85 | */ 86 | func read(r *reader, eC chan Errsocket) { 87 | //fmt.Println("Start read", r.conn.LocalAddr(), "->", r.conn.RemoteAddr()) 88 | //defer fmt.Println("->read quit", r.conn.LocalAddr(), "->", r.conn.RemoteAddr()) 89 | defer func() { 90 | close(r.strReqChan) 91 | }() 92 | readBytes := make([]byte, 1) 93 | for { 94 | var buffer bytes.Buffer 95 | for { 96 | _, err := r.conn.Read(readBytes) 97 | if err != nil { 98 | //fmt.Println(err.Error()) 99 | if r.d == 0 { 100 | r.strReqChan <- buffer.Bytes() 101 | } else { 102 | r.mu.Lock() 103 | eC <- Errsocket{err, r.conn.RemoteAddr().String()} 104 | } 105 | return 106 | } 107 | readByte := readBytes[0] 108 | if r.d != 0 && readByte == r.d { 109 | break 110 | } 111 | buffer.WriteByte(readByte) 112 | } 113 | if r.d == '\n' && buffer.Bytes()[len(buffer.Bytes())-1] == '\r' { 114 | r.strReqChan <- buffer.Bytes()[:len(buffer.Bytes())-1] 115 | } else { 116 | r.strReqChan <- buffer.Bytes() 117 | } 118 | } 119 | } 120 | 121 | func SetWriteFunc(f wf) { 122 | writeFunc = f 123 | } 124 | 125 | //it seems better 126 | func Write(c net.Conn, i interface{}, d byte) error { 127 | if c == nil { 128 | return errors.New("connection not found") 129 | } 130 | 131 | data, err := json.Marshal(i) 132 | if err != nil { 133 | return err 134 | } 135 | if d != 0 { 136 | data = append(data, d) 137 | } 138 | _, err = c.Write(data) 139 | return err 140 | } 141 | -------------------------------------------------------------------------------- /demo.go: -------------------------------------------------------------------------------- 1 | package chitchat 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | type MasterRoler interface { 11 | Listen() error 12 | Close() error 13 | } 14 | type NodeRoler interface { 15 | Register() error 16 | Leave() error 17 | } 18 | 19 | type Node struct { 20 | roleMaster bool 21 | local ipx 22 | remote ipx 23 | 24 | //For Nodes/master 25 | leave func() error 26 | 27 | //For MasterNode 28 | registeredIP []string 29 | closesignal chan struct{} 30 | } 31 | 32 | type ipx struct { 33 | ipaddr string 34 | ipport string 35 | } 36 | 37 | type pingStruct struct { 38 | Data string 39 | Id int 40 | } 41 | 42 | func registerNode(str []byte, s ReadFuncer) error { 43 | if sx := string(str); sx != "" { 44 | fmt.Println(sx) 45 | x := s.Addon().(*Node) 46 | x.registeredIP = append(x.registeredIP, x.remote.ipaddr) 47 | go daemonHBChecker(x.remote, x.closesignal) 48 | fmt.Println(x.registeredIP) 49 | } 50 | return nil 51 | } 52 | 53 | func hb4node(str []byte, s ReadFuncer) error { 54 | v := new(pingStruct) 55 | err := json.Unmarshal(str, v) 56 | if err != nil { 57 | return err 58 | } 59 | if v.Data == "heartbeat ping" { 60 | if err := s.Write(pingStruct{ 61 | Data: "heartbeat pong", 62 | Id: v.Id + 1, 63 | }); err == nil { 64 | return errors.New("succeed") 65 | } 66 | return errors.New("writing data error") 67 | } 68 | return errors.New("err message received") 69 | } 70 | 71 | func hb4master(str []byte, s ReadFuncer) error { 72 | defer s.Close() 73 | v := new(pingStruct) 74 | err := json.Unmarshal(str, v) 75 | if err != nil { 76 | return err 77 | } 78 | if v.Data == "heartbeat pong" { 79 | return errors.New("succeed") 80 | } 81 | return errors.New("err message received") 82 | } 83 | 84 | func (t *Node) daemonHBListener() error { //for Nodes listen Master's hbc 85 | fmt.Println("HBListen start.") 86 | defer fmt.Println("->HBListen quit.") 87 | s := NewServer(t.local.ipaddr+":"+"7939", '\n', hb4node, nil) 88 | t.leave = s.Cut 89 | if err := s.Listen(); err != nil { 90 | return err 91 | } 92 | go func() { 93 | defer fmt.Println("->HBL err Daemon closed.") 94 | fmt.Println("HBL err Daemon start.") 95 | timeout := time.Second * 10 96 | timer := time.NewTimer(timeout) 97 | 98 | for { 99 | select { 100 | case v, ok := <-s.ErrChan(): 101 | if ok { 102 | if v.Err.Error() == "succeed" { //node sends succeed. 103 | timer.Reset(timeout) 104 | } 105 | } else { 106 | return 107 | } 108 | case <-timer.C: 109 | err := s.Cut() 110 | if err != nil { 111 | println(err) 112 | } 113 | fmt.Println("!Found Master is Dead") 114 | return 115 | //TODO: Timeout, master is dead. 116 | } 117 | } 118 | }() 119 | return nil 120 | } 121 | 122 | func daemonHBChecker(ip ipx, csignal <-chan struct{}) { //for master check 123 | defer fmt.Println("->HBChecker quit") 124 | fmt.Println("HBChecker start.") 125 | i := time.NewTicker(3 * time.Second) 126 | failedTimes := 0 127 | for { 128 | select { 129 | case <-csignal: 130 | return 131 | case <-i.C: 132 | fmt.Println("-----------------------------------") 133 | c := NewClient(ip.ipaddr+":"+"7939", '\n', hb4master, nil) 134 | c.SetDeadLine(2 * time.Second) 135 | if err := c.Dial(); err != nil { 136 | //TODO: Failed once. 137 | failedTimes++ 138 | break 139 | } 140 | go func() { 141 | fmt.Println("HBC err Daemon start.") 142 | for { 143 | v, ok := <-c.ErrChan() 144 | if ok { 145 | if v.Err.Error() == "err message received" { 146 | failedTimes++ 147 | } else if v.Err.Error() == "hbc succeed" { 148 | failedTimes = 0 149 | } 150 | } else { 151 | fmt.Println("->HBC err Daemon closed.") 152 | return 153 | } 154 | } 155 | }() 156 | if err := c.Write(pingStruct{ 157 | Data: "heartbeat ping", 158 | Id: 0, 159 | }); err != nil { 160 | failedTimes++ 161 | break 162 | } 163 | } //break to here 164 | 165 | fmt.Println(ip.ipaddr+":"+ip.ipport+" failed time: ", failedTimes) 166 | if failedTimes > 3 { 167 | //TODO: this connection is failed. 168 | return 169 | } 170 | } 171 | } 172 | 173 | func iportSplitter(socket string) *ipx { 174 | flag := false 175 | s1 := make([]byte, 0) 176 | for i := 0; i < len(socket); i++ { 177 | if socket[i] == ':' { 178 | flag = true 179 | continue 180 | } 181 | if !flag { 182 | s1 = append(s1, socket[i]) 183 | } else { 184 | return &ipx{string(s1), socket[i:]} 185 | } 186 | } 187 | return nil 188 | } 189 | 190 | func NewNode(remoteAddr string) NodeRoler { 191 | return &Node{ 192 | roleMaster: false, 193 | remote: *iportSplitter(remoteAddr), 194 | registeredIP: nil, 195 | } 196 | } 197 | 198 | func NewMaster(ipAddr string) MasterRoler { 199 | t := iportSplitter(ipAddr) 200 | return &Node{ 201 | roleMaster: true, 202 | local: *t, 203 | remote: *t, 204 | registeredIP: make([]string, 0), 205 | closesignal: make(chan struct{}), 206 | } 207 | } 208 | 209 | func (t *Node) Listen() error { 210 | server := NewServer(t.local.ipaddr+":"+t.local.ipport, 0, registerNode, t) 211 | if err := server.Listen(); err != nil { 212 | return err 213 | } 214 | t.leave = server.Cut 215 | return nil 216 | } 217 | 218 | func (t *Node) Register() error { 219 | slave := NewClient(t.remote.ipaddr+":"+t.remote.ipport, 0, nil, nil) 220 | if err := slave.Dial(); err != nil { 221 | return err 222 | } 223 | t.local = *iportSplitter(slave.GetLocalAddr()) 224 | if err := slave.Write("hello"); err != nil { 225 | return err 226 | } 227 | slave.Close() 228 | return t.daemonHBListener() 229 | } 230 | 231 | func (t *Node) Leave() error { 232 | return t.leave() 233 | } 234 | 235 | func (t *Node) Close() error { 236 | err := t.leave() 237 | if err != nil { 238 | return err 239 | } 240 | close(t.closesignal) 241 | return nil 242 | } 243 | -------------------------------------------------------------------------------- /demo_test.go: -------------------------------------------------------------------------------- 1 | package chitchat 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestAll(t *testing.T) { 10 | master := NewMaster("127.0.0.1:12345") 11 | _ = master.Listen() 12 | node1 := NewNode("127.0.0.1:12345") 13 | _ = node1.Register() 14 | time.Sleep(time.Second * 10) 15 | //node1.Leave() 16 | fmt.Println(master.Close()) 17 | fmt.Println("Master closed") 18 | time.Sleep(time.Second * 20) 19 | } 20 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package chitchat 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "net" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | type ServerReadFunc func([]byte, ReadFuncer) error 12 | 13 | type Server interface { 14 | Listen() error 15 | Cut() error 16 | CloseRemote(string) error 17 | RangeRemoteAddr() []string 18 | GetLocalAddr() string 19 | SetDeadLine(time.Duration, time.Duration) 20 | ErrChan() <-chan Errsocket 21 | Write(interface{}) error 22 | } 23 | 24 | type server struct { 25 | //unchangable data 26 | ipaddr string 27 | readDDL time.Duration 28 | writeDDL time.Duration 29 | //if delimiter is 0, then read until it's EOF 30 | delimiter byte 31 | readfunc ServerReadFunc 32 | 33 | remoteMap *sync.Map 34 | 35 | //under protected data 36 | eDer 37 | eDerfunc eDinitfunc 38 | 39 | l net.Listener 40 | cancelfunc context.CancelFunc //cancelfunc for cancel listener(CUT) 41 | 42 | //tempory used for readfunc 43 | currentConn net.Conn 44 | additional interface{} 45 | } 46 | 47 | type hConnerServer struct { 48 | conn net.Conn 49 | d byte 50 | mu *sync.Mutex 51 | readfunc ServerReadFunc 52 | } 53 | 54 | func NewServer( 55 | ipaddrsocket string, 56 | delim byte, readfunc ServerReadFunc, additional interface{}) Server { 57 | 58 | s := &server{ 59 | ipaddr: ipaddrsocket, 60 | readDDL: 0, 61 | writeDDL: 0, 62 | delimiter: delim, 63 | eDer: eDer{ 64 | eU: make(chan Errsocket), 65 | closed: false, 66 | mu: new(sync.Mutex), 67 | pmu: nil, 68 | }, 69 | remoteMap: new(sync.Map), 70 | readfunc: readfunc, 71 | additional: additional, 72 | } 73 | s.eDerfunc = errDiversion(&s.eDer) 74 | return s 75 | } 76 | 77 | func (t *server) Listen() error { //Notifies the consumer when an error occurs ASYNCHRONOUSLY 78 | if t.readfunc == nil { 79 | return errors.New("read function is nil") 80 | } 81 | listener, err := net.Listen("tcp", t.ipaddr) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | var ctx, cfunc = context.WithCancel(context.Background()) 87 | t.cancelfunc = cfunc 88 | t.l = listener 89 | eC := make(chan Errsocket) 90 | go t.eDerfunc(eC) 91 | go handleListen(t, eC, ctx) 92 | return nil 93 | } 94 | 95 | func handleListen(s *server, eC chan Errsocket, ctx context.Context) { 96 | //fmt.Println("Start hL") 97 | //defer fmt.Println("->hL quit") 98 | defer close(eC) 99 | for { 100 | select { 101 | case <-ctx.Done(): 102 | return 103 | default: 104 | conn, err := s.l.Accept() 105 | if err != nil { 106 | s.mu.Lock() 107 | eC <- Errsocket{err, s.ipaddr} 108 | return 109 | } 110 | if s.readDDL != 0 { 111 | _ = conn.SetReadDeadline(time.Now().Add(s.readDDL)) 112 | } 113 | if s.writeDDL != 0 { 114 | _ = conn.SetWriteDeadline(time.Now().Add(s.writeDDL)) 115 | } 116 | 117 | cctx, childfunc := context.WithCancel(ctx) //TODO: if MAXLIVETIME is needed. 118 | s.remoteMap.Store(conn.RemoteAddr().String(), childfunc) 119 | 120 | ceC := make(chan Errsocket) 121 | go s.eDerfunc(ceC) 122 | go handleConnServer(&hConnerServer{ 123 | conn: conn, 124 | d: s.delimiter, 125 | readfunc: s.readfunc, 126 | mu: s.mu, 127 | }, ceC, cctx, s) 128 | } 129 | } 130 | } 131 | 132 | func handleConnServer(h *hConnerServer, eC chan Errsocket, ctx context.Context, s *server) { 133 | //fmt.Println("Start hCS:", h.conn.LocalAddr(), "->", h.conn.RemoteAddr()) 134 | //defer fmt.Println("->hCS quit", h.conn.LocalAddr(), "->", h.conn.RemoteAddr()) 135 | strReqChan := make(chan []byte) 136 | defer func() { 137 | err := h.conn.Close() 138 | <-strReqChan 139 | if err != nil { 140 | h.mu.Lock() 141 | eC <- Errsocket{err, h.conn.RemoteAddr().String()} 142 | } 143 | close(eC) 144 | }() 145 | 146 | go read(&reader{ 147 | conn: h.conn, 148 | d: h.d, 149 | mu: h.mu, 150 | strReqChan: strReqChan, 151 | }, eC) 152 | 153 | for { 154 | select { 155 | case <-ctx.Done(): //quit manually 156 | return 157 | case strReq, ok := <-strReqChan: //read a data slice successfully 158 | if !ok { 159 | return //EOF && d!=0 160 | } 161 | err := h.readfunc(strReq, &server{ 162 | currentConn: h.conn, 163 | delimiter: h.d, 164 | remoteMap: s.remoteMap, 165 | additional: s.additional, 166 | }) 167 | if err != nil { 168 | h.mu.Lock() 169 | eC <- Errsocket{err, h.conn.RemoteAddr().String()} 170 | } 171 | } 172 | } 173 | } 174 | 175 | /* 176 | will not wait for the rest of goroutines' error message. 177 | make sure all connections has exited successfully before doing this 178 | */ 179 | func (t *server) Cut() error { 180 | t.mu.Lock() 181 | err := t.l.Close() 182 | if err != nil { 183 | //t.eU <- Errsocket{err, t.ipaddr} 184 | return err 185 | } 186 | t.closed = true 187 | close(t.eU) 188 | t.mu.Unlock() 189 | t.cancelfunc() 190 | return nil 191 | } 192 | 193 | func (t *server) CloseRemote(remoteAddr string) error { 194 | x, ok := t.remoteMap.Load(remoteAddr) 195 | if !ok { 196 | return errors.New(remoteAddr + " does not connected to this server") 197 | } 198 | x.(context.CancelFunc)() 199 | t.remoteMap.Delete(remoteAddr) 200 | return nil 201 | } 202 | 203 | func (t *server) Close() { 204 | remoteAddr := t.currentConn.RemoteAddr().String() 205 | x, ok := t.remoteMap.Load(remoteAddr) 206 | if !ok { 207 | t.eU <- Errsocket{errors.New("internal error"), t.ipaddr} 208 | return 209 | } 210 | x.(context.CancelFunc)() 211 | t.remoteMap.Delete(remoteAddr) 212 | } 213 | 214 | func (t *server) RangeRemoteAddr() []string { 215 | rtnstring := make([]string, 0) 216 | t.remoteMap.Range( 217 | func(key, value interface{}) bool { 218 | rtnstring = append(rtnstring, key.(string)) 219 | return true 220 | }) 221 | return rtnstring 222 | } 223 | 224 | func (t *server) SetDeadLine(rDDL time.Duration, wDDL time.Duration) { 225 | t.readDDL, t.writeDDL = rDDL, wDDL 226 | } 227 | 228 | func (t *server) ErrChan() <-chan Errsocket { 229 | return t.eU 230 | } 231 | 232 | func (t *server) GetRemoteAddr() string { 233 | if t.currentConn == nil { 234 | return "" 235 | } 236 | return t.currentConn.RemoteAddr().String() 237 | } 238 | 239 | func (t *server) GetLocalAddr() string { 240 | if t.currentConn == nil { 241 | return "" 242 | } 243 | return t.currentConn.LocalAddr().String() 244 | } 245 | 246 | func (t *server) GetConn() net.Conn { 247 | return t.currentConn 248 | } 249 | 250 | func (t *server) Addon() interface{} { 251 | return t.additional 252 | } 253 | 254 | func (t *server) Write(i interface{}) error { 255 | return writeFunc(t.currentConn, i, t.delimiter) 256 | } 257 | --------------------------------------------------------------------------------