forked from alexsasharegan/go-simple-tcp-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
154 lines (131 loc) · 3.28 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package main
import (
"bufio"
"fmt"
"log"
"net"
"os"
"os/signal"
"strconv"
"syscall"
"time"
)
const (
port = 3280
connLimit = 6
validLen = 10
minValue = 1000000
outIntvl = 5 * time.Second
logIntvl = 10 * time.Second
)
func init() {
os.Mkdir("./logs", 0777)
}
func main() {
// Start up the tcp server.
srv, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
log.Fatalf("Error listening: %v", err)
}
fmt.Printf(
"Started %s server.\nListening on %s\n",
srv.Addr().Network(), srv.Addr().String())
defer srv.Close()
counter := NewCounter(connLimit)
// Listen for termination signals.
sig := make(chan os.Signal)
signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
// Set up intervals
go counter.RunOutputInterval(outIntvl)
go counter.RunLogInterval(logIntvl)
// Receive new connections on an unbuffered channel.
conns := acceptConns(srv, counter)
for {
select {
case conn := <-conns:
go handleConnection(conn, counter)
case <-sig:
// Add a leading new line since the signal escape sequence prints on stdout.
fmt.Printf("\nShutting down server.\n")
counter.Close()
os.Exit(0)
}
}
}
// acceptConns uses the semaphore channel on the counter to rate limit.
// New connections get sent on the returned channel.
func acceptConns(srv net.Listener, counter *Counter) <-chan net.Conn {
conns := make(chan net.Conn)
go func() {
for {
conn, err := srv.Accept()
if err != nil {
fmt.Fprintf(os.Stderr, "Error accepting connection: %v\n", err)
continue
}
select {
case counter.Sem <- 1:
conns <- conn
default:
fmt.Fprintf(conn, "Server busy.")
conn.Close()
}
}
}()
return conns
}
// Handles incoming requests.
// Input is parsed and written to log if unique.
// Handles closing of the connection.
func handleConnection(conn net.Conn, counter *Counter) {
// Defer all close logic.
// Using a closure makes it easy to group logic as well as execute serially
// and avoid the deferred LIFO exec order.
defer func() {
// Since handleConnection is run in a go routine,
// it manages the closing of our net.Conn.
conn.Close()
// Once our connection is closed,
// we can drain a value from our semaphore
// to free up a space in the connection limit.
<-counter.Sem
}()
scanner := bufio.NewScanner(conn)
var s string
for scanner.Scan() {
s = scanner.Text()
// Malformed Request: invalid length
// Digit chars are safe for counting via len()
if len(s) != validLen {
continue
}
num, err := strconv.Atoi(s)
// Malformed Request: not a number
if err != nil {
continue
}
// Malformed Request: less than minimum
if num < minValue {
continue
}
/* From here on out, we have a valid input. */
// Safely increment total counter.
counter.Inc()
// Check if input has been recorded previously.
if counter.HasValue(num) {
continue
}
// Record the new unique value.
// In this case, logging is part of our reqs.
// We should fail is we didn't get this right.
if err = counter.RecordUniq(num); err != nil {
log.Fatalf("could not log unique value: %v\n", err)
}
}
// If a failure to read input occurs,
// it's probably my bad.
// Fail and figure it out if so!
if err := scanner.Err(); err != nil {
log.Fatalf("Error reading: %v", err)
}
}