tendermint/peer/connection.go

201 lines
4.2 KiB
Go
Raw Normal View History

2014-06-18 20:48:32 -07:00
package peer
import (
2014-07-03 13:44:19 -07:00
"bufio"
2014-07-01 14:50:24 -07:00
"fmt"
"net"
"sync/atomic"
"time"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
2014-06-18 20:48:32 -07:00
)
const (
2014-07-01 14:50:24 -07:00
OUT_QUEUE_SIZE = 50
IDLE_TIMEOUT_MINUTES = 5
PING_TIMEOUT_MINUTES = 2
2014-06-18 20:48:32 -07:00
)
/* Connnection */
type Connection struct {
2014-07-01 14:50:24 -07:00
ioStats IOStats
sendQueue chan Packet // never closes
conn net.Conn
2014-07-03 13:44:19 -07:00
bufWriter *bufio.Writer
bufReader *bufio.Reader
2014-07-01 14:50:24 -07:00
quit chan struct{}
stopped uint32
pingDebouncer *Debouncer
pong chan struct{}
2014-06-18 20:48:32 -07:00
}
var (
2014-07-01 14:50:24 -07:00
PACKET_TYPE_PING = UInt8(0x00)
PACKET_TYPE_PONG = UInt8(0x01)
PACKET_TYPE_MSG = UInt8(0x10)
2014-06-18 20:48:32 -07:00
)
func NewConnection(conn net.Conn) *Connection {
2014-07-01 14:50:24 -07:00
return &Connection{
sendQueue: make(chan Packet, OUT_QUEUE_SIZE),
conn: conn,
2014-07-03 13:44:19 -07:00
bufWriter: bufio.NewWriterSize(conn, 1024),
bufReader: bufio.NewReaderSize(conn, 1024),
2014-07-01 14:50:24 -07:00
quit: make(chan struct{}),
pingDebouncer: NewDebouncer(PING_TIMEOUT_MINUTES * time.Minute),
pong: make(chan struct{}),
}
2014-06-18 20:48:32 -07:00
}
2014-06-24 17:28:40 -07:00
// returns true if successfully queued,
// returns false if connection was closed.
// blocks.
func (c *Connection) Send(pkt Packet) bool {
2014-07-01 14:50:24 -07:00
select {
case c.sendQueue <- pkt:
return true
case <-c.quit:
return false
}
2014-06-18 20:48:32 -07:00
}
func (c *Connection) Start(channels map[String]*Channel) {
2014-07-01 14:50:24 -07:00
log.Debugf("Starting %v", c)
go c.sendHandler()
go c.recvHandler(channels)
2014-06-18 20:48:32 -07:00
}
2014-06-24 17:28:40 -07:00
func (c *Connection) Stop() {
2014-07-01 14:50:24 -07:00
if atomic.CompareAndSwapUint32(&c.stopped, 0, 1) {
log.Debugf("Stopping %v", c)
close(c.quit)
c.conn.Close()
c.pingDebouncer.Stop()
// We can't close pong safely here because
// recvHandler may write to it after we've stopped.
// Though it doesn't need to get closed at all,
// we close it @ recvHandler.
// close(c.pong)
}
2014-06-24 17:28:40 -07:00
}
func (c *Connection) LocalAddress() *NetAddress {
2014-07-01 14:50:24 -07:00
return NewNetAddress(c.conn.LocalAddr())
2014-06-24 17:28:40 -07:00
}
func (c *Connection) RemoteAddress() *NetAddress {
2014-07-01 14:50:24 -07:00
return NewNetAddress(c.conn.RemoteAddr())
2014-06-18 20:48:32 -07:00
}
2014-06-29 00:35:16 -07:00
func (c *Connection) String() string {
2014-07-01 14:50:24 -07:00
return fmt.Sprintf("Connection{%v}", c.conn.RemoteAddr())
2014-06-29 00:35:16 -07:00
}
2014-06-18 20:48:32 -07:00
func (c *Connection) flush() {
2014-07-01 14:50:24 -07:00
// TODO flush? (turn off nagel, turn back on, etc)
2014-06-18 20:48:32 -07:00
}
func (c *Connection) sendHandler() {
2014-07-01 14:50:24 -07:00
log.Tracef("%v sendHandler", c)
// TODO: catch panics & stop connection.
FOR_LOOP:
for {
var err error
select {
case <-c.pingDebouncer.Ch:
2014-07-03 13:44:19 -07:00
_, err = PACKET_TYPE_PING.WriteTo(c.bufWriter)
2014-07-01 14:50:24 -07:00
case sendPkt := <-c.sendQueue:
log.Tracef("Found pkt from sendQueue. Writing pkt to underlying connection")
2014-07-03 13:44:19 -07:00
_, err = PACKET_TYPE_MSG.WriteTo(c.bufWriter)
2014-07-01 14:50:24 -07:00
if err != nil {
break
}
2014-07-03 13:44:19 -07:00
_, err = sendPkt.WriteTo(c.bufWriter)
2014-07-01 14:50:24 -07:00
case <-c.pong:
2014-07-03 13:44:19 -07:00
_, err = PACKET_TYPE_PONG.WriteTo(c.bufWriter)
2014-07-01 14:50:24 -07:00
case <-c.quit:
break FOR_LOOP
}
if atomic.LoadUint32(&c.stopped) == 1 {
break FOR_LOOP
}
2014-07-01 14:50:24 -07:00
if err != nil {
log.Infof("%v failed @ sendHandler:\n%v", c, err)
c.Stop()
break FOR_LOOP
}
c.flush()
}
log.Tracef("%v sendHandler done", c)
// cleanup
2014-06-18 20:48:32 -07:00
}
func (c *Connection) recvHandler(channels map[String]*Channel) {
2014-07-01 14:50:24 -07:00
log.Tracef("%v recvHandler with %v channels", c, len(channels))
// TODO: catch panics & stop connection.
FOR_LOOP:
for {
2014-07-03 13:44:19 -07:00
pktType, err := ReadUInt8Safe(c.bufReader)
2014-07-01 14:50:24 -07:00
if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 {
log.Infof("%v failed @ recvHandler", c)
c.Stop()
}
break FOR_LOOP
} else {
log.Tracef("Found pktType %v", pktType)
}
switch pktType {
case PACKET_TYPE_PING:
c.pong <- struct{}{}
case PACKET_TYPE_PONG:
// do nothing
case PACKET_TYPE_MSG:
2014-07-03 13:44:19 -07:00
pkt, err := ReadPacketSafe(c.bufReader)
2014-07-01 14:50:24 -07:00
if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 {
log.Infof("%v failed @ recvHandler", c)
c.Stop()
}
break FOR_LOOP
}
channel := channels[pkt.Channel]
if channel == nil {
Panicf("Unknown channel %v", pkt.Channel)
}
channel.recvQueue <- pkt
default:
Panicf("Unknown message type %v", pktType)
}
c.pingDebouncer.Reset()
}
log.Tracef("%v recvHandler done", c)
// cleanup
close(c.pong)
for _ = range c.pong {
// drain
}
2014-06-18 20:48:32 -07:00
}
/* IOStats */
type IOStats struct {
2014-07-01 14:50:24 -07:00
TimeConnected Time
LastSent Time
LastRecv Time
BytesRecv UInt64
BytesSent UInt64
PktsRecv UInt64
PktsSent UInt64
2014-06-18 20:48:32 -07:00
}