func (c *BaseConn) ReadMessage() (string, error) {
	if c.isTerminated() {
		return "", ErrorTerminated
	}
	msg, err := c.reader.ReadLine()
	if err != nil {
		if c.isTerminated() {
			return "", ErrorTerminated
		}
		return "", err
	}
	return string(msg[:len(msg)-1]), nil
}
ReadMessage reads a message.
package protocommon
import (
	"fmt"
	"io"
	"net"
	"sync/atomic"
	"time"
	"github.com/aler9/go-dc/lineproto"
	"github.com/aler9/dctk/pkg/log"
)
const (
	connReadTimeout  = 60 * time.Second
	connWriteTimeout = 10 * time.Second
)
const ReStrNick = "[^\\$ \\|\n]+"
const ReStrIP = "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"
const ReStrPort = "[0-9]{1,5}"
var ErrorTerminated = fmt.Errorf("terminated")
type MsgDecodable interface{}
type MsgEncodable interface{}
type monitoredConnIntf interface {
	PullReadCounter() uint
	PullWriteCounter() uint
}
type MsgBinary struct {
	Content []byte
}
type BaseConn struct {
	logLevel    log.Level
	remoteLabel string
	terminated  uint32  
	msgDelim    byte
	sendChan    chan []byte
	closer      io.Closer
	monitoredConnIntf
	reader       *lineproto.Reader
	writer       *lineproto.Writer
	binaryMode   bool
	syncMode     bool
	writerJoined chan struct{}
}
func NewBaseConn(logLevel log.Level,
	remoteLabel string,
	nconn net.Conn,
	applyReadTimeout bool,
	applyWriteTimeout bool,
	msgDelim byte) *BaseConn {
	readTimeout := func() time.Duration {
		if applyReadTimeout {
			return connReadTimeout
		}
		return 0
	}()
	writeTimeout := func() time.Duration {
		if applyWriteTimeout {
			return connWriteTimeout
		}
		return 0
	}()
	tc := newTimedConn(nconn, readTimeout, writeTimeout)
	mc := newMonitoredConn(tc)
	rdr := lineproto.NewReader(mc, msgDelim)
	wri := lineproto.NewWriter(mc)
	c := &BaseConn{
		logLevel:          logLevel,
		remoteLabel:       remoteLabel,
		msgDelim:          msgDelim,
		writerJoined:      make(chan struct{}),
		closer:            mc,
		monitoredConnIntf: mc,
		reader:            rdr,
		writer:            wri,
		sendChan:          make(chan []byte),
	}
	go c.writeReceiver()
	return c
}
func (c *BaseConn) Close() error {
	if !atomic.CompareAndSwapUint32(&c.terminated, 0, 1) {
		return nil  
	}
	c.closer.Close()
	if !c.syncMode {
		close(c.sendChan)
		<-c.writerJoined
	}
	return nil
}
func (c *BaseConn) isTerminated() bool {
	return atomic.LoadUint32(&c.terminated) != 0
}
func (c *BaseConn) LogLevel() log.Level {
	return c.logLevel
}
func (c *BaseConn) RemoteLabel() string {
	return c.remoteLabel
}
func (c *BaseConn) BinaryMode() bool {
	return c.binaryMode
}
func (c *BaseConn) SetSyncMode(val bool) {
	if val == c.syncMode {
		return
	}
	c.syncMode = val
	if val {
		close(c.sendChan)
		<-c.writerJoined
	} else {
		c.sendChan = make(chan []byte)
		go c.writeReceiver()
	}
}
func (c *BaseConn) SetBinaryMode(val bool) {
	c.binaryMode = val
}