mirror of
https://github.com/originalmk/archat-server.git
synced 2025-01-18 16:29:17 +00:00
Initial commit
This commit is contained in:
commit
15a92f508d
291
main.go
Normal file
291
main.go
Normal file
@ -0,0 +1,291 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Peer struct {
|
||||
id int
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
type PeerInfo struct {
|
||||
ID int `json:"id"`
|
||||
Addr string `json:"addr"`
|
||||
}
|
||||
|
||||
type EchoRequest struct {
|
||||
EchoByte byte `json:"echoByte"`
|
||||
}
|
||||
|
||||
type EchoResponse struct {
|
||||
EchoByte byte `json:"echoByte"`
|
||||
}
|
||||
|
||||
type ListPeersRequest struct {
|
||||
}
|
||||
|
||||
type ListPeersResponse struct {
|
||||
PeersInfo []PeerInfo `json:"peers"`
|
||||
}
|
||||
|
||||
const (
|
||||
echoRID = 1
|
||||
listPeersRID = 2
|
||||
)
|
||||
|
||||
var peersList []Peer
|
||||
var peersListLock sync.RWMutex
|
||||
var idCounter int = 0
|
||||
var idCounterLock sync.Mutex
|
||||
|
||||
func genNewID() int {
|
||||
var newid int
|
||||
idCounterLock.Lock()
|
||||
idCounter++
|
||||
newid = idCounter
|
||||
idCounterLock.Unlock()
|
||||
return newid
|
||||
}
|
||||
|
||||
func peerSliceIndexOf(s []Peer, id int) int {
|
||||
i := 0
|
||||
var p Peer
|
||||
for i, p = range s {
|
||||
if p.id == id {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func peerSliceRemove(s *[]Peer, i int) {
|
||||
(*s)[i] = (*s)[len(*s)-1]
|
||||
*s = (*s)[:len(*s)-1]
|
||||
}
|
||||
|
||||
func handleDisconnection(id int) {
|
||||
peersListLock.Lock()
|
||||
p := peersList[peerSliceIndexOf(peersList, id)]
|
||||
log.Printf("[Server] %s disconnected\n", p.conn.RemoteAddr())
|
||||
peerSliceRemove(&peersList, peerSliceIndexOf(peersList, id))
|
||||
peersListLock.Unlock()
|
||||
}
|
||||
|
||||
func handlePeer(p Peer) {
|
||||
br := bufio.NewReader(p.conn)
|
||||
bw := bufio.NewWriter(p.conn)
|
||||
|
||||
for {
|
||||
reqBytes, err := br.ReadBytes('\n')
|
||||
|
||||
if err == io.EOF {
|
||||
handleDisconnection(p.id)
|
||||
break
|
||||
} else if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
|
||||
if len(reqBytes) <= 1 {
|
||||
log.Println("got request without id")
|
||||
break
|
||||
}
|
||||
|
||||
reqBytes = reqBytes[:len(reqBytes)-1]
|
||||
operationCode := reqBytes[0]
|
||||
reqJsonBytes := reqBytes[1:]
|
||||
var resBytes []byte
|
||||
|
||||
if operationCode == echoRID {
|
||||
resBytes, err = handleEcho(reqJsonBytes)
|
||||
} else if operationCode == listPeersRID {
|
||||
resBytes, err = handleListPeers(reqJsonBytes)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
resBytes = append(resBytes, '\n')
|
||||
_, err = bw.Write(resBytes)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = bw.Flush()
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleEcho(reqBytes []byte) (resBytes []byte, err error) {
|
||||
var echoReq EchoRequest
|
||||
err = json.Unmarshal(reqBytes, &echoReq)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
echoRes := EchoResponse(echoReq)
|
||||
resBytes, err = json.Marshal(echoRes)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resBytes, nil
|
||||
}
|
||||
|
||||
func handleListPeers(reqBytes []byte) (resBytes []byte, err error) {
|
||||
// For the sake of conciseness -> currently unmarshalling empty slice to empty struct
|
||||
var listPeersReq ListPeersRequest
|
||||
err = json.Unmarshal(reqBytes, &listPeersReq)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peersListLock.RLock()
|
||||
peersFreeze := make([]Peer, len(peersList))
|
||||
copy(peersFreeze, peersList)
|
||||
peersListLock.RUnlock()
|
||||
listPeersRes := ListPeersResponse{make([]PeerInfo, 0)}
|
||||
|
||||
for _, peer := range peersFreeze {
|
||||
listPeersRes.PeersInfo = append(
|
||||
listPeersRes.PeersInfo,
|
||||
PeerInfo{peer.id, peer.conn.RemoteAddr().String()},
|
||||
)
|
||||
}
|
||||
|
||||
resBytes, err = json.Marshal(listPeersRes)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resBytes, nil
|
||||
}
|
||||
|
||||
func printConnectedPeers() {
|
||||
peersListLock.RLock()
|
||||
log.Println("[Server] Displaying all connections:")
|
||||
|
||||
for _, p := range peersList {
|
||||
log.Printf("[Server] ID#%d: %s\n", p.id, p.conn.RemoteAddr())
|
||||
}
|
||||
|
||||
peersListLock.RUnlock()
|
||||
}
|
||||
|
||||
func runServer() {
|
||||
ln, err := net.Listen("tcp", ":8080")
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
peersList = make([]Peer, 0)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
printConnectedPeers()
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
c, err := ln.Accept()
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
|
||||
log.Printf("[Server] client connected %s\n", c.RemoteAddr())
|
||||
peersListLock.Lock()
|
||||
np := Peer{genNewID(), c}
|
||||
peersList = append(peersList, np)
|
||||
peersListLock.Unlock()
|
||||
go handlePeer(np)
|
||||
}
|
||||
}
|
||||
|
||||
func runClient() {
|
||||
conn, err := net.Dial("tcp", ":8080")
|
||||
|
||||
if err != nil {
|
||||
log.Println("[Client] err connecting")
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
|
||||
br := bufio.NewReader(conn)
|
||||
bw := bufio.NewWriter(conn)
|
||||
|
||||
log.Println("[Client] connected to server")
|
||||
time.Sleep(time.Second * 1)
|
||||
|
||||
echoReq := EchoRequest{5}
|
||||
reqBytes, _ := json.Marshal(echoReq)
|
||||
bw.WriteByte(echoRID)
|
||||
bw.Write(reqBytes)
|
||||
bw.WriteByte('\n')
|
||||
bw.Flush()
|
||||
resBytes, _ := br.ReadBytes('\n')
|
||||
var echoRes EchoResponse
|
||||
json.Unmarshal(resBytes, &echoRes)
|
||||
log.Printf("[Client] echo sent (5), got %d\n", echoRes.EchoByte)
|
||||
|
||||
listReq := ListPeersRequest{}
|
||||
reqBytes, _ = json.Marshal(listReq)
|
||||
bw.WriteByte(listPeersRID)
|
||||
bw.Write(reqBytes)
|
||||
bw.WriteByte('\n')
|
||||
bw.Flush()
|
||||
resBytes, _ = br.ReadBytes('\n')
|
||||
var listRes ListPeersResponse
|
||||
json.Unmarshal(resBytes, &listRes)
|
||||
log.Println("[Client] printing all peers:")
|
||||
|
||||
for _, peer := range listRes.PeersInfo {
|
||||
log.Printf("[Client] Peer#%d from %s", peer.ID, peer.Addr)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
|
||||
func main() {
|
||||
args := os.Args[1:]
|
||||
|
||||
if len(args) != 1 {
|
||||
log.Fatalln("You must provide only one argument which is type of " +
|
||||
"application: 'server' or 'client'")
|
||||
}
|
||||
|
||||
runType := args[0]
|
||||
|
||||
if runType == "client" {
|
||||
runClient()
|
||||
} else if runType == "server" {
|
||||
runServer()
|
||||
} else {
|
||||
log.Fatalf("Unknown run type %s\n", runType)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user