This is a creation in Article, where the information may have evolved or changed.
TCP Server
Includes logs, timed processing, broadcasts, timeouts
Map Write added lock (read without lock)
Added a decoder
Addr-buf map removed, add delete lock
Mark: Listen to the big God today to deal with the system interruption eintr, and then do the simple processing eintr--retry
Mark: Encapsulates addr, netwith a struct. Listener, exit (whether disconnected) and other information: The most important thing is to use:
BR := Bufio.Newreader(Conn), BW := Bufio.Newwriter(Conn) to replace the read loop so that it can be read/written when needed
Https://github.com/zhangpeihao/gortmp/blob/master/server.go
package mainimport ( "bytes" "Encoding/binary" "FMT" "log" "NET" "OS" "StrConv" "strings" "Sync" "Time") Func main () { tcpstart (8889)}/* Defining related locks */ var ( connmkmutex sync. Mutex conndelmutex sync. Mutex)/* define Logger*/var logger *log. logger/* initializes the Log*/func initlog (Logfile *os. File) { // logger = log. New (logfile, "log:", log. Ldate|log. Ltime) logger = log. New (logfile, "prefix ", 0)}/* processing log*/func dolog (args ...interface{}) { str := time. Now (). Format ("2006-01-02 15:04:05") var logData string var temp string for _, arg := range args { switch val := arg. (type) { case int: temp = strconv. Itoa (val) case string: temp = val } if len (temp) > 64 { // Limit print only the first 64 characters logdata = temp[ : 64] } else { logdata = temp } str = str + " " + logdata } logger. Println (str)}/* defines socket conn mapping */var clisconnmap map[string]*net. tcpconn/* Initialize socket conn mapping */func initclisconnmap () { clisconnmap = make (map[string]*net. Tcpconn)}/* Establish socket conn mapping */func mkclisconn (key string, conn *net. Tcpconn) { connmkmutex.lock () defer connmkmutex.unlock () clisConnMap[key] = conn}/* Delete socket conn map */ Func delclisconn (key string) { conndelmutex.lock () defer&nBsp;conndelmutex.unlock () delete (clisconnmap, key)}/* definition decoder */type Unpacker struct { // Head (XY) 2bytes + identification 1byte + Package length 2bytes + data // Of course, the head could not be XY, here for example, and generally also need to escape _ buf []byte}func (Unpacker *unpacker) feed (data []byte) { Unpacker._buf = append (Unpacker._buf, data ...)} func (Unpacker *unpacker) unpack () (flag byte, msg []byte) { str := string (UNPACKER._BUF) for { if len (str) < 5 { break } else { _, head, data := partition (str, "XY") if len (head) == 0 { // no head if str[len (str) -1] == byte ( { // 120 => ' x ' unpacker._buf = []byte{byte (} ) } else { unpacker._buf = []byte{} } break } buf := bytes. Newreader ([]byte (data)) msg = make ([]byte, buf. Len ()) var datalen uint16 binary. Read (buf, binary. Littleendian, &flag) binary. Read (buf, binary. Littleendian, &datalen) fmt. Println ("DEC:", flag, datalen) if buf. Len () < int (datalen) { break } binary. Read (buf, binary. LITTLEENDIAN,&NBSP;&MSG) unpacker._ buf = unpacker._buf[2+1+2+datalen:] } } return}/* Start Service */func tcpstart (Port int) { initlog (OS. Stderr) initclisconnmap () dolog ("Tcpstart:") host := ":" + strconv. Itoa (port) tcpaddr, err := net. RESOLVETCPADDR ("TCP4", host) checkerror (err) listener, err := net. LISTENTCP ("TCP", &NBSP;TCPADDR) checkerror (err) For { conn, err := listener. ACCEPTTCP () if err != nil { continue } go handleclient (conn) }}/* socket conn*/func handleclient (conn *net. Tcpconn) { // **** Here is the initialization of the connection handling addr := conn. Remoteaddr (). String () dolog ("Handleclient:", addr) connectionmade (conn) request := make ([]byte, 128) defer conn. Close () buf := make ([]byte, 0) for { // **** here is the read loopHandling readloophandled (conn) read_len, err := conn. Read (Request) if err != nil { // here do not use checkerror because do not exit, just break out dolog ("ERR:", "Read err", err. Error ()) break } if read_len == 0 { // in GPRS data can not be determined whether to disconnect, through the heartbeat packet dolog ("ERR:", "connection already closed by client") break } else { // request[:read_len] Processing buf = append (Buf, request[:read_len] ...) dolog ("<=", addr, string ( Request[:read_len]) datareceived (conn, &NBSP;&BUF) request = Make ([]byte, 128) // clear last read content } } // **** here is the connection disconnect processing Connectionlost (conn)}/* Connection Initial processing (ed) */func connectionmade (Conn *net. Tcpconn) { //Initialize connection This function is called // **** Establish conn map addr := conn. Remoteaddr (). String () ip := strings. Split (addr, ":") [0] mkclisconn (Ip, conn) dolog (" Connectionmade: ", addr) // **** timing processing (heartbeat, etc.) go Loopingcall (conn)}/* read-cycle Processing (ed) */func readloophandled (Conn *net. Tcpconn) { //When entering the loop read data This function is called, primarily used to set timeouts (good refresh settings timeout) // ***** set timeout (to be written in the For Loop) setreadtimeout (Conn, 10*time. Minute)}/* A client connection sent to the message processing (ed) */func datareceived (Conn *net. Tcpconn, pbuf *[]byte) { //General conditions can be used pBuf parameters, but if there is a sub-package of the case, you must use Clisbufmap buf The buf of //clisbufmap is constantly increasing and should be handled //addr := conn whether or not it is used. Remoteaddr (). String () dolog ("*pbuf:", string (*PBUF)) &NBSP;&NBSP;&NBsp; //senddata (clisconnmap["192.168.6.234"], []byte ("xxx")) senddata (conn, []byte ("echo"))}/* Connection Disconnect (ed) */func connectionlost (Conn *net. Tcpconn) { //Disconnect This function is called addr := conn. Remoteaddr (). String () ip := strings. Split (addr, ":") [0] delclisconn (IP) // Delete clismap entry corresponding to closed connection dolog ("Connectionlost:", addr)}/* send data */func senddata (conn *net. Tcpconn, data []byte) (n int, err error) { addr := conn. Remoteaddr (). String () n, err = conn. Write (data) if err == nil { dolog ("= =", addr, string (data)) } return}/* Broadcast Data */func broadcast (tclismap map[string]*net. Tcpconn, data []byte) { for _, conn := range Tclismap { senddata (Conn, data) }}/* Timing Processing & delay processing */func loopingcall (conn *net. Tcpconn) { pingticker := time. Newticker (30 * time. Second) // timing testafter := time. After (5 * time. Second) // Delay for { select { case <-pingticker.c: //Send Heartbeat _, err := senddata (conn, [] Byte ("PING ")) if err != nil { Pingticker.stop () return } case <-testAfter: dolog ("Testafter:") } }}/* Set Read data timeout */func setreadtimeout (conn *net. Tcpconn, t time. Duration) { conn. Setreaddeadline (time. Now (). ADD (t))}/* error handling */func checkerror (err error) { if Err != nil { dolog ("ERR: ", err. Error ()) os. Exit (1) }}func partition (s string, sep string) (head string, retsep string, tail string) { // partition (S, &NBSP;SEP) -> (head, sep, tail) index := strings. Index (S,&NBSP;SEP) if index == -1 { head = s retSep = "" tail = "" } else { head = s[:index] retsep = sep tail = s[len (head) +len (Sep):] } return}