golang - 브로드캐스팅 하는 에코 서버 예제 코드

서버

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "os"
)

func main() {
    if len(os.Args) != 2 {
        log.Fatalf("Usage: %s <port>\n", os.Args[0])
    }

    port := os.Args[1]
    server := NewServer(port)
    server.ListenAndServe()
}

type message string

type Server struct {
    port         string
    clients      []*Client
    addClient    chan *Client
    removeClient chan *Client
    broadcast    chan message
}

func NewServer(port string) *Server {
    return &Server{
        port,
        make([]*Client, 0),
        make(chan *Client),
        make(chan *Client),
        make(chan message),
    }
}

func (s *Server) ListenAndServe() error {
    l, err := net.Listen("tcp", ":"+s.port)
    if err != nil {
        return err
    }
    defer l.Close()

    go s.Serve()

    for {
        conn, err := l.Accept()
        if err != nil {
            log.Printf("Error: %s\n", err)
        }
        c := NewClient(conn, s)

        s.addClient <- c

        go c.Read()
        go c.Write()
    }

}

func (s *Server) Serve() {
    for {
        select {
        case c := <-s.addClient:
            log.Println("Join: " + c.conn.RemoteAddr().String())
            s.clients = append(s.clients, c)
        case c := <-s.removeClient:
            for i := range s.clients {
                if s.clients[i] == c {
                    s.clients = append(s.clients[:i], s.clients[i+1:]...)
                    log.Println("Leave: " + c.conn.RemoteAddr().String())
                    break
                }
            }
        case m := <-s.broadcast:
            for _, c := range s.clients {
                log.Printf("Broadcast (%s): \"%s\"\n", c.conn.RemoteAddr(), m)
                c.channel <- m
            }
        }
    }
}

type Client struct {
    conn    net.Conn
    server  *Server
    channel chan message
    done    chan bool
}

func NewClient(conn net.Conn, server *Server) *Client {
    return &Client{
        conn,
        server,
        make(chan message),
        make(chan bool),
    }
}

func (c *Client) Read() {
    scanner := bufio.NewScanner(c.conn)
LOOP:
    for scanner.Scan() {
        select {
        case <-c.done:
            break LOOP
        default:
            m := scanner.Text()
            log.Printf("Receive (%s): \"%s\"\n", c.conn.RemoteAddr(), m)
            c.server.broadcast <- message(m)
        }
    }

    if err := scanner.Err(); err != nil {
        log.Printf("Error (read): %s\n", err)
    }
    c.server.removeClient <- c
    c.done <- true
}

func (c *Client) Write() {
LOOP:
    for {
        select {
        case <-c.done:
            break LOOP
        case m := <-c.channel:
            log.Printf("Send (%s): \"%s\"\n", c.conn.RemoteAddr(), m)
            _, err := fmt.Fprintln(c.conn, m)
            if err != nil {
                log.Printf("Error (write): %s\n", err)
                break LOOP
            }
        }
    }

    c.server.removeClient <- c
    c.done <- true
    return
}

Client

package main

import (
    "net"
    "os"
)

const (
    RECV_BUF_LEN = 1024
)

func main() {
    if len(os.Args) == 1 {
        println("need request parameter")
        os.Exit(1)
    }
    echo_contents := os.Args[1]
    tcp_addr, err := net.ResolveTCPAddr("tcp", "localhost:6666")
    if err != nil {
        println("error tcp resolve failed", err.Error())
        os.Exit(1)
    }
    tcp_conn, err := net.DialTCP("tcp", nil, tcp_addr)
    SendEcho(tcp_conn, echo_contents)

    echo := GetEcho(tcp_conn)
    println("echo: ", string(echo))
    println("receive success")
    tcp_conn.Close()
}

func SendEcho(conn *net.TCPConn, msg string) {
    _, err := conn.Write([]byte(msg))
    if err != nil {
        println("Error send request:", err.Error())
    } else {
        println("Request sent")
    }
}

func GetEcho(conn *net.TCPConn) string {
    buf_recever := make([]byte, RECV_BUF_LEN)
    _, err := conn.Read(buf_recever)
    if err != nil {
        println("Error while receive response:", err.Error())
        return ""
    }
    return string(buf_recever)
}

이 글은 2019-05-16에 작성되었습니다.