Gosof - A simple and easy golang socket server/client framework

Golang Socket Framework

What

A simple and easy 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

See the example folder for all examples.

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

package custom_msg

// 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
}

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 < 10; 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))
}
Similar Resources

A diffie-hellman socket implementation in golang.

I tried to implement the diffie hellman key exchange in golang with the use of sockets to understand how this protocol works and to learn golang. So its a development project: obviously don't use it in production.

Aug 9, 2022

Package socket provides a low-level network connection type which integrates with Go's runtime network poller to provide asynchronous I/O and deadline support. MIT Licensed.

socket Package socket provides a low-level network connection type which integrates with Go's runtime network poller to provide asynchronous I/O and d

Dec 14, 2022

Encode and Decode Message Length Indicators for TCP/IP socket based protocols

SimpleMLI A Message Length Indicator Encoder/Decoder Message Length Indicators (MLI) are commonly used in communications over raw TCP/IP sockets. This

Nov 24, 2022

Plugs module to see different types of plug types needed in different countries, and a comparison tool between two countries plug socket types.

plugs Importing the module: go get github.com/matthewboyd/plugs "github.com/matthewboyd/plugs" How to use the module: There are two functions wi

Dec 28, 2021

A Socket.IO backend implementation written in Go

go-socket.io The socketio package is a simple abstraction layer for different web browser- supported transport mechanisms. It is fully compatible with

Sep 25, 2022

Web socket in go :book:

websockets-in-go curl -i -G -d "id=UC29ju8bIPH5as8OGnQzwJyA&part=statistics&key=AIzaSyCuhMEgZHU6Epb9rjzKtRRGJY8bLEZjTA8" https://www.googleapis.com/yo

Dec 11, 2022

Demonstration of using Pion WebRTC with a shared socket

pion-webrtc-shared-socket This example demonstrates how Pion WebRTC can use an already listening UDP socket. On startup we listen on UDP Socket 8000.

Apr 4, 2022

Prometheus nginx socket exporter

Prometheus nginx socket exporter

stand-alone version of k8s/ingress-nginx' prometheus exporter based on openresty/lua-nginx-module how it works exporter opens socket (/tmp/prometheus-

Aug 8, 2022

TCP output for beats to send events over TCP socket.

beats-tcp-output How To Use Clone this project to elastic/beats/libbeat/output/ Modify elastic/beats/libbeat/publisher/includes/includes.go : // add i

Aug 25, 2022
GOWS is GoLang web-socket module Provides you with ease of handling web socket connections with a few lines

GOWS GOWS is GoLang web-socket module Provides you with ease of handling web socket connections with a few lines, it supports multi-connection on one

Apr 4, 2022
Glue - Robust Go and Javascript Socket Library (Alternative to Socket.io)

Glue - Robust Go and Javascript Socket Library Glue is a real-time bidirectional socket library. It is a clean, robust and efficient alternative to so

Nov 25, 2022
Golang simple socket framework

go-socket go-socket是一个Go(golang)编写的socket服务器、客户端开发框架包,使用非常简单,里面的两个示例代码验证通过 使用方式

Jun 15, 2022
A Socket Framework Built Using Golang

A Socket Framework Built Using Golang

Jan 20, 2022
Native macOS networking for QEMU using vmnet.framework and socket networking.

qemu-vmnet Native macOS networking for QEMU using vmnet.framework and socket networking. Getting started TODO -netdev socket,id=net0,udp=:1234,localad

Jan 5, 2023
A TCP socket based chat server implemented using Go

Go Chat Server A better TCP socket chat server implemented using Go Connecting nc localhost 5000 Docker Build the container image docker build -t grub

Oct 16, 2021
A golang library about socks5, supports all socks5 commands. That Provides server and client and easy to use. Compatible with socks4 and socks4a.

socks5 This is a Golang implementation of the Socks5 protocol library. To see in this SOCKS Protocol Version 5. This library is also compatible with S

Nov 22, 2022
Simple forwarding a unix domain socket to a local port.

WaziApp Proxy WaziApp proxy is a simple http proxy that is intended to listen on the WaziApp unix socket /var/lib/waziapp/proxy.sock and forwards to a

Nov 18, 2021
Server and client implementation of the grpc go libraries to perform unary, client streaming, server streaming and full duplex RPCs from gRPC go introduction

Description This is an implementation of a gRPC client and server that provides route guidance from gRPC Basics: Go tutorial. It demonstrates how to u

Nov 24, 2021
Golang unix-socket wrapper

Sockunx Golang unix-socket wrapper Server Running server server, e := sockunx.NewServer("/path/to/your/socks.sock", 512) if e != nil { log.Fatal(e

Jan 17, 2022