4-2. Go 언어을 사용한 TCP 연결 수립(1)

독수리박박·2024년 4월 8일
0

이 글은 go언어를 활용한 네트워크 프로그래밍을 기반으로 작성되었습니다


Go 표준 라이브러리를 사용한 TCP 연결 수립

소켓 바인딩, 연결 대기, 연결 수락

go의 net.Listen() 함수를 사용하면 수신 연결 요청 처리가 가능한 TCP 서버를 작성할 수 있습니다.

이런 서버를 리스너라고 하고 해당 함수는 netListener 인터페이스의 구현체를 반환합니다.

포트에 수신 대기 중인 리스너 생성하기

package ch03

import (
	"net"
	"testing"
)

func TestListener(t *testing.T) {
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		_ = listener.Close()
	}()

	t.Logf("listener's address: %s", listener.Addr().String())
}

go로 listener에 관한 테스트 코드입니다.

  • net.Listen은 listener 인터페이스와 에러 인터페이스를 반환합니다.
  • 함수가 성공적으로 실행되면 인자로 받은 IP주소와 포트 번호에 바인딩됩니다.

    바인딩: 운영체제가 지정된 IP주소와 포트번호를 어떤 리스너에게 단독으로 할당

만약 이미 바인딩된 포트에 리스너가 바인딩을 하려 한다면 당연히 에러가 반환됩니다.

그리고 IP주소와 포트번호 매개변수를 비워둔다면 Go가 리스너에 무작위 포트를 할당합니다. 따라서 왠만하면 명시적인 코드를 위해 지정해주는 것이 좋습니다.

그리고 "tcp"가 들어간 부분에는 네트워크 타입을 지정하며 tcp4, tcp6등 주소 체계도 지정 가능합니다. listener 변수에 net.Listener가 할당이 되면이 listner의 address도 얻을 수 있습니다.

마지막으로 함수가 종료되기 전 listener를 닫아줘야 합니다. listener.Close()통해 연결을 종료시킵니다.

테스트 코드이기 때문에 함수가 종료되면 메모리가 자동으로 해제되지만 더욱 명시적인 코드 작성을 위해 defer함께 종료하는 함수를 써주면 좋습니다.


TCP 수신 연결 응답 및 요청 처리하기

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

		go func(c net.Conn) {
			defer c.Close()

			/* other logic */
		}(conn)
	}

위 코드에서는 하나 이상의 연결 수신을 위해 반복문을 통해 서버가 계속해서 연결을 수락하고 처리하는걸 보여줍니다.

  • 연결 수락
  • go 루틴을 통해 연결 처리
  • 다시 연결 수락
  • go 루틴을 통해 연결 처리
    .
    .
    .
    .
    반복

go 루틴을 사용하지 않고 직렬화된 코드 사용이 가능하지만 go 루틴은 go언어가 가진 큰 강점이기 때문에 사용하여 여러 연결을 비동기적으로 처리할 수 있도록 합니다.

Accept 메서드는 서버가 연결 요청을 수락하고 연결(핸드 셰이킹)이 처리될 때 가지 블로킹됩니다. 이후에는 실제 연결이 수락되었기 때문에 net.Conn 객체의 포인터가 반환됩니다.

이후에는 go 루틴 안에서 연결과 관련된 여러가지 로직들을 처리하고 함수가 종료될 때 닫아줍니다. Close()는 우아한 종료를 위해 연결이 끊기기 전 서버로 FIN 패킷을 전송합니다.


0개의 댓글