Golang--TCP Server (2)

Source: Internet
Author: User
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;&AMP;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;&AMP;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} 



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.