172 lines
2.9 KiB
Go
172 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"net"
|
|
"os"
|
|
"fmt"
|
|
"time"
|
|
"encoding/binary"
|
|
)
|
|
|
|
var BE = binary.BigEndian
|
|
|
|
type SpeedMessage interface {
|
|
serialize() []byte
|
|
}
|
|
|
|
type ErrorMessage struct {
|
|
err string
|
|
}
|
|
|
|
func (m ErrorMessage) serialize() []byte {
|
|
return []byte{0x10}
|
|
}
|
|
|
|
type HeartbeatMessage struct {}
|
|
|
|
func (m HeartbeatMessage) serialize() []byte {
|
|
return []byte{0x41}
|
|
}
|
|
|
|
type SpeedServer struct {
|
|
port uint16
|
|
tickQ chan bool
|
|
clients map[uint]*SpeedClient
|
|
numClients uint
|
|
}
|
|
|
|
func NewSpeedServer(port uint16) *SpeedServer {
|
|
s := &SpeedServer{
|
|
port,
|
|
make(chan bool),
|
|
make(map[uint]*SpeedClient),
|
|
0,
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
type SpeedClient struct {
|
|
clientId uint
|
|
con net.Conn
|
|
q chan SpeedMessage
|
|
ctype uint8
|
|
lastHeartbeat int64
|
|
heartbeat int64
|
|
}
|
|
|
|
func NewSpeedClient(clientId uint, con net.Conn) *SpeedClient {
|
|
return &SpeedClient{
|
|
clientId,
|
|
con,
|
|
make(chan SpeedMessage),
|
|
0,
|
|
0,
|
|
0,
|
|
}
|
|
}
|
|
|
|
func (s *SpeedServer) tick() {
|
|
cur := time.Now().UnixMilli()
|
|
m := HeartbeatMessage{}
|
|
for _, c := range s.clients {
|
|
if c.heartbeat == 0 { continue }
|
|
|
|
if c.lastHeartbeat + c.heartbeat < cur {
|
|
c.q <- m
|
|
c.lastHeartbeat = cur
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *SpeedServer) ticker() {
|
|
for {
|
|
s.tickQ <- true
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
func (s *SpeedServer) main() {
|
|
for {
|
|
select {
|
|
case _ = <- s.tickQ: s.tick()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *SpeedServer) Run() {
|
|
go s.listen()
|
|
go s.ticker()
|
|
s.main()
|
|
}
|
|
|
|
func (s *SpeedServer) listen() {
|
|
addr := fmt.Sprintf("0.0.0.0:%d", s.port)
|
|
server, err := net.Listen("tcp", addr)
|
|
if err != nil {
|
|
fmt.Println("Error listening:", err.Error())
|
|
os.Exit(1)
|
|
}
|
|
defer server.Close()
|
|
fmt.Println("SpeedServer waiting for client...")
|
|
for {
|
|
connection, err := server.Accept()
|
|
if err != nil {
|
|
fmt.Println("Error accepting: ", err.Error())
|
|
os.Exit(1)
|
|
}
|
|
|
|
s.start(connection)
|
|
}
|
|
}
|
|
|
|
func (s *SpeedServer) start(con net.Conn) {
|
|
c := NewSpeedClient(s.numClients, con)
|
|
s.clients[s.numClients] = c
|
|
s.numClients += 1
|
|
go c.sender()
|
|
go c.receiver()
|
|
}
|
|
|
|
func (c *SpeedClient) sender() {
|
|
for m := range c.q {
|
|
c.con.Write(m.serialize())
|
|
}
|
|
|
|
c.con.Close()
|
|
fmt.Printf("Client %d closed\n", c.clientId)
|
|
}
|
|
|
|
func (c *SpeedClient) receiver() {
|
|
var err error
|
|
fmt.Printf("client %d connected\n", c.clientId)
|
|
for {
|
|
var mType uint8
|
|
err = binary.Read(c.con, BE, &mType);
|
|
if err != nil { break }
|
|
switch mType {
|
|
case 0x40: err = c.hdlWantHeartbeat()
|
|
|
|
default: err = fmt.Errorf("Unknown message type 0x%x", mType)
|
|
}
|
|
if err != nil { break }
|
|
}
|
|
if err != nil {
|
|
fmt.Printf("Client %d error %s\n", c.clientId, err)
|
|
} else {
|
|
fmt.Printf("Client %d closing\n", c.clientId)
|
|
}
|
|
close(c.q)
|
|
}
|
|
|
|
func (c *SpeedClient) hdlWantHeartbeat() error {
|
|
var wantedHb uint32
|
|
err := binary.Read(c.con, BE, &wantedHb)
|
|
if err != nil { return err }
|
|
c.heartbeat = int64(wantedHb) * 100
|
|
fmt.Printf("heartbeat for %d set to %d\n", c.clientId, wantedHb)
|
|
return nil
|
|
}
|
|
|
|
|