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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.qowevisa.me/Qowevisa/tcpmachine/tcpclient"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conf := tcpclient.GetDefaultConfig()
|
||||
client := tcpclient.CreateClient(conf)
|
||||
client := tcpclient.CreateClient("127.0.0.1:10000")
|
||||
go func() {
|
||||
for {
|
||||
if client.IsConnected {
|
||||
|
@ -19,8 +19,11 @@ func main() {
|
|||
for i := 0; i < 10; i++ {
|
||||
client.Server.Write([]byte("PING\n"))
|
||||
time.Sleep(time.Second)
|
||||
client.ErrorsChannel <- fmt.Errorf("test err")
|
||||
}
|
||||
}()
|
||||
go client.ErrorResolver(client.ErrorsChannel)
|
||||
client.StartClient("127.0.0.1:10000")
|
||||
err := client.StartClient()
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: StartClient: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,26 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"git.qowevisa.me/Qowevisa/tcpmachine/tcpcommand"
|
||||
)
|
||||
|
||||
type ClientConfiguration struct {
|
||||
MessageEndRune rune
|
||||
MessageSplitRune rune
|
||||
//
|
||||
Status uint32
|
||||
ErrorResolver func(chan error)
|
||||
//
|
||||
ServerHandlerFunc func(server net.Conn)
|
||||
//
|
||||
}
|
||||
|
||||
func GetDefaultConfig() ClientConfiguration {
|
||||
return ClientConfiguration{
|
||||
func GetDefaultConfig() *ClientConfiguration {
|
||||
return &ClientConfiguration{
|
||||
MessageEndRune: '\n',
|
||||
MessageSplitRune: ' ',
|
||||
ErrorResolver: func(c chan error) {
|
||||
for err := range c {
|
||||
fmt.Printf("DefConfig:Error: %v\n", err)
|
||||
|
@ -22,50 +34,129 @@ func GetDefaultConfig() ClientConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
type ErrorResolverFunc func(errors chan error)
|
||||
|
||||
type Client struct {
|
||||
MessageEndRune rune
|
||||
MessageSplitRune rune
|
||||
//
|
||||
addr string
|
||||
Status uint32
|
||||
exit chan bool
|
||||
Server net.Conn
|
||||
IsConnected bool
|
||||
//
|
||||
Messages chan []byte
|
||||
ServerHandlerFunc func(server net.Conn)
|
||||
//
|
||||
ErrorsChannel chan error
|
||||
ErrorResolver func(chan error)
|
||||
ErrorResolver ErrorResolverFunc
|
||||
//
|
||||
Commands []tcpcommand.Command
|
||||
}
|
||||
|
||||
func CreateClient(conf ClientConfiguration) *Client {
|
||||
return &Client{
|
||||
Messages: make(chan []byte, 16),
|
||||
ErrorResolver: conf.ErrorResolver,
|
||||
ErrorsChannel: make(chan error, 8),
|
||||
exit: make(chan bool, 1),
|
||||
func CreateClient(addr string, options ...ClientOption) *Client {
|
||||
conf := GetDefaultConfig()
|
||||
|
||||
for _, opt := range options {
|
||||
opt(conf)
|
||||
}
|
||||
c := &Client{
|
||||
MessageEndRune: conf.MessageEndRune,
|
||||
MessageSplitRune: conf.MessageSplitRune,
|
||||
addr: addr,
|
||||
ErrorResolver: conf.ErrorResolver,
|
||||
ErrorsChannel: make(chan error, 8),
|
||||
exit: make(chan bool, 1),
|
||||
ServerHandlerFunc: conf.ServerHandlerFunc,
|
||||
}
|
||||
if c.ServerHandlerFunc == nil {
|
||||
c.ServerHandlerFunc = GetDefaultServerHandlerFunc(c)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
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)
|
||||
for {
|
||||
select {
|
||||
case <-c.exit:
|
||||
return
|
||||
default:
|
||||
rawMsg, err := serverReader.ReadString(byte(c.MessageEndRune))
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
c.exit <- true
|
||||
break
|
||||
}
|
||||
c.ErrorsChannel <- fmt.Errorf("serverReader.ReadString: %w", err)
|
||||
}
|
||||
fmt.Printf("Server send us a message: %s", rawMsg)
|
||||
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(addr string) error {
|
||||
server, err := net.Dial("tcp", addr)
|
||||
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)
|
||||
}
|
||||
serverReader := bufio.NewReader(server)
|
||||
c.IsConnected = true
|
||||
c.Server = server
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-c.exit:
|
||||
break loop
|
||||
default:
|
||||
msg, err := serverReader.ReadString('\n')
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
c.exit <- true
|
||||
break
|
||||
}
|
||||
c.ErrorsChannel <- fmt.Errorf("serverReader.ReadString: %w", err)
|
||||
}
|
||||
fmt.Printf("Server send us a message: %s", msg)
|
||||
c.Messages <- []byte(msg)
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
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"
|
||||
|
||||
type ServerOption func(*ServerConfiguration)
|
||||
type ServerOption func(conf *ServerConfiguration)
|
||||
|
||||
// WithMessageEndRune sets the MessageEndRune in the server configuration.
|
||||
func WithMessageEndRune(r rune) ServerOption {
|
||||
|
@ -31,3 +31,9 @@ func WithHandleClientFunc(handler func(client net.Conn)) ServerOption {
|
|||
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"
|
||||
)
|
||||
|
||||
type ServerLoggingLevel int
|
||||
|
||||
const (
|
||||
LogLevel_Nothing = 0
|
||||
LogLevel_Connection = 1 << iota
|
||||
LogLevel_Messages
|
||||
)
|
||||
|
||||
const (
|
||||
LogLevel_ALL = LogLevel_Connection | LogLevel_Messages
|
||||
)
|
||||
|
||||
type ServerConfiguration struct {
|
||||
MessageEndRune rune
|
||||
MessageSplitRune rune
|
||||
HandleClientFunc func(client net.Conn)
|
||||
LogLevel ServerLoggingLevel
|
||||
//
|
||||
ErrorResolver func(chan error)
|
||||
}
|
||||
|
@ -46,20 +59,28 @@ func CreateHandleClientFuncFromCommands(bundle *tcpcommand.CommandBundle, conf S
|
|||
}
|
||||
|
||||
type Server struct {
|
||||
addr string
|
||||
HandleClientFunc func(client net.Conn)
|
||||
Exit chan bool
|
||||
addr string
|
||||
PreHandlerClientFunc 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
|
||||
//
|
||||
MessageEndRune rune
|
||||
MessageSplitRune rune
|
||||
ErrorsChannel chan error
|
||||
ErrorResolver func(chan error)
|
||||
LogLevel ServerLoggingLevel
|
||||
//
|
||||
Commands []tcpcommand.Command
|
||||
}
|
||||
|
||||
func defaultHandleClientFunc(server *Server) func(net.Conn) {
|
||||
return func(client net.Conn) {
|
||||
if server.PostHandlerClientFunc != nil {
|
||||
defer server.PostHandlerClientFunc(client)
|
||||
}
|
||||
defer client.Close()
|
||||
connReader := bufio.NewReader(client)
|
||||
for {
|
||||
|
@ -73,6 +94,9 @@ func defaultHandleClientFunc(server *Server) func(net.Conn) {
|
|||
msgWoNl := strings.Trim(msg, string(server.MessageEndRune))
|
||||
parts := strings.Split(msgWoNl, string(server.MessageSplitRune))
|
||||
commandFound := false
|
||||
if server.LogLevel&LogLevel_Messages > 0 {
|
||||
log.Printf("Message received from %s : %s\n", client.RemoteAddr(), msgWoNl)
|
||||
}
|
||||
for _, cmd := range server.Commands {
|
||||
if cmd.Command == parts[0] {
|
||||
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)
|
||||
func GetDefaultConfig() ServerConfiguration {
|
||||
return ServerConfiguration{
|
||||
|
@ -118,6 +142,7 @@ func CreateServer(addr string, options ...ServerOption) *Server {
|
|||
MessageSplitRune: conf.MessageSplitRune,
|
||||
ErrorsChannel: make(chan error, 8),
|
||||
ErrorResolver: conf.ErrorResolver,
|
||||
LogLevel: conf.LogLevel,
|
||||
//
|
||||
Commands: cmds,
|
||||
}
|
||||
|
@ -158,12 +183,18 @@ loop:
|
|||
case <-s.Exit:
|
||||
break loop
|
||||
default:
|
||||
newCLient, err := listener.Accept()
|
||||
newClient, err := listener.Accept()
|
||||
if err != nil {
|
||||
s.ErrorsChannel <- fmt.Errorf("listener.Accept: %w", err)
|
||||
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
|
||||
|
@ -172,6 +203,12 @@ loop:
|
|||
var commandDuplicateError = errors.New("Command already exists in server")
|
||||
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 {
|
||||
for _, cmd := range s.Commands {
|
||||
if cmd.Command == command {
|
||||
|
|
Loading…
Reference in New Issue
Block a user