Add Default working tcp files
This commit is contained in:
parent
9b6eb1b5d5
commit
1e395f69ef
71
tcpclient/client.go
Normal file
71
tcpclient/client.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package tcpclient
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type ClientConfiguration struct {
|
||||
ErrorResolver func(chan error)
|
||||
}
|
||||
|
||||
func GetDefaultConfig() ClientConfiguration {
|
||||
return ClientConfiguration{
|
||||
ErrorResolver: func(c chan error) {
|
||||
for err := range c {
|
||||
fmt.Printf("DefConfig:Error: %v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
exit chan bool
|
||||
Server net.Conn
|
||||
IsConnected bool
|
||||
//
|
||||
Messages chan []byte
|
||||
ErrorsChannel chan error
|
||||
ErrorResolver func(chan error)
|
||||
}
|
||||
|
||||
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 (c *Client) StartClient(addr string) error {
|
||||
server, err := net.Dial("tcp", 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)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
43
tcpcommand/command.go
Normal file
43
tcpcommand/command.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package tcpcommand
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
// Command is what server/client will RECEIVE
|
||||
Command string
|
||||
// Action is what to DO when such command is RECEIVED
|
||||
Action func([]string, net.Conn)
|
||||
}
|
||||
|
||||
type CommandBundle struct {
|
||||
Commands []Command
|
||||
}
|
||||
|
||||
// implementation is O(n)
|
||||
func (cb *CommandBundle) alreadyHaveCommand(cmd string) bool {
|
||||
for _, command := range cb.Commands {
|
||||
if command.Command == cmd {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var ErrCommandBundleDuplicateCommands = errors.New("Found two commands with the same Command value")
|
||||
|
||||
func CreateCommandBundle(commands []Command) (*CommandBundle, error) {
|
||||
cb := &CommandBundle{
|
||||
Commands: []Command{},
|
||||
}
|
||||
for _, cmd := range commands {
|
||||
// cb.alreadyHaveCommand is O(n) but it's ok since we won't have commandBundle with over 100k commands
|
||||
if cb.alreadyHaveCommand(cmd.Command) {
|
||||
return nil, ErrCommandBundleDuplicateCommands
|
||||
}
|
||||
cb.Commands = append(cb.Commands, cmd)
|
||||
}
|
||||
return cb, nil
|
||||
}
|
110
tcpserver/server.go
Normal file
110
tcpserver/server.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
package tcpserver
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"git.qowevisa.me/Qowevisa/tcpmachine/tcpcommand"
|
||||
)
|
||||
|
||||
type ServerConfiguration struct {
|
||||
MessageEndRune rune
|
||||
MessageSplitRune rune
|
||||
HandleClientFunc func(client net.Conn)
|
||||
//
|
||||
ErrorResolver func(chan error)
|
||||
}
|
||||
|
||||
func CreateHandleClientFuncFromCommands(bundle *tcpcommand.CommandBundle, conf ServerConfiguration) (func(client net.Conn), chan error) {
|
||||
clientErrors := make(chan error, 16)
|
||||
return func(client net.Conn) {
|
||||
connReader := bufio.NewReader(client)
|
||||
for {
|
||||
msg, err := connReader.ReadString(byte(conf.MessageEndRune))
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
clientErrors <- err
|
||||
}
|
||||
msgWoNl := strings.Trim(msg, string(conf.MessageEndRune))
|
||||
parts := strings.Split(msgWoNl, string(conf.MessageSplitRune))
|
||||
for _, cmd := range bundle.Commands {
|
||||
if cmd.Command == parts[0] {
|
||||
cmd.Action(parts[1:], client)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}, clientErrors
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
HandleClientFunc func(client net.Conn)
|
||||
Exit chan bool
|
||||
//
|
||||
ErrorsChannel chan error
|
||||
ErrorResolver func(chan error)
|
||||
}
|
||||
|
||||
// HandleClientFunc is NOT created by this function
|
||||
// see: CreateHandleClientFuncFromCommands(bundle)
|
||||
func GetDefaultConfig() ServerConfiguration {
|
||||
return ServerConfiguration{
|
||||
ErrorResolver: func(c chan error) {
|
||||
for err := range c {
|
||||
log.Printf("DefConfig:Error: %v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateServer(conf ServerConfiguration) *Server {
|
||||
return &Server{
|
||||
HandleClientFunc: conf.HandleClientFunc,
|
||||
Exit: make(chan bool, 1),
|
||||
//
|
||||
ErrorsChannel: make(chan error, 8),
|
||||
ErrorResolver: conf.ErrorResolver,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) StartServer(address string) error {
|
||||
if s.Exit == nil {
|
||||
return fmt.Errorf("server's Exit channel is nil. Can't start server\n")
|
||||
}
|
||||
listener, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("net.Listen: %w", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
if s.ErrorsChannel == nil {
|
||||
return fmt.Errorf("server's ErrorsChannel is nil. Can't start server\n")
|
||||
}
|
||||
if s.ErrorResolver == nil {
|
||||
return fmt.Errorf("server's ErrorResolver is nil. Can't start server\n")
|
||||
}
|
||||
go s.ErrorResolver(s.ErrorsChannel)
|
||||
log.Printf("Server started listening on %s\n", address)
|
||||
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-s.Exit:
|
||||
break loop
|
||||
default:
|
||||
newCLient, err := listener.Accept()
|
||||
if err != nil {
|
||||
s.ErrorsChannel <- fmt.Errorf("listener.Accept: %w", err)
|
||||
break
|
||||
}
|
||||
go s.HandleClientFunc(newCLient)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user