Fragment Description:



A TCP Echo Server (must be read along with the tcpEchoClient example).
Launch this 'tcpEchoServer' (in one terminal), then launch a tcpEchoClient or many such tcpEchoClients (in other terminals).
See package 'gotcp' by Jie Li (gansidui), it is worth a reading! see 'github.com/gansidui/gotcp' Also, the work by By Jan Newmarch is an excellent concise introduction.
(see his excellent ebook:
https://jan.newmarch.name/go/ ) The book to have about TCP/IP:
'TCP/IP illustrated volume1 the protocols' by K.R.Fall and W.
Richard Stevens (my personal appreciation)

tcpEchoServer

Last update, on 2015, Mon 12 Oct, 09:41:57

/* ... <== see fragment description ... */

package main

import (
    "encoding/binary"
    "errors"
    "fmt"
    "github.com/gansidui/gotcp"
    "io"
    "log"
    "net"
    "os"
    "os/signal"
    "runtime"
    "syscall"
    "time"
)

// //////////////////////////////////////
// Echo custom Packet protocol: how we deal with Packet
// 2 Types and their methods
// The code that follows is there as a learning facility (see Server)
// it should be written in a specific file, part of the package
//
type EchoPacket struct {
    buff []byte
}

func (epk *EchoPacket) Serialize() []byte {
    return epk.buff
}
func (epk *EchoPacket) GetLength() uint32 {
    return binary.BigEndian.Uint32(epk.buff[0:4])
}
func (epk *EchoPacket) GetBody() []byte {
    return epk.buff[4:]
}
func NewEchoPacket(buff []byte, hasLengthField bool) *EchoPacket {
    p := &EchoPacket{}
    if hasLengthField {
        p.buff = buff
    } else {
        p.buff = make([]byte, 4+len(buff))
        binary.BigEndian.PutUint32(p.buff[0:4], uint32(len(buff)))
        copy(p.buff[4:], buff)
    }
    return p
}

type EchoProtocol struct {
}

func (epl *EchoProtocol) ReadPacket(conn *net.TCPConn) (gotcp.Packet, error) {
    var (
        lengthBytes []byte = make([]byte, 4)
        length      uint32
    )
    // read length, io.ReadFull deals with EOF
    if _, err := io.ReadFull(conn, lengthBytes); err != nil {
        return nil, err
    }
    /* type ByteOrder interface {
               Uint16([]byte) uint16
               Uint32([]byte) uint32
               Uint64([]byte) uint64
               PutUint16([]byte, uint16)
               PutUint32([]byte, uint32)
               PutUint64([]byte, uint64)
               String() string
           }
   */
    // BigEndian is the big-endian implementation of ByteOrder.
    //
    if length = binary.BigEndian.Uint32(lengthBytes); length > 1024 {
        return nil, errors.New("the size of packet is larger than the limit")
    }
    buff := make([]byte, 4+length)
    copy(buff[0:4], lengthBytes)
    // read body ( buff = lengthBytes + body )
    if _, err := io.ReadFull(conn, buff[4:]); err != nil {
        return nil, err
    }
    return NewEchoPacket(buff, true), nil
}

//
// End of Echo custom protocol
// //////////////////////////////////////
type Callback struct{}

func (cb *Callback) OnConnect(c *gotcp.Conn) bool {
    addr := c.GetRawConn().RemoteAddr()
    c.PutExtraData(addr)
    fmt.Println("OnConnect:", addr)
    return true
}
func (cb *Callback) OnMessage(c *gotcp.Conn, p gotcp.Packet) bool {
    echoPacket := p.(*EchoPacket)
    message := "OnMessage: received packet length is[%v]\t" +
        "received packet Body is [%v]\n"
    fmt.Printf(message, echoPacket.GetLength(), string(echoPacket.GetBody()))
    c.AsyncWritePacket(NewEchoPacket(echoPacket.Serialize(), true), time.Second)
    return true
}
func (cb *Callback) OnClose(c *gotcp.Conn) {
    fmt.Println("OnClose:", c.GetExtraData())
}
func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    // creates a tcp listener
    tcpAddr, err := net.ResolveTCPAddr("tcp4", ":8989")
    checkError(err)
    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)
    // creates a server
    config := &gotcp.Config{
        PacketSendChanLimit:    20,
        PacketReceiveChanLimit: 20,
    }
    srv := gotcp.NewServer(config, &Callback{}, &EchoProtocol{})
    // starts service
    go srv.Start(listener, time.Second)
    fmt.Println("listening:", listener.Addr())
    // catchs system signal
    chSig := make(chan os.Signal)
    signal.Notify(chSig, syscall.SIGINT, syscall.SIGTERM)
    fmt.Println("Signal: ", <-chSig)
    // stops service
    srv.Stop()
}
func checkError(err error) {
    if err != nil {
        log.Fatal(err)
    }
}



Comments