github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/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 c.stopForError(fmt.Sprintf("Error: %v\nStack: %s", r, stack)) 248 } 249 } 250 251 // recvRoutine reads msgPackets and reconstructs the message using the channels' "recving" buffer. 252 // After a whole message has been assembled, it's pushed to onReceive(). 253 // Blocks depending on how the connection is throttled. 254 func (c *MConnection) recvRoutine() { 255 defer c._recover() 256 defer close(c.pong) 257 258 for { 259 // Block until .recvMonitor says we can read. 260 c.recvMonitor.Limit(maxMsgPacketTotalSize, atomic.LoadInt64(&c.config.RecvRate), true) 261 262 // Read packet type 263 var n int 264 var err error 265 pktType := wire.ReadByte(c.bufReader, &n, &err) 266 c.recvMonitor.Update(int(n)) 267 if err != nil { 268 if c.IsRunning() { 269 log.WithFields(log.Fields{"module": logModule, "conn": c, "error": err}).Warn("Connection failed @ recvRoutine (reading byte)") 270 c.conn.Close() 271 c.stopForError(err) 272 } 273 return 274 } 275 276 // Read more depending on packet type. 277 switch pktType { 278 case packetTypePing: 279 log.WithFields(log.Fields{"module": logModule, "conn": c}).Debug("receive Ping") 280 select { 281 case c.pong <- struct{}{}: 282 default: 283 } 284 285 case packetTypePong: 286 log.WithFields(log.Fields{"module": logModule, "conn": c}).Debug("receive Pong") 287 288 case packetTypeMsg: 289 pkt, n, err := msgPacket{}, int(0), error(nil) 290 wire.ReadBinaryPtr(&pkt, c.bufReader, maxMsgPacketTotalSize, &n, &err) 291 c.recvMonitor.Update(int(n)) 292 if err != nil { 293 if c.IsRunning() { 294 log.WithFields(log.Fields{"module": logModule, "conn": c, "error": err}).Error("failed on recvRoutine") 295 c.stopForError(err) 296 } 297 return 298 } 299 300 channel, ok := c.channelsIdx[pkt.ChannelID] 301 if !ok || channel == nil { 302 cmn.PanicQ(cmn.Fmt("Unknown channel %X", pkt.ChannelID)) 303 } 304 305 msgBytes, err := channel.recvMsgPacket(pkt) 306 if err != nil { 307 if c.IsRunning() { 308 log.WithFields(log.Fields{"module": logModule, "conn": c, "error": err}).Error("failed on recvRoutine") 309 c.stopForError(err) 310 } 311 return 312 } 313 314 if msgBytes != nil { 315 c.onReceive(pkt.ChannelID, msgBytes) 316 } 317 318 default: 319 cmn.PanicSanity(cmn.Fmt("Unknown message type %X", pktType)) 320 } 321 } 322 } 323 324 // Returns true if messages from channels were exhausted. 325 func (c *MConnection) sendMsgPacket() bool { 326 var leastRatio float32 = math.MaxFloat32 327 var leastChannel *channel 328 for _, channel := range c.channels { 329 if !channel.isSendPending() { 330 continue 331 } 332 if ratio := float32(channel.recentlySent) / float32(channel.priority); ratio < leastRatio { 333 leastRatio = ratio 334 leastChannel = channel 335 } 336 } 337 if leastChannel == nil { 338 return true 339 } 340 341 n, err := leastChannel.writeMsgPacketTo(c.bufWriter) 342 if err != nil { 343 log.WithFields(log.Fields{"module": logModule, "error": err}).Error("failed to write msgPacket") 344 c.stopForError(err) 345 return true 346 } 347 c.sendMonitor.Update(int(n)) 348 c.flushTimer.Set() 349 return false 350 } 351 352 // sendRoutine polls for packets to send from channels. 353 func (c *MConnection) sendRoutine() { 354 defer c._recover() 355 356 for { 357 var n int 358 var err error 359 select { 360 case <-c.flushTimer.Ch: 361 c.flush() 362 case <-c.chStatsTimer.C: 363 for _, channel := range c.channels { 364 channel.updateStats() 365 } 366 case <-c.pingTimer.C: 367 log.WithFields(log.Fields{"module": logModule, "conn": c}).Debug("send Ping") 368 wire.WriteByte(packetTypePing, c.bufWriter, &n, &err) 369 c.sendMonitor.Update(int(n)) 370 c.flush() 371 case <-c.pong: 372 log.WithFields(log.Fields{"module": logModule, "conn": c}).Debug("send Pong") 373 wire.WriteByte(packetTypePong, c.bufWriter, &n, &err) 374 c.sendMonitor.Update(int(n)) 375 c.flush() 376 case <-c.quit: 377 return 378 case <-c.send: 379 if eof := c.sendSomeMsgPackets(); !eof { 380 select { 381 case c.send <- struct{}{}: 382 default: 383 } 384 } 385 } 386 387 if !c.IsRunning() { 388 return 389 } 390 if err != nil { 391 log.WithFields(log.Fields{"module": logModule, "conn": c, "error": err}).Warn("Connection failed @ sendRoutine") 392 c.stopForError(err) 393 return 394 } 395 } 396 } 397 398 // Returns true if messages from channels were exhausted. 399 func (c *MConnection) sendSomeMsgPackets() bool { 400 // Block until .sendMonitor says we can write. 401 // Once we're ready we send more than we asked for, 402 // but amortized it should even out. 403 c.sendMonitor.Limit(maxMsgPacketTotalSize, atomic.LoadInt64(&c.config.SendRate), true) 404 for i := 0; i < numBatchMsgPackets; i++ { 405 if c.sendMsgPacket() { 406 return true 407 } 408 } 409 return false 410 } 411 412 func (c *MConnection) stopForError(r interface{}) { 413 c.Stop() 414 if atomic.CompareAndSwapUint32(&c.errored, 0, 1) && c.onError != nil { 415 c.onError(r) 416 } 417 }