Fragment Description:



A TCP Echo Client example (must be read along with the tcpEchoServer 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)

tcpEchoClient

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

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

package main

import (
    "encoding/binary"
    "errors"
    "fmt"
    "github.com/gansidui/gotcp"
    "io"
    "log"
    "net"
    "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
// //////////////////////////////////////
func main() {
    // resolves a pair of domain name and port name on the network net,
    // which must be "tcp", "tcp4" or "tcp6"
    tcpAddr, err := net.ResolveTCPAddr("tcp4", "127.0.0.1:8989")
    checkError(err)
    // A client DialTCP
    // sign: DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)
    // DialTCP connects to the remote address raddr on the network net
    // If laddr is not nil, it is used as the local address for the connection.
    conn, err := net.DialTCP("tcp", nil, tcpAddr)
    checkError(err)
    echoProtocol := &EchoProtocol{}
    // ping <--> pong
    for i := 0; i < 3; i++ {
        // write
        conn.Write(NewEchoPacket([]byte("Hello, I am the client"), false).Serialize())
        // read the Packet as specified in the Echo custom Packet protocol
        p, err := echoProtocol.ReadPacket(conn)
        if err == nil {
            echoPacket := p.(*EchoPacket)
            message := "Server reply: returned packet length[%v]bytes\t" +
                "returned packet body is[%v]\n"
            fmt.Printf(message,
                echoPacket.GetLength(), string(echoPacket.GetBody()))
        }
        time.Sleep(2 * time.Second)
    }
    // When we no longer need this resource we must release it
    conn.Close()
}
func checkError(err error) {
    if err != nil {
        log.Fatal(err)
    }
}



Comments