Skip to content

Instantly share code, notes, and snippets.

@satococoa
Created May 7, 2019 10:30
Show Gist options
  • Save satococoa/0d4c2a1e487f5a6b95e017f3669c0c9d to your computer and use it in GitHub Desktop.
Save satococoa/0d4c2a1e487f5a6b95e017f3669c0c9d to your computer and use it in GitHub Desktop.

Revisions

  1. satococoa renamed this gist May 7, 2019. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. satococoa created this gist May 7, 2019.
    118 changes: 118 additions & 0 deletions tcp_server.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,118 @@
    package server

    import (
    "fmt"
    "log"
    "net"
    "strings"
    "sync"
    "time"
    )

    // Server is a echo server.
    type Server struct {
    listener *net.TCPListener
    timeout time.Duration
    quit chan bool
    exited chan bool
    }

    func logf(format string, v ...interface{}) {
    log.Printf(format, v...)
    }

    // NewServer retruns Server with given listener and response.
    func NewServer(port int, timeout time.Duration) (*Server, error) {
    tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", port))
    if err != nil {
    return nil, err
    }
    l, err := net.ListenTCP("tcp", tcpAddr)
    if err != nil {
    return nil, err
    }
    s := &Server{
    listener: l,
    quit: make(chan bool),
    exited: make(chan bool),
    timeout: timeout,
    }
    return s, nil
    }

    // Start starts echo server.
    func (server *Server) Start() {
    logf("Server Listening on %s", server.listener.Addr())

    var handlers sync.WaitGroup
    for {
    select {
    case <-server.quit:
    logf("Shutting down...")
    handlers.Wait()
    close(server.exited)
    return
    default:
    conn, err := server.listener.Accept()
    if err != nil {
    if opErr, ok := err.(*net.OpError); ok && opErr.Temporary() {
    continue
    }
    // socket has been closed.
    if strings.Contains(err.Error(), "use of closed network connection") {
    continue
    }
    }
    handlers.Add(1)
    go func() {
    if err := server.handleConnection(conn); err != nil {
    logf("handle error: %v", err)
    }
    handlers.Done()
    }()
    }
    }
    }

    // Stop stops server.
    func (server *Server) Stop() error {
    logf("Server is stopping...")
    if err := server.listener.Close(); err != nil {
    return err
    }
    close(server.quit)
    <-server.exited
    logf("Server stopped successfully.")
    return nil
    }

    func (server *Server) handleConnection(conn net.Conn) error {
    logf("Connection accepted: %v -> %v", conn.RemoteAddr(), conn.LocalAddr())
    defer conn.Close()
    conn.SetDeadline(time.Now().Add(server.timeout))

    buf := make([]byte, 4*1024)
    for {
    n, err := conn.Read(buf)
    if err != nil {
    if opErr, ok := err.(*net.OpError); ok {
    if opErr.Timeout() {
    return err
    }
    if opErr.Temporary() {
    continue
    }
    return err
    }
    logf("read error: %v", err)
    return err
    }
    logf("read: %s", buf[:n])
    response := fmt.Sprintf("> %s", buf[:n])
    if _, err := conn.Write([]byte(response)); err != nil {
    logf("write error: %v", err)
    return nil
    }
    logf("write: %s", buf[:n])
    }
    }