Compare commits
10 Commits
64e6494ff8
...
6e244be880
Author | SHA1 | Date | |
---|---|---|---|
6e244be880 | |||
7338bc69fa | |||
028dc99fc2 | |||
ad9863d2c8 | |||
e94bbb8901 | |||
6bb8dc19b1 | |||
374e3984ce | |||
2e7e0bf512 | |||
0fecb28b58 | |||
45f08adff3 |
|
@ -1,14 +1,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.qowevisa.me/Qowevisa/tcpmachine/tcpclient"
|
"git.qowevisa.me/Qowevisa/tcpmachine/tcpclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
conf := tcpclient.GetDefaultConfig()
|
client := tcpclient.CreateClient("127.0.0.1:10000")
|
||||||
client := tcpclient.CreateClient(conf)
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
if client.IsConnected {
|
if client.IsConnected {
|
||||||
|
@ -19,8 +19,11 @@ func main() {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
client.Server.Write([]byte("PING\n"))
|
client.Server.Write([]byte("PING\n"))
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
client.ErrorsChannel <- fmt.Errorf("test err")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
go client.ErrorResolver(client.ErrorsChannel)
|
err := client.StartClient()
|
||||||
client.StartClient("127.0.0.1:10000")
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: StartClient: %v\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,26 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.qowevisa.me/Qowevisa/tcpmachine/tcpcommand"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientConfiguration struct {
|
type ClientConfiguration struct {
|
||||||
|
MessageEndRune rune
|
||||||
|
MessageSplitRune rune
|
||||||
|
//
|
||||||
|
Status uint32
|
||||||
ErrorResolver func(chan error)
|
ErrorResolver func(chan error)
|
||||||
|
//
|
||||||
|
ServerHandlerFunc func(server net.Conn)
|
||||||
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDefaultConfig() ClientConfiguration {
|
func GetDefaultConfig() *ClientConfiguration {
|
||||||
return ClientConfiguration{
|
return &ClientConfiguration{
|
||||||
|
MessageEndRune: '\n',
|
||||||
|
MessageSplitRune: ' ',
|
||||||
ErrorResolver: func(c chan error) {
|
ErrorResolver: func(c chan error) {
|
||||||
for err := range c {
|
for err := range c {
|
||||||
fmt.Printf("DefConfig:Error: %v\n", err)
|
fmt.Printf("DefConfig:Error: %v\n", err)
|
||||||
|
@ -22,40 +34,63 @@ func GetDefaultConfig() ClientConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrorResolverFunc func(errors chan error)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
MessageEndRune rune
|
||||||
|
MessageSplitRune rune
|
||||||
|
//
|
||||||
|
addr string
|
||||||
|
Status uint32
|
||||||
exit chan bool
|
exit chan bool
|
||||||
Server net.Conn
|
Server net.Conn
|
||||||
IsConnected bool
|
IsConnected bool
|
||||||
//
|
//
|
||||||
Messages chan []byte
|
ServerHandlerFunc func(server net.Conn)
|
||||||
|
//
|
||||||
ErrorsChannel chan error
|
ErrorsChannel chan error
|
||||||
ErrorResolver func(chan error)
|
ErrorResolver ErrorResolverFunc
|
||||||
|
//
|
||||||
|
Commands []tcpcommand.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateClient(conf ClientConfiguration) *Client {
|
func CreateClient(addr string, options ...ClientOption) *Client {
|
||||||
return &Client{
|
conf := GetDefaultConfig()
|
||||||
Messages: make(chan []byte, 16),
|
|
||||||
|
for _, opt := range options {
|
||||||
|
opt(conf)
|
||||||
|
}
|
||||||
|
c := &Client{
|
||||||
|
MessageEndRune: conf.MessageEndRune,
|
||||||
|
MessageSplitRune: conf.MessageSplitRune,
|
||||||
|
addr: addr,
|
||||||
ErrorResolver: conf.ErrorResolver,
|
ErrorResolver: conf.ErrorResolver,
|
||||||
ErrorsChannel: make(chan error, 8),
|
ErrorsChannel: make(chan error, 8),
|
||||||
exit: make(chan bool, 1),
|
exit: make(chan bool, 1),
|
||||||
|
ServerHandlerFunc: conf.ServerHandlerFunc,
|
||||||
}
|
}
|
||||||
|
if c.ServerHandlerFunc == nil {
|
||||||
|
c.ServerHandlerFunc = GetDefaultServerHandlerFunc(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) StartClient(addr string) error {
|
return c
|
||||||
server, err := net.Dial("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("net.Dial: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ERROR_CLIENT_ERRRSL_NIL = errors.New("Error Resolver is nil")
|
||||||
|
ERROR_CLIENT_ERRCHL_NIL = errors.New("Error Channel is nil")
|
||||||
|
ERROR_CLIENT_SRVHND_NIL = errors.New("Server Handler Func is nil")
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetDefaultServerHandlerFunc(c *Client) func(server net.Conn) {
|
||||||
|
return func(server net.Conn) {
|
||||||
serverReader := bufio.NewReader(server)
|
serverReader := bufio.NewReader(server)
|
||||||
c.IsConnected = true
|
|
||||||
c.Server = server
|
|
||||||
loop:
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.exit:
|
case <-c.exit:
|
||||||
break loop
|
return
|
||||||
default:
|
default:
|
||||||
msg, err := serverReader.ReadString('\n')
|
rawMsg, err := serverReader.ReadString(byte(c.MessageEndRune))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
c.exit <- true
|
c.exit <- true
|
||||||
|
@ -63,9 +98,65 @@ loop:
|
||||||
}
|
}
|
||||||
c.ErrorsChannel <- fmt.Errorf("serverReader.ReadString: %w", err)
|
c.ErrorsChannel <- fmt.Errorf("serverReader.ReadString: %w", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Server send us a message: %s", msg)
|
fmt.Printf("Server send us a message: %s", rawMsg)
|
||||||
c.Messages <- []byte(msg)
|
msg := strings.TrimRight(rawMsg, string(c.MessageEndRune))
|
||||||
|
parts := strings.Split(msg, string(c.MessageSplitRune))
|
||||||
|
// ???
|
||||||
|
if len(parts) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cmd := parts[0]
|
||||||
|
found := false
|
||||||
|
for _, _cmd := range c.Commands {
|
||||||
|
if cmd == _cmd.Command {
|
||||||
|
found = true
|
||||||
|
_cmd.Action(parts[1:], server)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !found {
|
||||||
|
fmt.Printf("Command %s was not handled\n", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) StartClient() error {
|
||||||
|
if c.Status&statusBitCustomErrorHandling == 0 {
|
||||||
|
if c.ErrorResolver == nil {
|
||||||
|
return fmt.Errorf("Can't start client: %w", ERROR_CLIENT_ERRRSL_NIL)
|
||||||
|
}
|
||||||
|
if c.ErrorResolver == nil {
|
||||||
|
return fmt.Errorf("Can't start client: %w", ERROR_CLIENT_ERRCHL_NIL)
|
||||||
|
}
|
||||||
|
go c.ErrorResolver(c.ErrorsChannel)
|
||||||
|
}
|
||||||
|
if c.ServerHandlerFunc == nil {
|
||||||
|
return fmt.Errorf("Can't start client: %w", ERROR_CLIENT_SRVHND_NIL)
|
||||||
|
}
|
||||||
|
server, err := net.Dial("tcp", c.addr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("net.Dial: %w", err)
|
||||||
|
}
|
||||||
|
c.IsConnected = true
|
||||||
|
c.Server = server
|
||||||
|
c.ServerHandlerFunc(server)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandDuplicateError = errors.New("Command already exists in server")
|
||||||
|
var commandNotHandledError = errors.New("Command was not handled")
|
||||||
|
|
||||||
|
func (c *Client) On(command string, action func(args []string, server net.Conn)) error {
|
||||||
|
for _, cmd := range c.Commands {
|
||||||
|
if cmd.Command == command {
|
||||||
|
return fmt.Errorf("Failed addding command %s: %w ", command, commandDuplicateError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Commands = append(c.Commands, tcpcommand.Command{
|
||||||
|
Command: command,
|
||||||
|
Action: action,
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
23
tcpclient/options.go
Normal file
23
tcpclient/options.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package tcpclient
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type ClientOption func(conf *ClientConfiguration)
|
||||||
|
|
||||||
|
const (
|
||||||
|
statusBitNothing = 0
|
||||||
|
statusBitCustomErrorHandling = 1 << iota
|
||||||
|
)
|
||||||
|
|
||||||
|
func WithCustomErrorHandling(fun ErrorResolverFunc) ClientOption {
|
||||||
|
return func(conf *ClientConfiguration) {
|
||||||
|
conf.Status |= statusBitCustomErrorHandling
|
||||||
|
conf.ErrorResolver = fun
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithServerHandler(fun func(server net.Conn)) ClientOption {
|
||||||
|
return func(conf *ClientConfiguration) {
|
||||||
|
conf.ServerHandlerFunc = fun
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ package tcpserver
|
||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
type ServerOption func(*ServerConfiguration)
|
type ServerOption func(conf *ServerConfiguration)
|
||||||
|
|
||||||
// WithMessageEndRune sets the MessageEndRune in the server configuration.
|
// WithMessageEndRune sets the MessageEndRune in the server configuration.
|
||||||
func WithMessageEndRune(r rune) ServerOption {
|
func WithMessageEndRune(r rune) ServerOption {
|
||||||
|
@ -31,3 +31,9 @@ func WithHandleClientFunc(handler func(client net.Conn)) ServerOption {
|
||||||
conf.HandleClientFunc = handler
|
conf.HandleClientFunc = handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithLoggingLevel(level ServerLoggingLevel) ServerOption {
|
||||||
|
return func(conf *ServerConfiguration) {
|
||||||
|
conf.LogLevel = level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,10 +12,23 @@ import (
|
||||||
"git.qowevisa.me/Qowevisa/tcpmachine/tcpcommand"
|
"git.qowevisa.me/Qowevisa/tcpmachine/tcpcommand"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ServerLoggingLevel int
|
||||||
|
|
||||||
|
const (
|
||||||
|
LogLevel_Nothing = 0
|
||||||
|
LogLevel_Connection = 1 << iota
|
||||||
|
LogLevel_Messages
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LogLevel_ALL = LogLevel_Connection | LogLevel_Messages
|
||||||
|
)
|
||||||
|
|
||||||
type ServerConfiguration struct {
|
type ServerConfiguration struct {
|
||||||
MessageEndRune rune
|
MessageEndRune rune
|
||||||
MessageSplitRune rune
|
MessageSplitRune rune
|
||||||
HandleClientFunc func(client net.Conn)
|
HandleClientFunc func(client net.Conn)
|
||||||
|
LogLevel ServerLoggingLevel
|
||||||
//
|
//
|
||||||
ErrorResolver func(chan error)
|
ErrorResolver func(chan error)
|
||||||
}
|
}
|
||||||
|
@ -47,19 +60,27 @@ func CreateHandleClientFuncFromCommands(bundle *tcpcommand.CommandBundle, conf S
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
addr string
|
addr string
|
||||||
|
PreHandlerClientFunc func(client net.Conn)
|
||||||
HandleClientFunc func(client net.Conn)
|
HandleClientFunc func(client net.Conn)
|
||||||
|
// Use PostHandlerClientFunc in your HandleClientFunc if you
|
||||||
|
// use custom HandleClientFunc
|
||||||
|
PostHandlerClientFunc func(client net.Conn)
|
||||||
Exit chan bool
|
Exit chan bool
|
||||||
//
|
//
|
||||||
MessageEndRune rune
|
MessageEndRune rune
|
||||||
MessageSplitRune rune
|
MessageSplitRune rune
|
||||||
ErrorsChannel chan error
|
ErrorsChannel chan error
|
||||||
ErrorResolver func(chan error)
|
ErrorResolver func(chan error)
|
||||||
|
LogLevel ServerLoggingLevel
|
||||||
//
|
//
|
||||||
Commands []tcpcommand.Command
|
Commands []tcpcommand.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultHandleClientFunc(server *Server) func(net.Conn) {
|
func defaultHandleClientFunc(server *Server) func(net.Conn) {
|
||||||
return func(client net.Conn) {
|
return func(client net.Conn) {
|
||||||
|
if server.PostHandlerClientFunc != nil {
|
||||||
|
defer server.PostHandlerClientFunc(client)
|
||||||
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
connReader := bufio.NewReader(client)
|
connReader := bufio.NewReader(client)
|
||||||
for {
|
for {
|
||||||
|
@ -73,6 +94,9 @@ func defaultHandleClientFunc(server *Server) func(net.Conn) {
|
||||||
msgWoNl := strings.Trim(msg, string(server.MessageEndRune))
|
msgWoNl := strings.Trim(msg, string(server.MessageEndRune))
|
||||||
parts := strings.Split(msgWoNl, string(server.MessageSplitRune))
|
parts := strings.Split(msgWoNl, string(server.MessageSplitRune))
|
||||||
commandFound := false
|
commandFound := false
|
||||||
|
if server.LogLevel&LogLevel_Messages > 0 {
|
||||||
|
log.Printf("Message received from %s : %s\n", client.RemoteAddr(), msgWoNl)
|
||||||
|
}
|
||||||
for _, cmd := range server.Commands {
|
for _, cmd := range server.Commands {
|
||||||
if cmd.Command == parts[0] {
|
if cmd.Command == parts[0] {
|
||||||
cmd.Action(parts[1:], client)
|
cmd.Action(parts[1:], client)
|
||||||
|
@ -87,7 +111,7 @@ func defaultHandleClientFunc(server *Server) func(net.Conn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleClientFunc is NOT created by this function
|
// NOTE: HandleClientFunc is NOT created by this function
|
||||||
// see: CreateHandleClientFuncFromCommands(bundle)
|
// see: CreateHandleClientFuncFromCommands(bundle)
|
||||||
func GetDefaultConfig() ServerConfiguration {
|
func GetDefaultConfig() ServerConfiguration {
|
||||||
return ServerConfiguration{
|
return ServerConfiguration{
|
||||||
|
@ -118,6 +142,7 @@ func CreateServer(addr string, options ...ServerOption) *Server {
|
||||||
MessageSplitRune: conf.MessageSplitRune,
|
MessageSplitRune: conf.MessageSplitRune,
|
||||||
ErrorsChannel: make(chan error, 8),
|
ErrorsChannel: make(chan error, 8),
|
||||||
ErrorResolver: conf.ErrorResolver,
|
ErrorResolver: conf.ErrorResolver,
|
||||||
|
LogLevel: conf.LogLevel,
|
||||||
//
|
//
|
||||||
Commands: cmds,
|
Commands: cmds,
|
||||||
}
|
}
|
||||||
|
@ -158,12 +183,18 @@ loop:
|
||||||
case <-s.Exit:
|
case <-s.Exit:
|
||||||
break loop
|
break loop
|
||||||
default:
|
default:
|
||||||
newCLient, err := listener.Accept()
|
newClient, err := listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.ErrorsChannel <- fmt.Errorf("listener.Accept: %w", err)
|
s.ErrorsChannel <- fmt.Errorf("listener.Accept: %w", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
go s.HandleClientFunc(newCLient)
|
if s.LogLevel&LogLevel_Connection > 0 {
|
||||||
|
fmt.Printf("New Connection from %s is accepted\n", newClient.RemoteAddr())
|
||||||
|
}
|
||||||
|
if s.PreHandlerClientFunc != nil {
|
||||||
|
s.PreHandlerClientFunc(newClient)
|
||||||
|
}
|
||||||
|
go s.HandleClientFunc(newClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -172,6 +203,12 @@ loop:
|
||||||
var commandDuplicateError = errors.New("Command already exists in server")
|
var commandDuplicateError = errors.New("Command already exists in server")
|
||||||
var commandNotHandledError = errors.New("Command was not handled")
|
var commandNotHandledError = errors.New("Command was not handled")
|
||||||
|
|
||||||
|
// On registers an action for a specific command. The action will be executed
|
||||||
|
// when the command is received from a client. For example, given the input
|
||||||
|
// string `TEST 1 2 3 4`, the command would be `TEST`, and the args would be
|
||||||
|
// []string{"1", "2", "3", "4"}.
|
||||||
|
//
|
||||||
|
// NOTE: This behavior applies only if `MessageSplitRune` is set to the default value (' ').
|
||||||
func (s *Server) On(command string, action func(args []string, client net.Conn)) error {
|
func (s *Server) On(command string, action func(args []string, client net.Conn)) error {
|
||||||
for _, cmd := range s.Commands {
|
for _, cmd := range s.Commands {
|
||||||
if cmd.Command == command {
|
if cmd.Command == command {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user