go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/transport/tcp/tcp.go (about) 1 // Copyright 2020 The Mangos Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use file except in compliance with the License. 5 // You may obtain a copy of the license at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package tcp implements the TCP transport for mangos. To enable it simply 16 // import it. 17 package tcp 18 19 import ( 20 "context" 21 "net" 22 "sync" 23 "time" 24 25 "go.nanomsg.org/mangos/v3" 26 "go.nanomsg.org/mangos/v3/transport" 27 ) 28 29 const ( 30 // Transport is a transport.Transport for TCP. 31 Transport = tcpTran(0) 32 ) 33 34 func init() { 35 transport.RegisterTransport(Transport) 36 } 37 38 type dialer struct { 39 addr string 40 proto transport.ProtocolInfo 41 hs transport.Handshaker 42 maxRecvSize int 43 d net.Dialer 44 lock sync.Mutex 45 } 46 47 func (d *dialer) Dial() (_ transport.Pipe, err error) { 48 conn, err := d.d.Dial("tcp", d.addr) 49 if err != nil { 50 return nil, err 51 } 52 53 p := transport.NewConnPipe(conn, d.proto) 54 d.lock.Lock() 55 p.SetOption(mangos.OptionMaxRecvSize, d.maxRecvSize) 56 d.lock.Unlock() 57 d.hs.Start(p) 58 return d.hs.Wait() 59 } 60 61 func (d *dialer) SetOption(n string, v interface{}) error { 62 d.lock.Lock() 63 defer d.lock.Unlock() 64 65 switch n { 66 case mangos.OptionMaxRecvSize: 67 if b, ok := v.(int); ok { 68 d.maxRecvSize = b 69 return nil 70 } 71 return mangos.ErrBadValue 72 case mangos.OptionKeepAliveTime: 73 if b, ok := v.(time.Duration); ok { 74 d.d.KeepAlive = b 75 return nil 76 } 77 return mangos.ErrBadValue 78 79 // The following options exist *only* for compatibility reasons. 80 // Remove them from new code. 81 82 case mangos.OptionKeepAlive: 83 if b, ok := v.(bool); ok { 84 if b { 85 d.d.KeepAlive = 0 // Enable (default time) 86 } else { 87 d.d.KeepAlive = -1 // Disable 88 } 89 return nil 90 } 91 return mangos.ErrBadValue 92 93 case mangos.OptionNoDelay: 94 if _, ok := v.(bool); ok { 95 return nil 96 } 97 return mangos.ErrBadValue 98 } 99 return mangos.ErrBadOption 100 } 101 102 func (d *dialer) GetOption(n string) (interface{}, error) { 103 d.lock.Lock() 104 defer d.lock.Unlock() 105 106 switch n { 107 case mangos.OptionMaxRecvSize: 108 return d.maxRecvSize, nil 109 case mangos.OptionKeepAliveTime: 110 return d.d.KeepAlive, nil 111 case mangos.OptionNoDelay: 112 return true, nil 113 case mangos.OptionKeepAlive: 114 if d.d.KeepAlive >= 0 { 115 return true, nil 116 } 117 return false, nil 118 119 } 120 return nil, mangos.ErrBadOption 121 } 122 123 type listener struct { 124 addr string 125 bound net.Addr 126 proto transport.ProtocolInfo 127 l net.Listener 128 lc net.ListenConfig 129 maxRecvSize int 130 handshaker transport.Handshaker 131 closeq chan struct{} 132 once sync.Once 133 lock sync.Mutex 134 } 135 136 func (l *listener) Accept() (transport.Pipe, error) { 137 138 if l.l == nil { 139 return nil, mangos.ErrClosed 140 } 141 return l.handshaker.Wait() 142 } 143 144 func (l *listener) Listen() (err error) { 145 select { 146 case <-l.closeq: 147 return mangos.ErrClosed 148 default: 149 } 150 l.l, err = l.lc.Listen(context.Background(), "tcp", l.addr) 151 if err != nil { 152 return 153 } 154 l.bound = l.l.Addr() 155 go func() { 156 for { 157 conn, err := l.l.Accept() 158 if err != nil { 159 select { 160 case <-l.closeq: 161 return 162 default: 163 // We probably should be checking 164 // if this is a temporary error. 165 // If we run out of files we will 166 // spin hard here. 167 time.Sleep(time.Millisecond) 168 continue 169 } 170 } 171 p := transport.NewConnPipe(conn, l.proto) 172 l.lock.Lock() 173 p.SetOption(mangos.OptionMaxRecvSize, l.maxRecvSize) 174 l.lock.Unlock() 175 l.handshaker.Start(p) 176 } 177 }() 178 return 179 } 180 181 func (l *listener) Address() string { 182 if b := l.bound; b != nil { 183 return "tcp://" + b.String() 184 } 185 return "tcp://" + l.addr 186 } 187 188 func (l *listener) Close() error { 189 l.once.Do(func() { 190 close(l.closeq) 191 if l.l != nil { 192 _ = l.l.Close() 193 } 194 l.handshaker.Close() 195 }) 196 return nil 197 } 198 199 func (l *listener) SetOption(n string, v interface{}) error { 200 l.lock.Lock() 201 defer l.lock.Unlock() 202 203 switch n { 204 case mangos.OptionMaxRecvSize: 205 if b, ok := v.(int); ok { 206 l.maxRecvSize = b 207 return nil 208 } 209 return mangos.ErrBadValue 210 case mangos.OptionKeepAliveTime: 211 if b, ok := v.(time.Duration); ok { 212 l.lc.KeepAlive = b 213 return nil 214 } 215 return mangos.ErrBadValue 216 217 // The following options exist *only* for compatibility reasons. 218 // Remove them from new code. 219 220 case mangos.OptionKeepAlive: 221 if b, ok := v.(bool); ok { 222 if b { 223 l.lc.KeepAlive = 0 // Enable (default time) 224 } else { 225 l.lc.KeepAlive = -1 // Disable 226 } 227 return nil 228 } 229 return mangos.ErrBadValue 230 231 case mangos.OptionNoDelay: 232 if _, ok := v.(bool); ok { 233 return nil 234 } 235 return mangos.ErrBadValue 236 } 237 return mangos.ErrBadOption 238 } 239 240 func (l *listener) GetOption(n string) (interface{}, error) { 241 l.lock.Lock() 242 defer l.lock.Unlock() 243 244 switch n { 245 case mangos.OptionMaxRecvSize: 246 return l.maxRecvSize, nil 247 case mangos.OptionKeepAliveTime: 248 return l.lc.KeepAlive, nil 249 case mangos.OptionNoDelay: 250 return true, nil 251 case mangos.OptionKeepAlive: 252 if l.lc.KeepAlive >= 0 { 253 return true, nil 254 } 255 return false, nil 256 } 257 return nil, mangos.ErrBadOption 258 } 259 260 type tcpTran int 261 262 func (t tcpTran) Scheme() string { 263 return "tcp" 264 } 265 266 func (t tcpTran) NewDialer(addr string, sock mangos.Socket) (transport.Dialer, error) { 267 var err error 268 if addr, err = transport.StripScheme(t, addr); err != nil { 269 return nil, err 270 } 271 272 // check to ensure the provided addr resolves correctly. 273 if _, err = transport.ResolveTCPAddr(addr); err != nil { 274 return nil, err 275 } 276 277 d := &dialer{ 278 addr: addr, 279 proto: sock.Info(), 280 hs: transport.NewConnHandshaker(), 281 } 282 283 return d, nil 284 } 285 286 func (t tcpTran) NewListener(addr string, sock mangos.Socket) (transport.Listener, error) { 287 var err error 288 l := &listener{ 289 proto: sock.Info(), 290 closeq: make(chan struct{}), 291 } 292 293 if addr, err = transport.StripScheme(t, addr); err != nil { 294 return nil, err 295 } 296 297 l.addr = addr 298 299 l.handshaker = transport.NewConnHandshaker() 300 return l, nil 301 }