Before learning HTTP and TCP requests often see a noun is a long connection, has been very curious how to achieve, recently occasionally saw an article written by the IM system, want to reprint learning.
Im system, it is necessary to maintain the TCP long connection, because the base library of Golang itself and the external dependent library is very much, we can simply reference the base Net network library to establish TCP server. The general TCP server-side model, can have a co-process "or thread" to execute the accept independently, and is a for loop has been accept new connections, if there is a new connection, then establish a connection and execute connect, because the Golang inside the cost of the process is very small, Therefore, the TCP server side can also connect one goroutine to iterate through the data on the respective connection link and process it. Of course, in the C + + language of the TCP server model, generally through the Epoll model to build server side, this is the difference between C + +.
With regard to reading data, the Linux system has recv and send functions to read the sending data, and in Golang, it comes with an IO library, which encapsulates a variety of reading and writing methods, such as IO. Readfull, which reads data of the specified byte length
In order to maintain the connection and the user, and a connection to a user's one by one corresponding, need to be able to find the user according to the connection, but also need to be able to find the corresponding connection, then need to design a good structure to maintain. We initially used a map to manage, but found that the data inside the map is too large, the performance of the search is not high, to optimize the data structure, Conn contains user,user contains conn, the structure is as follows "only important fields."
// 一个用户对应一个连接type User struct { uid int64 conn *MsgConn BKicked bool // 被另外登陆的一方踢下线 BHeartBeatTimeout bool // 心跳超时}type MsgConn struct { conn net.Conn lastTick time.Time // 上次接收到包时间 remoteAddr string // 为每个连接创建一个唯一标识符 user *User // MsgConn与User一一映射}
Establish the TCP server code snippet as follows
Func Listenandserve (Network, address string) {tcpaddr, err: = Net. RESOLVETCPADDR (Network, address) if err! = Nil {logger. Fatalf (Nil, "Resolvetcpaddr err:%v", err)} listener, err = net. LISTENTCP (Network, TCPADDR) if err! = Nil {logger. Fatalf (Nil, "Listentcp err:%v", err)} Go accept ()}func accept () {for {conn, err: = Listener. ACCEPTTCP () If Err = = Nil {//Packet count, used to limit frequency//anti-attack, black-and-white list//New connection Imconn: = NEWMSGCONN (conn)//Run Imconn. Run ()}}}FUNC (conn *msgconn) run () {//on connect conn.onconnect () go func () {tickerrecv: = Time. Newticker (time. Second * time. Duration (Ratestatinterval)) for {select {case <-conn.stopchan:tickerrecv . Stop () return case <-TICKERRECV.C:CONN.PACKETSRECV = 0 Default: In Conn.parseandhandlEPDU inside the Golang itself through the IO library provides methods to read data, such as IO. Readfull conn_closed: = CONN.PARSEANDHANDLEPDU () if conn_closed {Tickerr Ecv. Stop () Return}}} ()}//corresponds to user and conn one by one func (Conn *msgconn). OnConnect () *user {User: = &user{conn:conn, durationlevel:0, Starttime:time. Now (), Ackwaitmsgidset:make (map[int64]struct{})} conn.user = user return user}