github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/p2p/connection/connection.go (about) 1 package connection 2 3 import ( 4 "bufio" 5 "fmt" 6 "math" 7 "net" 8 "runtime/debug" 9 "sync/atomic" 10 "time" 11 12 log "github.com/sirupsen/logrus" 13 wire "github.com/tendermint/go-wire" 14 cmn "github.com/tendermint/tmlibs/common" 15 "github.com/tendermint/tmlibs/flowrate" 16 ) 17 18 const ( 19 packetTypePing = byte(0x01) 20 packetTypePong = byte(0x02) 21 packetTypeMsg = byte(0x03) 22 maxMsgPacketPayloadSize = 1024 23 maxMsgPacketOverheadSize = 10 // It's actually lower but good enough 24 maxMsgPacketTotalSize = maxMsgPacketPayloadSize + maxMsgPacketOverheadSize 25 26 numBatchMsgPackets = 10 27 minReadBufferSize = 1024 28 minWriteBufferSize = 65536 29 updateState = 2 * time.Second 30 pingTimeout = 40 * time.Second 31 flushThrottle = 100 * time.Millisecond 32 33 defaultSendQueueCapacity = 1 34 defaultSendRate = int64(512000) // 500KB/s 35 defaultRecvBufferCapacity = 4096 36 defaultRecvMessageCapacity = 22020096 // 21MB 37 defaultRecvRate = int64(512000) // 500KB/s 38 defaultSendTimeout = 10 * time.Second 39 logModule = "p2pConn" 40 ) 41 42 type receiveCbFunc func(chID byte, msgBytes []byte) 43 type errorCbFunc func(interface{}) 44 45 // Messages in channels are chopped into smaller msgPackets for multiplexing. 46 type msgPacket struct { 47 ChannelID byte 48 EOF byte // 1 means message ends here. 49 Bytes []byte 50 } 51 52 func (p msgPacket) String() string { 53 return fmt.Sprintf("MsgPacket{%X:%X T:%X}", p.ChannelID, p.Bytes, p.EOF) 54 } 55 56 /* 57 MConnection handles message transmission on multiple abstract communication 58 `Channel`s. Each channel has a globally unique byte id. 59 The byte id and the relative priorities of each `Channel` are configured upon 60 initialization of the connection. 61 62 There are two methods for sending messages: 63 func (m MConnection) Send(chID byte, msg interface{}) bool {} 64 func (m MConnection) TrySend(chID byte, msg interface{}) bool {} 65 66 `Send(chID, msg)` is a blocking call that waits until `msg` is successfully queued 67 for the channel with the given id byte `chID`, or until the request times out. 68 The message `msg` is serialized using the `tendermint/wire` submodule's 69 `WriteBinary()` reflection routine. 70 71 `TrySend(chID, msg)` is a nonblocking call that returns false if the channel's 72 queue is full. 73 74 Inbound message bytes are handled with an onReceive callback function. 75 */ 76 type MConnection struct { 77 cmn.BaseService 78 79 conn net.Conn 80 bufReader *bufio.Reader 81 bufWriter *bufio.Writer 82 sendMonitor *flowrate.Monitor 83 recvMonitor *flowrate.Monitor 84 send chan struct{} 85 pong chan struct{} 86 channels []*channel 87 channelsIdx map[byte]*channel 88 onReceive receiveCbFunc 89 onError errorCbFunc 90 errored uint32 91 config *MConnConfig 92 93 quit chan struct{} 94 flushTimer *cmn.ThrottleTimer // flush writes as necessary but throttled. 95 pingTimer *time.Ticker // send pings periodically 96 chStatsTimer *time.Ticker // update channel stats periodically 97 } 98 99 // MConnConfig is a MConnection configuration. 100 type MConnConfig struct { 101 SendRate int64 `mapstructure:"send_rate"` 102 RecvRate int64 `mapstructure:"recv_rate"` 103 } 104 105 // DefaultMConnConfig returns the default config. 106 func DefaultMConnConfig() *MConnConfig { 107 return &MConnConfig{ 108 SendRate: defaultSendRate, 109 RecvRate: defaultRecvRate, 110 } 111 } 112 113 // NewMConnectionWithConfig wraps net.Conn and creates multiplex connection with a config 114 func NewMConnectionWithConfig(conn net.Conn, chDescs []*ChannelDescriptor, onReceive receiveCbFunc, onError errorCbFunc, config *MConnConfig) *MConnection { 115 mconn := &MConnection{ 116 conn: conn, 117 bufReader: bufio.NewReaderSize(conn, minReadBufferSize), 118 bufWriter: bufio.NewWriterSize(conn, minWriteBufferSize), 119 sendMonitor: flowrate.New(0, 0), 120 recvMonitor: flowrate.New(0, 0), 121 send: make(chan struct{}, 1), 122 pong: make(chan struct{}, 1), 123 channelsIdx: map[byte]*channel{}, 124 channels: []*channel{}, 125 onReceive: onReceive, 126 onError: onError, 127 config: config, 128 129 pingTimer: time.NewTicker(pingTimeout), 130 chStatsTimer: time.NewTicker(updateState), 131 } 132 133 for _, desc := range chDescs { 134 descCopy := *desc // copy the desc else unsafe access across connections 135 channel := newChannel(mconn, &descCopy) 136 mconn.channelsIdx[channel.id] = channel 137 mconn.channels = append(mconn.channels, channel) 138 } 139 mconn.BaseService = *cmn.NewBaseService(nil, "MConnection", mconn) 140 return mconn 141 } 142 143 // OnStart implements BaseService 144 func (c *MConnection) OnStart() error { 145 c.BaseService.OnStart() 146 c.quit = make(chan struct{}) 147 c.flushTimer = cmn.NewThrottleTimer("flush", flushThrottle) 148 go c.sendRoutine() 149 go c.recvRoutine() 150 return nil 151 } 152 153 // OnStop implements BaseService 154 func (c *MConnection) OnStop() { 155 c.BaseService.OnStop() 156 c.flushTimer.Stop() 157 c.pingTimer.Stop() 158 c.chStatsTimer.Stop() 159 if c.quit != nil { 160 close(c.quit) 161 } 162 c.conn.Close() 163 // We can't close pong safely here because recvRoutine may write to it after we've 164 // stopped. Though it doesn't need to get closed at all, we close it @ recvRoutine. 165 } 166 167 // CanSend returns true if you can send more data onto the chID, false otherwise 168 func (c *MConnection) CanSend(chID byte) bool { 169 if !c.IsRunning() { 170 return false 171 } 172 173 channel, ok := c.channelsIdx[chID] 174 if !ok { 175 return false 176 } 177 return channel.canSend() 178 } 179 180 // Send will queues a message to be sent to channel(blocking). 181 func (c *MConnection) Send(chID byte, msg interface{}) bool { 182 if !c.IsRunning() { 183 return false 184 } 185 186 channel, ok := c.channelsIdx[chID] 187 if !ok { 188 log.WithFields(log.Fields{"module": logModule, "chID": chID}).Error("cannot send bytes due to unknown channel") 189 return false 190 } 191 192 if !channel.sendBytes(wire.BinaryBytes(msg)) { 193 log.WithFields(log.Fields{"module": logModule, "chID": chID, "conn": c, "msg": msg}).Error("MConnection send failed") 194 return false 195 } 196 197 select { 198 case c.send <- struct{}{}: 199 default: 200 } 201 return true 202 } 203 204 // TrafficStatus return the in and out traffic status 205 func (c *MConnection) TrafficStatus() (*flowrate.Status, *flowrate.Status) { 206 sentStatus := c.sendMonitor.Status() 207 receivedStatus := c.recvMonitor.Status() 208 return &sentStatus, &receivedStatus 209 } 210 211 // TrySend queues a message to be sent to channel(Nonblocking). 212 func (c *MConnection) TrySend(chID byte, msg interface{}) bool { 213 if !c.IsRunning() { 214 return false 215 } 216 217 channel, ok := c.channelsIdx[chID] 218 if !ok { 219 log.WithFields(log.Fields{"module": logModule, "chID": chID}).Error("cannot send bytes due to unknown channel") 220 return false 221 } 222 223 ok = channel.trySendBytes(wire.BinaryBytes(msg)) 224 if ok { 225 select { 226 case c.send <- struct{}{}: 227 default: 228 } 229 } 230 return ok 231 } 232 233 func (c *MConnection) String() string { 234 return fmt.Sprintf("MConn{%v}", c.conn.RemoteAddr()) 235 } 236 237 func (c *MConnection) flush() { 238 if err := c.bufWriter.Flush(); err != nil { 239 log.WithFields(log.Fields{"module": logModule, "error": err}).Warn("MConnection flush failed") 240 } 241 } 242 243 // Catch panics, usually caused by remote disconnects. 244 func (c *MConnection) _recover() { 245 if r := recover(); r != nil { 246 stack := debug.Stack() 247 err := cmn.StackError{r, stack} 248 c.stopForError(err) 249 } 250 } 251 252 // recvRoutine reads msgPackets and reconstructs the message using the channels' "recving" buffer. 253 // After a whole message has been assembled, it's pushed to onReceive(). 254 // Blocks depending on how the connection is throttled. 255 func (c *MConnection) recvRoutine() { 256 defer c._recover() 257 defer close(c.pong) 258 259 for { 260 // Block until .recvMonitor says we can read. 261 c.recvMonitor.Limit(maxMsgPacketTotalSize, atomic.LoadInt64(&c.config.RecvRate), true) 262 263 // Read packet type 264 var n int 265 var err error 266 pktType := wire.ReadByte(c.bufReader, &n, &err) 267 c.recvMonitor.Update(int(n)) 268 if err != nil { 269 if c.IsRunning() { 270 log.WithFields(log.Fields{"module": logModule, "conn": c, "error": err}).Warn("Connection failed @ recvRoutine (reading byte)") 271 c.conn.Close() 272 c.stopForError(err) 273 } 274 return 275 } 276 277 // Read more depending on packet type. 278 switch pktType { 279 case packetTypePing: 280 log.WithFields(log.Fields{"module": logModule, "conn": c}).Debug("receive Ping") 281 select { 282 case c.pong <- struct{}{}: 283 default: 284 } 285 286 case packetTypePong: 287 log.WithFields(log.Fields{"module": logModule, "conn": c}).Debug("receive Pong") 288 289 case packetTypeMsg: 290 pkt, n, err := msgPacket{}, int(0), error(nil) 291 wire.ReadBinaryPtr(&pkt, c.bufReader, maxMsgPacketTotalSize, &n, &err) 292 c.recvMonitor.Update(int(n)) 293 if err != nil { 294 if c.IsRunning() { 295 log.WithFields(log.Fields{"module": logModule, "conn": c, "error": err}).Error("failed on recvRoutine") 296 c.stopForError(err) 297 } 298 return 299 } 300 301 channel, ok := c.channelsIdx[pkt.ChannelID] 302 if !ok || channel == nil { 303 cmn.PanicQ(cmn.Fmt("Unknown channel %X", pkt.ChannelID)) 304 } 305 306 msgBytes, err := channel.recvMsgPacket(pkt) 307 if err != nil { 308 if c.IsRunning() { 309 log.WithFields(log.Fields{"module": logModule, "conn": c, "error": err}).Error("failed on recvRoutine") 310 c.stopForError(err) 311 } 312 return 313 } 314 315 if msgBytes != nil { 316 c.onReceive(pkt.ChannelID, msgBytes) 317 } 318 319 default: 320 cmn.PanicSanity(cmn.Fmt("Unknown message type %X", pktType)) 321 } 322 } 323 } 324 325 // Returns true if messages from channels were exhausted. 326 func (c *MConnection) sendMsgPacket() bool { 327 var leastRatio float32 = math.MaxFloat32 328 var leastChannel *channel 329 for _, channel := range c.channels { 330 if !channel.isSendPending() { 331 continue 332 } 333 if ratio := float32(channel.recentlySent) / float32(channel.priority); ratio < leastRatio { 334 leastRatio = ratio 335 leastChannel = channel 336 } 337 } 338 if leastChannel == nil { 339 return true 340 } 341 342 n, err := leastChannel.writeMsgPacketTo(c.bufWriter) 343 if err != nil { 344 log.WithFields(log.Fields{"module": logModule, "error": err}).Error("failed to write msgPacket") 345 c.stopForError(err) 346 return true 347 } 348 c.sendMonitor.Update(int(n)) 349 c.flushTimer.Set() 350 return false 351 } 352 353 // sendRoutine polls for packets to send from channels. 354 func (c *MConnection) sendRoutine() { 355 defer c._recover() 356 357 for { 358 var n int 359 var err error 360 select { 361 case <-c.flushTimer.Ch: 362 c.flush() 363 case <-c.chStatsTimer.C: 364 for _, channel := range c.channels { 365 channel.updateStats() 366 } 367 case <-c.pingTimer.C: 368 log.WithFields(log.Fields{"module": logModule, "conn": c}).Debug("send Ping") 369 wire.WriteByte(packetTypePing, c.bufWriter, &n, &err) 370 c.sendMonitor.Update(int(n)) 371 c.flush() 372 case <-c.pong: 373 log.WithFields(log.Fields{"module": logModule, "conn": c}).Debug("send Pong") 374 wire.WriteByte(packetTypePong, c.bufWriter, &n, &err) 375 c.sendMonitor.Update(int(n)) 376 c.flush() 377 case <-c.quit: 378 return 379 case <-c.send: 380 if eof := c.sendSomeMsgPackets(); !eof { 381 select { 382 case c.send <- struct{}{}: 383 default: 384 } 385 } 386 } 387 388 if !c.IsRunning() { 389 return 390 } 391 if err != nil { 392 log.WithFields(log.Fields{"module": logModule, "conn": c, "error": err}).Warn("Connection failed @ sendRoutine") 393 c.stopForError(err) 394 return 395 } 396 } 397 } 398 399 // Returns true if messages from channels were exhausted. 400 func (c *MConnection) sendSomeMsgPackets() bool { 401 // Block until .sendMonitor says we can write. 402 // Once we're ready we send more than we asked for, 403 // but amortized it should even out. 404 c.sendMonitor.Limit(maxMsgPacketTotalSize, atomic.LoadInt64(&c.config.SendRate), true) 405 for i := 0; i < numBatchMsgPackets; i++ { 406 if c.sendMsgPacket() { 407 return true 408 } 409 } 410 return false 411 } 412 413 func (c *MConnection) stopForError(r interface{}) { 414 c.Stop() 415 if atomic.CompareAndSwapUint32(&c.errored, 0, 1) && c.onError != nil { 416 c.onError(r) 417 } 418 }