golang - 에코 서버 예제코드
- 클라이언트 관리를 채널 별로 나누어서 처리.
- distribute 라는 함수를 고루틴으로 동작시키고, 내부에서 채널을 통해서 메시지를 받는다.
- 클라이언트 접속, 클라이언트 접속 해제, 클라이언트에게 메시지 보내기 채널을 나눈다.
- 고루틴은 handle, distribute(메시지 보내기 채널을 호출하는 고루틴이 있음)
서버
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"os"
)
type Client struct {
con net.Conn
c chan<- string
}
func handle(con net.Conn, addclient chan<- Client, deleteclient chan<- Client, msgchan chan string) {
c := make(chan string)
client := Client{con, c}
io.WriteString(client.con, "> ")
go func() {
defer client.con.Close()
for s := range c {
if _, err := io.WriteString(client.con, s); err != nil {
deleteclient <- client
return
}
io.WriteString(client.con, "> ")
}
}()
addclient <- client
buf := bufio.NewReader(client.con)
for {
l, _, err := buf.ReadLine()
if err != nil {
break
}
msgchan <- string(l) + "\r\n"
}
}
func distribute(addclient <-chan Client, deleteclient <-chan Client, msgchan <-chan string) {
clients := make(map[Client]bool)
for {
select {
case client := <-addclient:
fmt.Printf("new client: %v\n", client.con.RemoteAddr())
clients[client] = true
case client := <-deleteclient:
fmt.Printf("delete client: %v\n", client.con.RemoteAddr())
delete(clients, client)
case msg := <-msgchan:
for client, _ := range clients {
go func(client Client) { client.c <- msg }(client)
}
}
}
}
func main() {
port := ":8080"
if len(os.Args) > 1 {
port = ":" + os.Args[1]
}
ln, err := net.Listen("tcp", port)
if err != nil {
log.Fatal(err)
}
addclient := make(chan Client)
deleteclient := make(chan Client)
msgchan := make(chan string)
go distribute(addclient, deleteclient, msgchan)
for {
conn, err := ln.Accept()
if err != nil {
continue
}
go handle(conn, addclient, deleteclient, msgchan)
}
}
이 글은 2019-05-15에 작성되었습니다.