first commit
This commit is contained in:
commit
f0a810c330
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module versestudios.com/go-telnet-asyncio-test
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require github.com/xtaci/gaio v1.2.9
|
260
main.go
Normal file
260
main.go
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtaci/gaio"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
port := flag.Int("port", 3333, "Port to accept connections on.")
|
||||||
|
host := flag.String("host", "127.0.0.1", "Host or IP to bind to")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
w, err := gaio.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
l, err := net.Listen("tcp", *host+":"+strconv.Itoa(*port))
|
||||||
|
if err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
log.Println("Listening to connections at '"+*host+"' on port", strconv.Itoa(*port))
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("new client", conn.RemoteAddr())
|
||||||
|
|
||||||
|
// set up io channel for this connection
|
||||||
|
chIO := make(chan gaio.OpResult)
|
||||||
|
|
||||||
|
// submit the first async write IO request
|
||||||
|
err = (chIO, conn, welcomeHandler())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("err sending welcomeHandler: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// hand off channel to menuHandler
|
||||||
|
menuHandler(chIO, conn, *w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuHandler(chIO chan gaio.OpResult, c net.Conn, w gaio.Watcher) {
|
||||||
|
log.Printf("Connection received: %s", c.RemoteAddr())
|
||||||
|
|
||||||
|
// watcher.WaitIO goroutine
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
results, err := w.WaitIO()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, res := range results {
|
||||||
|
chIO <- res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// send welcome banner
|
||||||
|
if err := welcomeHandler(&c); err != nil {
|
||||||
|
log.Printf("error sending welcomeHandler on initial connect: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
scanner := bufio.NewScanner(c)
|
||||||
|
scanner.Split(bufio.ScanLines)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Bytes()
|
||||||
|
|
||||||
|
log.Printf("Menu received line: %v (%v)", line, string(line))
|
||||||
|
switch string(line) {
|
||||||
|
case "welcome":
|
||||||
|
if err := welcomeHandler(&c); err != nil {
|
||||||
|
log.Printf("error sending welcomeHandler from cmd `welcome`: %v", err)
|
||||||
|
}
|
||||||
|
case "adventure":
|
||||||
|
// start the door and wait
|
||||||
|
log.Printf("starting door handler...")
|
||||||
|
if err := doorHandler(&c); err != nil {
|
||||||
|
log.Printf("error from cmd `adventure`: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("returning from door")
|
||||||
|
n, err := c.Write([]byte("\n\n> "))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error writing to connection: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("wrote %d byte prompt to connection", n)
|
||||||
|
}
|
||||||
|
case "exit":
|
||||||
|
if err := exitHandler(c); err != nil {
|
||||||
|
log.Printf("error sending exitHandler from cmd `exit`: %v", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
n, err := c.Write([]byte("huh?\n\n> "))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error writing to connection: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("wrote %d bytes to connection", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
wg.Wait()
|
||||||
|
log.Printf("Goodbye %s!", c.RemoteAddr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendClientText(c net.Conn, s string) error {
|
||||||
|
n, err := c.Write([]byte(s))
|
||||||
|
if err != nil { // svr.ListenAndServe()
|
||||||
|
|
||||||
|
log.Printf("error writing to connection: %v", err)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
log.Printf("wrote %d bytes to connection", n)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func welcomeHandler(c *net.Conn) error {
|
||||||
|
const bannerText = "\x1b[2J\x1b[H\x1b[31m\x1b[J\r\n" + `
|
||||||
|
|
||||||
|
_ __ _ ` + "" + `
|
||||||
|
| | / /__ / /________ ____ ___ ___` + "" + `
|
||||||
|
| | /| / / _ \/ / ___/ __ \/ __ '__ \/ _ \` + "" + `
|
||||||
|
| |/ |/ / __/ / /__/ /_/ / / / / / / __/` + "" + `
|
||||||
|
|__/|__/\___/_/\___/\____/_/ /_/ /_/\___/` + "\r\n\x1b[33m" + `
|
||||||
|
__` + "" + `
|
||||||
|
/ /_____ ` + "" + `
|
||||||
|
/ __/ __ \` + "" + `
|
||||||
|
/ /_/ /_/ /` + "" + `
|
||||||
|
\__/\____/` + "\x1b[35m\r\n" + `
|
||||||
|
_ __` + "" + `
|
||||||
|
| | / /__ _____________` + "" + `
|
||||||
|
| | / / _ \/ ___/ ___/ _ \` + "" + `
|
||||||
|
| |/ / __/ / (__ ) __/` + "" + `
|
||||||
|
|___/\___/_/ /____/\___/` + "\x1b[37m\r\n" + `
|
||||||
|
_____ __ ___` + "" + `
|
||||||
|
/ ___// /___ ______/ (_)___ _____` + "" + `
|
||||||
|
\__ \/ __/ / / / __ / / __ \/ ___/` + "" + `
|
||||||
|
___/ / /_/ /_/ / /_/ / / /_/ (__ ) ` + "" + `
|
||||||
|
/____/\__/\__,_/\__,_/_/\____/____/ ` + "\x1b[32m\r\n\r\n\r\n" + `
|
||||||
|
Help is available: type help` + "\r\n\r\n> "
|
||||||
|
err := sendClientText(*c, bannerText)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error writing to connection: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func exitHandler(c net.Conn) error {
|
||||||
|
const exitMessage = "\x1b[2J\x1b[H\x1b[31m\x1b[J\r\n" + `
|
||||||
|
|
||||||
|
___| | |
|
||||||
|
\___ \ _ \ _ \ | | _ \ | | __| __ \ _' | __| _ \ __| _ \\ \ \ / __ \ _ \ | | |
|
||||||
|
| __/ __/ | | ( | | | \__ \ | | ( | ( __/ ( ( |\ \ \ / | | ( | | |_|
|
||||||
|
_____/ \___|\___| \__, |\___/ \__,_| ) ____/ .__/ \__,_|\___|\___| \___|\___/ \_/\_/ _.__/ \___/ \__, |_)
|
||||||
|
____/ / _| ____/
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
err := sendClientText(c, exitMessage)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error writing to connection: %v", err)
|
||||||
|
}
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
|
return c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func doorHandler(c *net.Conn) error {
|
||||||
|
const bannerText = "\r\nCOLOSSAL CAVE\r\n\r\n"
|
||||||
|
err := sendClientText(*c, bannerText)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error writing to connection: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("/usr/games/adventure")
|
||||||
|
|
||||||
|
stdout, _ := cmd.StdoutPipe()
|
||||||
|
stdin, _ := cmd.StdinPipe()
|
||||||
|
|
||||||
|
cmd.Start()
|
||||||
|
Terminator := make(chan bool)
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
wg.Add(1)
|
||||||
|
go func(c net.Conn, Terminator *chan bool, wg *sync.WaitGroup) {
|
||||||
|
log.Println("starting stdin reader goroutine")
|
||||||
|
defer wg.Done()
|
||||||
|
Check:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-*Terminator:
|
||||||
|
break Check
|
||||||
|
default:
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
inBuf := make([]byte, 80)
|
||||||
|
var l int
|
||||||
|
if l, err = c.Read(inBuf); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
log.Println("connection closed?")
|
||||||
|
break Check
|
||||||
|
} else {
|
||||||
|
log.Printf("error reading conn: %v\n", err)
|
||||||
|
break Check
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l > 0 {
|
||||||
|
log.Printf("writing %d bytes to stdin: %v\n", l, string(inBuf[:l]))
|
||||||
|
stdin.Write(inBuf[:l])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println("exiting stdin reader goroutine")
|
||||||
|
}(*c, &Terminator, wg)
|
||||||
|
|
||||||
|
log.Println("starting stdout reader goroutine")
|
||||||
|
scanner := bufio.NewScanner(stdout)
|
||||||
|
scanner.Split(bufio.ScanLines)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
m := scanner.Bytes()
|
||||||
|
(*c).Write(m)
|
||||||
|
(*c).Write([]byte{'\n'})
|
||||||
|
log.Printf("wrote %d bytes to stdout: %v\n", len(m)+1, string(m))
|
||||||
|
}
|
||||||
|
log.Println("exiting stdout reader goroutine")
|
||||||
|
Terminator <- true
|
||||||
|
|
||||||
|
log.Println("waiting for wg")
|
||||||
|
wg.Wait()
|
||||||
|
log.Println("waiting for cmd")
|
||||||
|
exitErr := cmd.Wait()
|
||||||
|
if exitErr != nil {
|
||||||
|
log.Printf("cmd exited with err: %v\n", exitErr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user