Go를 이용한 TCP 서버와 클라이언트 상호작용 이해하기

jaehan·2024년 1월 2일

이번 포스트에서는 TCP 연결과 해제에 대해 구현한 Go 코드를 살펴보겠습니다.

먼저, TCP 연결과 해제 과정은 다음과 같습니다.

TCP 연결 3 Way Handshaking

TCP 해제 4 Way Handshaking

코드 예제

(1) 기본 설정: TCP 서버 생성
먼저, net.Listen 함수를 사용하여 로컬 호스트의 랜덤 포트에서 리스닝을 시작하는 TCP 서버를 생성합니다. 이 서버는 들어오는 연결을 수락하고 처리할 준비를 합니다.
여기서, 127.0.0.1:는 로컬 호스트의 랜덤 포트를 의미합니다.

(2) 고루틴을 사용한 연결 수락
여기서는 go 키워드를 사용하여 고루틴을 시작하고, 무한 루프를 통해 계속해서 들어오는 연결을 수락합니다.
TCP는 1:1 연결을 기반으로 하는 프로토콜이지만, 하나의 서버가 여러 클라이언트와 동시에 연결을 유지하는 것이 가능합니다.

(3) 클라이언트 연결과 TCP Handshakes
TCP 연결은 "3-way handshake"를 통해 설정됩니다. 클라이언트는 net.Dial을 사용하여 서버에 연결을 시도하며, 이는 SYN 패킷을 전송하는 것으로 시작됩니다.

(4) ~ (5) 클라이언트가 작업을 완료하면, "4-way handshake"를 통해 연결을 종료합니다. 클라이언트는 conn.Close()를 호출하여 FIN 패킷을 전송하고 연결 종료를 시작합니다. 이후, 서버 또한 클라이언트와의 listener.Close()를 호출하여 FIN 패킷을 전송하여 연결을 종료하고자 합니다.

package main

import (
	"io"
	"log"
	"net"
)

func main() {
	// (1) 랜덤 포트에 리스너 생성 => TCP 서버 생성
	listener, err := net.Listen("tcp", "127.0.0.1:")
	if err != nil {
		log.Fatal("error listening on socker:", err)
	}

	// 'done' 채널은 고루틴이 완료될 때 신호를 보내기 위해 사용
	done := make(chan struct{})

	// (2) 고루틴으로 연결 수락
	go func() {
		defer func() { done <- struct{}{} }()

		// 무한 루프를 통해 계속해서 들어오는 연결 수락
		for {
			conn, err := listener.Accept()
			if err != nil {
				log.Println("error accepting connection:", err)
				return
			}

			// 새 고루틴에서 각 연결 처리
			go func(c net.Conn) {
				defer func() {
					c.Close()
					done <- struct{}{}
				}()

				// 1024 바이트의 버퍼를 생성하고, 클라이언트로부터 데이터를 읽음
				buf := make([]byte, 1024)
				for {
					n, err := c.Read(buf)
					if err != nil {
						if err != io.EOF {
							log.Println("error reading from c:", err)
						}
						return
					}

					log.Printf("received: %q", buf[:n])
				}
			}(conn)
		}
	}()

	// (3) 클라이언트 연결 생성 및 종료 - SYN 전송(Client to Server) - 3 Way Handshaking 시작
	conn, err := net.Dial("tcp", listener.Addr().String())
	if err != nil {
		log.Fatal("error creating connection: ", err)
	}
	conn.Close() // (4) FIN 전송 (Client to Server) - 4 Way Handshaking 시작

	// 리스너 및 고루틴 종료
	<-done 
	listener.Close() // (5) FIN 전송(Server to Client) - 4 Way Handshaking 3번째 과정
	<-done
}
profile
공부공부

0개의 댓글