jeremyko / gosof

A simple yet quite practical TCP, UDP and Domain socket framework made with golang.

Home Page:https://jeremyko.github.io/2022/01/01/gosof-simple-and-easy-golang-socket.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Golang SOcket Framework

What

A simple yet quite practical golang socket server/client framework especially convenient for handling TCP fixed-length header and variable-length body using pure net package. Of course, it also supports udp and domain socket.

  • The framework calls the user-specified callback.
  • For TCP, the total size of user data is passed to the framework via a callback, and the framework does TCP buffering automatically.
  • Byte data is exchanged.
  • Supports TCP, UDP and Domain Socket.

Usage

go get github.com/jeremyko/gosof@latest

Example

See the example folder for all examples.

tcp echo server (Fixed-length header and variable-length body)

package custom_msg
import "encoding/binary"
// user specific custom data example

// UserMsgHeader : fixed length header
type UserMsgHeader struct {
	MsgTotalLen uint32 // total length of data -> header + body
	MsgType     [6]byte
	EtcInfo     [20]byte
}

var FixedHeaderSize = binary.Size(UserMsgHeader{}) // or -> const FixedHeaderSize = 4 + 6 + 20

// UserMsgBody : body of dynamic length
type UserMsgBody struct {
	Field1 int
	Field2 string
	Field3 []byte
}
package main

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"encoding/gob"
	custommsg "github.com/jeremyko/gosof/example/tcp/echo_custom_msg/custom_msg_def"
	"github.com/jeremyko/gosof"
	"log"
	"os"
)

var svr gosof.Server

func main() {
	svr.SetInitCompletedCb(onInitCompleted)
	svr.SetNewClientCb(onNewClient)
	svr.SetCalculateDataLenCb(onCalculateDataLen)
	svr.SetCompleteDataCb(onCompleteData)
	svr.SetDisConnectedCB(onClientDisconnected)
	//lc := net.ListenConfig{
	//	Control: func(network, address string, conn syscall.RawConn) error {
	//		var operr error
	//		if err := conn.Control(func(fd uintptr) {
	//			operr = syscall.SetsockoptInt(syscall.Handle(int(fd)),
	//				syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) //for windows !!
	//		}); err != nil {
	//			return err
	//		}
	//		return operr
	//	},
	//}
	//if svr.InitTcpServerListenConfig("tcp4", "127.0.0.1", 9990, &lc) != nil {
	if svr.InitTcpServer("tcp4", "127.0.0.1", 9990) != nil {
		log.Println("error! ", svr.GetLastErrMsg())
		return
	}
	log.Println("press enter to exit")
	input := bufio.NewScanner(os.Stdin) // wait user input to terminate
	input.Scan()
}

//------------------------------------------------------------------------------
//user specific callbacks
//------------------------------------------------------------------------------
func onInitCompleted() {
	log.Println("initialized") //successfully initialized.
}

func onNewClient(ctx *gosof.Context) {
	log.Println("new connection : ", ctx.Conn.RemoteAddr().String())
}

func onClientDisconnected(ctx *gosof.Context, err error) {
	log.Println("client disconnected : ", ctx.Conn.RemoteAddr().String(), " - ", err.Error())
}

// calculate your complete packet length here
// Return 'gosof.NeedMoreInfo' if you haven't received enough information about your data.
func onCalculateDataLen(data []byte, receivedAccumulatedLen int) (gosof.SocketOpFlag, int) {
	if receivedAccumulatedLen < custommsg.FixedHeaderSize {
		// Fixed-length header data has not arrived yet.
		return gosof.NeedMoreInfo, 0
	}
	// get fixed length header
	myMsgHeader := custommsg.UserMsgHeader{}
	binBufHeader := bytes.Buffer{}
	binBufHeader.Write(data[:receivedAccumulatedLen])
	err := binary.Read(&binBufHeader, binary.LittleEndian, &myMsgHeader)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	return gosof.AnalyzedCompleted, int(myMsgHeader.MsgTotalLen)
}

// your whole data has arrived.
// 'data' contains header and actual data.
func onCompleteData(ctx *gosof.Context, data []byte, packetLen int) {
	// header
	myMsgHeader := custommsg.UserMsgHeader{}
	binBufHeader := bytes.Buffer{}
	binBufHeader.Write(data[:custommsg.FixedHeaderSize])
	err := binary.Read(&binBufHeader, binary.LittleEndian, &myMsgHeader)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	// body
	clientMsg := custommsg.UserMsgBody{}
	bufDataOnly := bytes.NewBuffer(data[custommsg.FixedHeaderSize:]) //except header data
	dec := gob.NewDecoder(bufDataOnly)                               // body of dynamic length
	err = dec.Decode(&clientMsg)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	log.Println(ctx.Conn.RemoteAddr().String(), " / client data : type = ", string(myMsgHeader.MsgType[:]),
		" etc info =", string(myMsgHeader.EtcInfo[:]),
		" / data :  ==> ", clientMsg.Field1, ", ", clientMsg.Field2, ", ", string(clientMsg.Field3))

	// this is a simple echo server
	err = svr.SendTcp(ctx, packetLen, data)
	if err != nil {
		log.Println(err.Error())
	}
}

tcp echo client (Fixed-length header and variable-length body)

package main

import (
	"bytes"
	"encoding/binary"
	"encoding/gob"
	"fmt"
	custommsg "github.com/jeremyko/gosof/example/tcp/echo_custom_msg/custom_msg_def"
	"github.com/jeremyko/gosof"
	"log"
	"time"
)

var recvedCnt uint = 0

func main() {
	var client gosof.Client
	client.SetInitCompletedCb(onInitCompleted)
	client.SetServerConnectedCb(onServerConnected)
	client.SetCalculateDataLenCb(onCalculateDataLen)
	client.SetCompleteDataCb(onCompleteData)
	client.SetDisConnectedCB(onServerDisConnected)
	log.SetFlags(log.Llongfile)

	if client.InitTcpClient("tcp", "127.0.0.1", 9990, 10) != nil {
		log.Println("error! ", client.GetLastErrMsg())
		return
	}

	for i := 0; i < 1000; i++ {
		go func(index int) {
			var binBufHeader bytes.Buffer
			var binBufBody bytes.Buffer

			// fixed length header
			myMsgHeader := custommsg.UserMsgHeader{}
			copy(myMsgHeader.MsgType[:], "type1")
			copy(myMsgHeader.EtcInfo[:], "some useful info")

			// body of dynamic length
			myMsg := custommsg.UserMsgBody{}
			myMsg.Field1 = index
			myMsg.Field2 = fmt.Sprintf("TEST %d", index)
			myMsg.Field3 = []byte("byte test")
			encBody := gob.NewEncoder(&binBufBody)
			err := encBody.Encode(myMsg)
			if err != nil {
				log.Fatal("encode error:", err)
			}
			bodyLen := binBufBody.Len()
			myMsgHeader.MsgTotalLen = uint32(custommsg.FixedHeaderSize + bodyLen)
			binary.Write(&binBufHeader, binary.LittleEndian, &myMsgHeader)
			//--------------------------------- send header and real data
			errSend := client.SendToServer(int(myMsgHeader.MsgTotalLen),
				binBufHeader.Bytes(), binBufBody.Bytes())
			if errSend != nil {
				log.Println(errSend.Error())
				return
			}
		}(i)
	} // for

	for {
		time.Sleep(1 * time.Second)
	}
}

//------------------------------------------------------------------------------
//user specific callbacks
//------------------------------------------------------------------------------
func onInitCompleted() {
	log.Println("initialized") //successfully initialized.
}

func onServerConnected(ctx *gosof.Context) {
	log.Println("server connected : ", ctx.Conn.RemoteAddr().String())
}

func onServerDisConnected(ctx *gosof.Context, err error) {
	log.Println("server disconnected : ", ctx.Conn.RemoteAddr().String(), " - ", err.Error())
}

// calculate your complete packet length here
// Return 'gosof.NeedMoreInfo' if you haven't received enough information about your data.
func onCalculateDataLen(data []byte, receivedAccumulatedLen int) (gosof.SocketOpFlag, int) {
	if receivedAccumulatedLen < custommsg.FixedHeaderSize {
		// Fixed-length header data has not arrived yet.
		return gosof.NeedMoreInfo, 0
	}
	// get fixed length header
	myMsgHeader := custommsg.UserMsgHeader{}
	binBufHeader := bytes.Buffer{}
	binBufHeader.Write(data[:custommsg.FixedHeaderSize])
	err := binary.Read(&binBufHeader, binary.LittleEndian, &myMsgHeader)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	return gosof.AnalyzedCompleted, int(myMsgHeader.MsgTotalLen)
}

// your whole data has arrived. The echo response sent by the server has arrived.
// 'data' contains header and actual data.
func onCompleteData(ctx *gosof.Context, data []byte, packetLen int) {
	// header
	myMsgHeader := custommsg.UserMsgHeader{}
	binBufHeader := bytes.Buffer{}
	binBufHeader.Write(data[:custommsg.FixedHeaderSize])
	err := binary.Read(&binBufHeader, binary.LittleEndian, &myMsgHeader)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	// body
	myMsg := custommsg.UserMsgBody{}
	bufDataOnly := bytes.NewBuffer(data[custommsg.FixedHeaderSize:]) //except header data
	dec := gob.NewDecoder(bufDataOnly)                               // body of dynamic length
	err = dec.Decode(&myMsg)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	recvedCnt++
	log.Println(" received count :", recvedCnt, " : len [", packetLen, "] ==> ",
		myMsg.Field1, ", ", myMsg.Field2, ", ", string(myMsg.Field3))
}

About

A simple yet quite practical TCP, UDP and Domain socket framework made with golang.

https://jeremyko.github.io/2022/01/01/gosof-simple-and-easy-golang-socket.html

License:MIT License


Languages

Language:Go 100.0%