对Conn封装的基本思路
go内置了net包已经很好的封装了socket通讯。然而在实际使用中,由于net/Conn的Read/Write方法是堵塞的原因,必须将其放入单独的goroutine中进行处理。
我们先简单的整理下思路,对于连接(Conn)的处理,我们可以开启2条goroutine进行处理,一条用于堵塞的Read的处理,另一条进行Write的处理。
这里必须指出,其实Write本身就是线程安全的,也就是我们在别的任何地方都可以进行Write,但是Write是堵塞的,所以考虑到这点,我们还是将其放入一个单独的goroutine中进行处理。
这样设计的原因在于Conn是支持同时Read/Write的,这样我们的基本的Conn的模型就成型了。对于服务端或者客户端而言,我们只需要封装对应过来的Conn即可,Conn的读写goroutine进行处理,并将获得的事件抛向外部。
那么我们就按这个思路来实现一个简单的Connection封装,该封装支持线程安全的写,并且支持解包操作。
package tcpnetwork
import (
"errors"
"log"
"net"
"time"
)
const (
kConnStatus_None = iota
kConnStatus_Connected
kConnStatus_Disconnected
)
const (
kConnEvent_None = iota
kConnEvent_Connected
kConnEvent_Disconnected
kConnEvent_Data
kConnEvent_Close
)
const (
kConnConf_DefaultSendTimeoutSec = 5
kConnConf_MaxReadBufferLength = 0xffff // 0xffff
)
type Connection struct {
conn net.Conn
status int
connId int
sendMsgQueue chan []byte
sendTimeoutSec int
eventQueue IEventQueue
streamProtocol IStreamProtocol
maxReadBufferLength int
userdata interface{}
from int
readTimeoutSec int
}
func newConnection(c net.Conn, sendBufferSize int, eq IEventQueue) *Connection {
return &Connection{
conn: c,
status: kConnStatus_None,
connId: 0,
sendMsgQueue: make(chan []byte, sendBufferSize),
sendTimeoutSec: kConnConf_DefaultSendTimeoutSec,
maxReadBufferLength: kConnConf_MaxReadBufferLength,
eventQueue: eq,
}
}
type ConnEvent struct {
EventType int
Conn *Connection
Data []byte
}
func newConnEvent(et int, c *Connection, d []byte) *ConnEvent {
return &ConnEvent{
EventType: et,
Conn: c,
Data: d,
}
}
// directly close, packages in queue will not be sent
func (this *Connection) close() {
if kConnStatus_Connected != this.status {
return
}
this.conn.Close()
this.status = kConnStatus_Disconnected
}
func (this *Connection) Close() {
if this.status != kConnStatus_Connected {
return
}
select {
case this.sendMsgQueue <- nil:
{
// nothing