🐹 Go - TCP Server
TCP Server
Updated at 2015-01-17 14:02
Server program that accept TCP connections, creates representative clients of those connections, maintains a list of the clients, sends all clients a message every few seconds and prints all messages the clients send.
package main
import (
"container/list"
"fmt"
"net"
"runtime"
"time"
)
/**
* CLIENT
*/
type Client struct {
Name string
Conn net.Conn
ReadChan chan string
WriteChan chan string
CloseChan chan bool
}
func NewClient(name string, conn net.Conn) *Client {
fmt.Printf("%s joined.\n", name)
readChan := make(chan string)
writeChan := make(chan string)
closeChan := make(chan bool)
newClient := &Client{name, conn, readChan, writeChan, closeChan}
go newClient.ReadToReadChan()
go newClient.WriteChanToWrite()
return newClient
}
func (self *Client) Close() {
close(self.CloseChan)
self.Conn.Close()
fmt.Printf("%s left.\n", self.Name)
}
func (self *Client) ReadToReadChan() {
defer self.Close()
var buffer [1024]byte
for {
bytesRead, errRead := self.Conn.Read(buffer[0:])
if errRead != nil {
fmt.Printf("! Client read error: %s.\n", errRead)
return
}
request := string(buffer[:bytesRead])
self.ReadChan <- request
}
}
func (self *Client) WriteChanToWrite() {
for {
select {
case <-self.CloseChan:
// Exit func/goroutine if this client is closed.
return
case response := <-self.WriteChan:
fmt.Printf("Server => %s : %s\n", self.Name, response)
_, errWrite := self.Conn.Write([]byte(response))
if errWrite != nil {
fmt.Printf("! Client write error: %s.\n", errWrite)
return
}
}
}
}
/**
* CLIENTS
*/
type Clients struct {
list *list.List
}
func NewClients() *Clients {
return new(Clients).Init()
}
func (self *Clients) Init() *Clients {
self.list = list.New()
return self
}
func (self *Clients) Count() int {
return self.list.Len()
}
func (self *Clients) SendAll(msg string) {
for entry := self.list.Front(); entry != nil; entry = entry.Next() {
client := entry.Value.(*Client)
client.WriteChan <- msg
}
}
func (self *Clients) Add(client *Client) {
self.list.PushBack(client)
}
func (self *Clients) RemoveOnClose(client *Client) {
<-client.CloseChan // Wait for the client to close.
for entry := self.list.Front(); entry != nil; entry = entry.Next() {
otherClient := entry.Value.(*Client)
if otherClient.Name == client.Name {
self.list.Remove(entry)
}
}
}
/**
* MAIN
*/
func main() {
addr, _ := net.ResolveTCPAddr("tcp4", ":1234")
listener, _ := net.ListenTCP("tcp", addr)
var clients = NewClients()
// Print connected clients and running goroutine count every second.
infoTicker := time.NewTicker(time.Second)
go func() {
for {
<-infoTicker.C
c := clients.Count()
g := runtime.NumGoroutine()
fmt.Printf("%d clients online, running on %d goroutines.\n", c, g)
}
}()
// Send Tick or Tock every 2 seconds.
tickTockTicker := time.NewTicker(time.Second * 2)
go func() {
for {
<-tickTockTicker.C
clients.SendAll("Tick")
<-tickTockTicker.C
clients.SendAll("Tock")
}
}()
// Each new client starts total of 4 goroutines, which of 2
// are internal and added in NewClient().
// 1: Listen to Conn.Read and direct it to ReadChan.
// 2: Listen to WriteChan and direct it to Conn.Write.
// 3: Remove client on client lists when it is closed.
// 4: Act on messages received from ReadChan.
clientNumber := 0
for {
conn, _ := listener.AcceptTCP()
clientNumber++
name := fmt.Sprintf("Client%d", clientNumber)
client := NewClient(name, conn)
clients.Add(client)
go clients.RemoveOnClose(client)
go handleClient(client)
}
}
func handleClient(client *Client) {
for {
select {
case <-client.CloseChan:
// Exit func/goroutine if this client is closed.
return
case request := <-client.ReadChan:
// Wait for requests and act based them.
fmt.Printf("%s => Server : %s\n", client.Name, request)
client.WriteChan <- "Pong"
}
}
}
Client spawn program that creates TCP connections in a regular interval.
package main
import (
"fmt"
"net"
"time"
)
func main() {
spanwInterval := time.Millisecond * 3000
for {
go ping()
<-time.After(spanwInterval)
}
}
func ping() {
addr, errAddr := net.ResolveTCPAddr("tcp4", "localhost:1234")
if errAddr != nil {
fmt.Printf("Client addr error: %s\n", errAddr)
return
}
conn, errConn := net.DialTCP("tcp", nil, addr)
defer conn.Close()
if errConn != nil {
fmt.Printf("Client conn error: %s\n", errConn)
return
}
// Wait for a message from the server.
var buff [64]byte
bytesTickerRead, errTickerRead := conn.Read(buff[0:])
if errTickerRead != nil {
fmt.Printf("Client read error: %s\n", errTickerRead)
return
}
fmt.Printf("We got %s from the server\n", string(buff[:bytesTickerRead]))
// Send a message to the server.
msg := "Ping"
_, errWrite := conn.Write([]byte(msg))
if errWrite != nil {
fmt.Printf("Client write error: %s\n", errWrite)
return
}
// Wait for a message from the server.
bytesPongRead, errPongRead := conn.Read(buff[0:])
if errPongRead != nil {
fmt.Printf("Client read error: %s\n", errPongRead)
return
}
fmt.Printf("We got %s from the server\n", string(buff[:bytesPongRead]))
}