简洁的并发TCP服务器
为了能够和网络中的 key-value 存储交互,我们创建自定义的 TCP 协议。您将需要为 key-value 存储的每一个函数定义关键字。为简单起见,每个关键字都跟着相关数据。大多数命令的结果将是成功或失败消息。
这个主题使用的工具命名为 kvTCP.go
,它被分为六个部分。
kvTCP.go
的第一部分如下:
func handleConnection(c net.Conn) {
c.Write([]byte(welcome))
for {
netData, err := bufio.NewReader(c).ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
command := strings.TrimSpace(string(netData))
tokens := strings.Fields(command)
switch len(tokens) {
case 0:
continue
case 1:
tokens = append(tokens, "")
tokens = append(tokens, "")
tokens = append(tokens, "")
tokens = append(tokens, "")
case 2:
tokens = append(tokens, "")
tokens = append(tokens, "")
tokens = append(tokens, "")
case 3:
tokens = append(tokens, "")
tokens = append(tokens, "")
case 4:
tokens = append(tokens, "")
}
switch tokens[0] {
case "STOP":
err = save()
if err != nil {
fmt.Println(err)
}
c.Close()
return
case "PRINT":
PRINT(c)
case "DELETE":
if !DELETE(tokens[1]) {
netData := "Delete operation failed!\n"
c.Write([]byte(netData))
}else{
netData := "Delete operation successful!\n"
c.Write([]byte(netData))
}
case "ADD":
n := myElement{tokens[2], tokens[3], tokens[4]}
if !ADD(tokens[1], n) {
netData := "Add operation failed!\n"
c.Write([]byte(netData))
} else {
netData := "Add operation successful!\n"
}
if err != nil {
fmt.Println(err)
}
case "LOOKUP":
n := LOOKUP(tokens[1])
if n != nil {
netData := fmt.Sprintf("%v\n", *n)
c.Write([]byte(netData))
} else {
netData := "Did not find key!\n"
c.Write([]byte(netData))
}
case "CHANGE":
n := myElement{tokens[2], tokens[3], tokens[4]}
if !CHANGE(tokens[1], n) {
netData := "Update operation failed!\n"
c.Write([]byte(netData))
} else {
netData := "Update operation successful!\n"
c.Write([]byte(netData))
}
err = save()
if err != nil {
fmt.Println(err)
}
default:
netData := "Unknown command - please try again!\n"
c.Write([]byte(netData))
}
}
}
handleConnection()
函数和每个 TCP 客户端交互并解析客户端的输入。
kvTCP.go
的第三部分包含如下代码:
kvTCP.go
的第四段如下:
func ADD(k string, n myElement) bool {
if k == "" {
return false
}
if LOOKUP(k) == nil {
DATA[k] = n
return true
}
return false
}
func DELETE(k string) bool {
if LOOKUP(k) != nil {
delete(DATA, k)
return true
}
return false
}
func LOOKUP(k string) *myElement {
if ok {
n := DATA[k]
return &n
return nil
}
}
func CHANGE(k string, n myElement) bool {
DATA[k] = n
return true
}
上面的这些函数实现与 keyValue.go
一样。它们没有直接和 TCP 客户端交互。
PRINT()
函数直接发送数据给 TCP 客户端,一次一行。
这个程序的剩余代码如下:
func main() {
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide a port number!")
return
}
PORT := ":" + arguments[1]
l, err := net.Listen("tcp", PORT)
if err != nil {
fmt.Println(err)
return
}
defer l.Close()
err = load()
if err != nil {
fmt.Println(err)
}
for {
c, err := l.Accept()
if err != nil {
fmt.Println(err)
os.Exit(100)
}
go handleConnection(c)
}
}
执行 kvTCP.go
将产生如下输出:
为了这节的目的,netcat(l)
工具用来作为 kvTCP.go
的客户端:
$ nc localhost 9000
Welcome to the Key-value store!
LOOKUP 1
Did not find key!
ADD 1 2 3 4
Add operation successful!
LOOKUP 1
{2 3 4}
ADD 4 -1 -2 -3
Add operation successful!
key: 1 value: {2 3 4}
STOP