2016-10-22 3 views
2

나는 Go -를 실험 중이며 텔넷으로 연결할 수 있고 명령을 보내고 응답을받을 수있는 TCP 서버를 만들고 싶습니다.Golang 서버에서 영구적 인 tcp 연결을 수락합니다.

const (
    CONN_HOST = "localhost" 
    CONN_PORT = "3333" 
    CONN_TYPE = "tcp" 
) 

func main() { 

    listener, err := net.Listen(CONN_TYPE, fmt.Sprintf("%s:%s", CONN_HOST, CONN_PORT)) 
    if err != nil { 
     log.Panicln(err) 
    } 

    defer listener.Close() 

    for { 
     conn, err := listener.Accept() 
     if err != nil { 
      log.Panicln(err) 
     } 

     go handleRequest(conn) 
    } 
} 

func handleRequest(conn net.Conn) { 
    buffer := make([]byte, 1024) 

    length, err := conn.Read(buffer) 
    if err != nil { 
     log.Panicln(err) 
    } 

    str := string(buffer[:length]) 

    fmt.Println(conn.RemoteAddr().String()) 
    fmt.Printf("Received command %d\t:%s\n", length, str) 

    switch str { 
     case "PING\r\n": 
     sendResponse("PONG", conn) 
     case "PUSH\r\n": 
     sendResponse("GOT PUSH", conn) 
    default: 
     conn.Write([]byte(fmt.Sprintf("UNKNOWN_COMMAND: %s\n", str))) 
    } 

    conn.Close() // closes the connection 
} 

func sendResponse(res string, conn net.Conn) { 
    conn.Write([]byte(res+"\n")) 
} 

위의 코드는 매번 연결을 닫아 터미널 세션에서 나를 쫓아냅니다. 하지만 실제로 원하는 것은 더 많은 I/O 작업을 위해 연결을 열어 둘 수 있어야한다는 것입니다. conn.Close()을 제거하면 더 이상 응답을받지 못하기 때문에 어딘가에 서버가 멈춘 것 같습니다.

내가 해결 한 방법은 내 handleRequest 메서드를 끝없이 반복하여 메시지가 QUIT\r\n 메시지가 될 때까지 끝내지 않도록하는 것입니다. 이것이 적절한가요? 아니면 더 좋은 방법이 있습니까?

func handleRequest(conn net.Conn) { 
    for { 
     log.Println("Handling Request") 
     buffer := make([]byte, 1024) 

     length, err := conn.Read(buffer) 
     if err != nil { 
      log.Panicln(err) 
     } 

     str := string(buffer[:length]) 

     fmt.Println(conn.RemoteAddr().String()) 
     fmt.Printf("Received command %d\t:%s\n", length, str) 

     switch str { 
     case "PING\r\n": 
      sendResponse("PONG", conn) 
     case "PUSH\r\n": 
      sendResponse("GOT PUSH", conn) 
     case "QUIT\r\n": 
      sendResponse("Goodbye", conn) 
      conn.Close() 
     default: 
      conn.Write([]byte(fmt.Sprintf("UNKNOWN_COMMAND: %s\n", str))) 
     } 
    } 
} 
+1

예 예를 두 번 이상 읽으려면 읽음을 반복해야합니다. 오류가 발생하면 당황하지 마십시오. 연결이 끊어지면 프로그램을 중단 할 이유가 없습니다. – JimB

+1

이것이 올바른 구현이라고 생각합니다. 힌트 : 당신이'conn.Close()'를 호출 한 후에 break 문을 호출하면, for 루프가 영원히 계속 실행됩니다. – Tinwor

+0

JimB Thanks. 오류에 대한 당황 스러움 - 나는 적절한 오류 처리기를 아직 만들지 않았기 때문에 dev에서하고있었습니다. 그리고 그 오류를 포착하고 싶었습니다. 틴워 - 잘 했어. – Gravy

답변

4

두 번째 예를 들어 당신이 원하는 것을 이미. 원하는만큼 루프를 반복하고 (또는 일부 읽기/쓰기 시간 초과 또는 외부 취소 신호가 발생할 때까지)

그러나 여전히 오류가 있습니다 : TCP는 바이트 스트림을 제공합니다. 여기서 한 쪽 쓰기가 동일한 데이터 길이의 다른쪽에 정확히 하나의 읽기를 생성하지는 않습니다. 즉, 클라이언트가 PING\r\n을 쓰는 경우 첫 번째 읽기에서는 여전히 PI 만 수신 할 수 있습니다. bufio.Scanner을 사용하여 문제를 해결하고 항상 첫 번째 줄 바꿈까지 읽을 수 있습니다.

3

찾고있는 것이 맞는지 확실하지 않습니다. net/http 구현에서 가져온 것이므로 net.TCPListenerAccept 메소드를 래핑합니다.

tcpKeepAliveListener{listener.(*net.TCPListener)}

type tcpKeepAliveListener struct { 
    *net.TCPListener 
} 

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { 
    tc, err := ln.AcceptTCP() 
    if err != nil { 
     return 
    } 
    tc.SetKeepAlive(true) 
    tc.SetKeepAlivePeriod(3 * time.Minute) 
    return tc, nil 
} 

참조 : 루프와 Link 1 & Link 2

+1

내가 원하는 것은 아니지만 매우 유용합니다. 감사 – Gravy

관련 문제