이번 시간에는 go언어 소켓 프로그래밍을 다뤄보겠습니다.

net 패키지

go 의 네트워크 통신 관련 함수는 net 패키지에 정의되어 있습니다.

import "net"

다음 함수들은 기본적으로 동기 적으로 작동합니다.

net.Dial()

클라이언트가 서버에게 연결을 요청할 때 net.Dial() 함수를 사용합니다.

첫 번째 인자로 통신 프로토콜을, 두 번째 인자로 주소를 입력합니다.

지원하는 프로토콜

  • tcp, tcp4, tcp6
  • udp, udp4, udp6
  • ip, ip4, ip6
  • unix, unixgram, unixpacket

tcpipv4, ipv6 전부 사용, tcp4ipv4만 사용, tcp6ipv6만 사용합니다.

tcp 예제

func main() {
	conn, err := net.Dial("tcp", "localhost:http")
	if err != nil {
		log.Fatal("dial error: " + err.Error())
	}
	
    defer conn.Close()
    
	// ...
}

udp 예제

func main() {
	conn, err := net.Dial("udp", "[2001:db8::1]:50000")
	if err != nil {
		log.Fatal("dial error: " + err.Error())
	}

	// ...
}

unix 예제

func main() {
	conn, err := net.Dial("unix", "./sock")
	if err != nil {
		log.Fatal("dial error: " + err.Error())
	}

	// ...
}

net.Conn

net.Dial 함수는 서버와 연결 후, net.Conn을 리턴 합니다.

이를 사용해서 서버와 입출력을 하면 됩니다.

net.Connio.Reader, io.Writer 인터페이스를 따릅니다.

net.Conn 소켓에 데이터 쓰기

io, ioutil, bytes, strings 패키지를 활용할 수 있습니다.

// n - 소켓에 작성한 바이트 수
// err - 에러
n, err := conn.Write([]byte("hello"))
reader := strings.NewReader("hello world")
reader.WriteTo(conn)

net.Conn 소켓에 데이터 읽기

b := make([]byte, 2048)

// n - 소켓에서 읽어낸 바이트 수
// err - 에러
n, err := conn.Read(b)
buffer := bytes.NewBuffer(nil)
buffer.ReadFrom(conn)
f, _ := os.OpenFile("./data", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0600)
io.Copy(f, conn)

net.Listen()

서버 소켓을 열고자 할 때 사용하는 함수입니다.

listener, err := net.Listen("tcp", "localhost:50000")
if err != nil {
log.Fatal(err.Error())
}

defer listener.Close()

net.Listener, Accept()

net.Listne() 함수는 net.Listener를 리턴 합니다.

Listener.Accept()

이 함수 실행 시, 클라이언트로 부터 연결 요청이 있을 때 까지 프로세스를 대기 시킵니다.

만약, 연결 요청 시, Accept() 함수는 클라이언트 소켓을 리턴하고, 함수를 종료합니다.

clientConn, err := listener.Accept()

if err != nil {
log.Print(err.Error())
}

연결을 요청한 클라이언트와 통신하는 clientConn 이 생성 되었습니다. net.Dial() 이 리턴 하는 net.Conn 과 사용법이 같습니다.

예제: 에코 서버

package main

import (
	"fmt"
	"log"
	"net"
)

func main() {
	listener, err := net.Listen("tcp", "localhost:50000")
	if err != nil {
		log.Fatal(err.Error())
	}

	defer listener.Close()

	go func() {
		// 쓰레드 시작
		
		// 클라이언트 소켓 생성
		conn, _ := net.Dial("tcp", "localhost:50000")
		
		// 데이터를 저장할 버퍼
		buf := make([]byte, 512)
		
		// 서버로부터 데이터를 읽어옴
		conn.Read(buf)
		
		// 데이터 출력
		fmt.Printf("%s\n", buf)
	}()

	for {
		// 클라이언트 연결 요청까지 대기
		accept, err := listener.Accept()
		
		// 예외처리
		if err != nil {
			log.Print(err.Error())
		}

		// 클라이언트 소켓 핸들러
		go handleClient(accept)
		
		// 반복문 처음으로 돌아감
	}

}

func handleClient(conn net.Conn) {
	// hello 메세지와 클라이언트의 주소를 전달함
	_, err := conn.Write([]byte("hello " + conn.RemoteAddr().String()))
	
	// 예외처리
	if err != nil {
		log.Print(err.Error())
	}
}