Skip to content

Instantly share code, notes, and snippets.

@s-shin
Created June 8, 2017 04:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save s-shin/fd79c53757ffbd360fd5c4354c84de96 to your computer and use it in GitHub Desktop.
Save s-shin/fd79c53757ffbd360fd5c4354c84de96 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"bytes"
"compress/gzip"
"encoding/binary"
"fmt"
"io"
"log"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: %s <host:port>", os.Args[0])
os.Exit(1)
}
hostport := os.Args[1]
conn, err := net.Dial("tcp", hostport)
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("> ")
if !scanner.Scan() {
break
}
raw := bytes.NewBuffer(scanner.Bytes())
gz := &bytes.Buffer{}
gzw := gzip.NewWriter(gz)
io.Copy(gzw, raw)
gzw.Flush()
header := &bytes.Buffer{}
log.Printf("size to be sent = %d", uint32(gz.Len()))
binary.Write(header, binary.LittleEndian, uint32(gz.Len()))
io.Copy(conn, header)
io.Copy(conn, gz)
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
log.Print("quit")
}
package main
import (
"bufio"
"bytes"
"compress/gzip"
"encoding/binary"
"fmt"
"io"
"log"
"net"
"os"
)
//---
func splitChunk(data []byte, atEOF bool) (int, []byte, error) {
if len(data) == 0 && atEOF {
return 0, nil, io.EOF
}
buf := bytes.NewReader(data)
var dataSize uint32
err := binary.Read(buf, binary.LittleEndian, &dataSize)
log.Printf("splitChunk: size = %d", dataSize)
if err != nil {
if atEOF {
return 0, nil, fmt.Errorf("too short data")
}
return 0, nil, nil // read more
}
dataSizeWithSizeHeader := dataSize + 4
if uint32(len(data)) < dataSizeWithSizeHeader {
if atEOF {
return 0, nil, fmt.Errorf("too short data")
}
return 0, nil, nil // read more
}
return int(dataSizeWithSizeHeader), data[4:dataSizeWithSizeHeader], nil
}
//---
type ClientID int
var lastClientID ClientID
type Client struct {
ID ClientID
conn net.Conn
}
func NewClient(conn net.Conn) *Client {
lastClientID++
return &Client{ID: lastClientID, conn: conn}
}
func (c *Client) Activate() {
scanner := bufio.NewScanner(c.conn)
scanner.Split(splitChunk)
for scanner.Scan() {
gz := bytes.NewBuffer(scanner.Bytes())
gzr, err := gzip.NewReader(gz)
if err != nil {
log.Printf("client[%d]: err = %s", c.ID, err)
continue
}
raw := &bytes.Buffer{}
io.Copy(raw, gzr)
log.Printf("client[%d]: msg = %s", c.ID, raw.String())
}
if err := scanner.Err(); err != nil {
log.Printf("client[%d]: err = %s", c.ID, err)
}
}
//---
type App struct {
clients map[ClientID]*Client
}
func NewApp() *App {
return &App{clients: make(map[ClientID]*Client)}
}
func (a *App) HandleConnection(conn net.Conn) {
defer conn.Close()
client := NewClient(conn)
a.clients[client.ID] = client
defer func() {
delete(a.clients, client.ID)
}()
client.Activate()
}
//---
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: %s <port>", os.Args[0])
os.Exit(1)
}
app := NewApp()
port := os.Args[1]
l, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatal(err)
}
for {
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
go app.HandleConnection(conn)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment