본문 바로가기

Golang/etc

tcp/ip - 01

728x90
반응형

윤성우의 열혈 tcp/ip 소켓프로그래밍을 보며 공부하고 쓰는 블로그

 

참고로 이 책은 술먹고 쿠팡에서 산건데 여튼 술에 취한내가 저지른 일을 감당하고자 블로깅까지 해본다. 

누구 보라고 쓰는건 아니고 나중에 까먹지 말자고 정리 차원에서 쓰는 블로깅이 될 것같다. 

 

이 책의 가장 처음에서는 소켓이 무엇인지, 서버-클라이언트 소켓 통신이 무엇인지 설명하고있다. 

그리고 c언어 코드로 구현되고 있는데, 나는 이걸 최대한 golang으로 바꿔서 작성하는식으로 공부해보고자 한다. 

 

먼저 용어 정리

- socket : 서로 멀리 떨어져 있는 host간에 데이터를 주고 받을 수 있도록 네트워크 망에 연결시키는 창구

운영체제에서 제공을 하고 있다. 

 

이 책에서는 socket 통신을 전화기의 작동 원리에 빗대어 표현하고 있다. 

1. socket 생성 - 전화기 구입 / 네트워크 통신을 하기 위해 먼저 그 도구인 소켓을 생성한다. 

2. bind - 전화번호 할당 / 다른 곳에서 걸려오는 전화를 받기 위해 전화번호 등 정보를 등록하는 행위이다. 

네트워크 프로그래밍에서는 ip와 port number를 할당해준다. 

3. listen - 연결 요청이 가능한 상태로 만든다. / 전화국에 이제 연결해도 된다고 등록하는 행위라고 생각하면된다. 

4. accpet - 연결 요청에 대한 수락 / server(listener) socket은 연결 요청을 수락하고, client socket은 연결 요청을 진행한다. 

5. connect - 함수 호출을 통한 서버로의 연결 요청 과정

 

그리고 이 것을 golang 코드로 구현해보면,

 

listener socket 

func handler(conn net.Conn) {
	// 받은 msg 저장할 buffer
	recv := make([]byte, 4096)

	for {
		// read msg from client
		n, err := conn.Read(recv)
		if err != nil {
			if err == io.EOF {
				fmt.Println("connection is closed from client : ", conn.RemoteAddr().String())
			}
			fmt.Println("Failed to receive data : ", err)
			break
		}
		// msg print
		if n > 0 {
			fmt.Println(string(recv[:n]))
			// connection에 보낼 메시지 write
			//conn.Write(recv[:n])
		}
	}
}

func main() {
	// socket 생성
	listener, err := net.Listen("tcp", ":10000")
	if err != nil {
		fmt.Println("Failed to Listen : ", err)
	}
	// 프로세스가 멈추면 listener도 close
	defer listener.Close()

	// accept
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("Failed to Accept : ", err)
			continue
		}

		// accept 한 후 client에서 받은 메시지 handle
		go handler(conn)
	}
}

net.Listen 함수로 bind를 해주면서, listen 상태인 socket을 생성한다. 

그리고 accept로 connection을 생성하면 이 connection을 통해 전달 받은 데이터를 출력하는 식으로 코드를 구현했다. 

 

client socket

func main() {
	// dial & connect to server
	conn, err := net.Dial("tcp", ":10000")
	if err != nil {
		fmt.Println("Failed to Dial : ", err)
	}
	defer conn.Close()

	// go routine 익명함수
	go func(c net.Conn) {
		// 보낼 메시지
		send := "Hello"
		for {
			// connection에 보낼 메시지 write
			_, err = c.Write([]byte(send))
			if err != nil {
				fmt.Println("Failed to write data : ", err)
				break
			}
			// 1초마다 반복
			time.Sleep(time.Second * 1)
		}
	}(conn)

	go func(c net.Conn) {
		// connection으로 부터 전달 받은 msg 저장할 buffer
		recv := make([]byte, 4096)

		for {
			// read msg from conn
			n, err := c.Read(recv)
			if err != nil {
				fmt.Println("Failed to Read data : ", err)
				break
			}
			fmt.Println("Recv data : ", string(recv[:n]))
		}
	}(conn)

	// 사용자 입력이 들어올때까지 blocking 했다가 입력을 마치면 서버로 전송
	for {
		var s string
		fmt.Scanln(&s)
		conn.Write([]byte(s))
		time.Sleep(time.Duration(3) * time.Second)
	}
}

listener socket에 dial 한 후 메시지를 1초 간격으로 보내고, 또 서버에서 보낸 데이터를 읽어들이는 코드이다. 

 

일단 이렇게 간단하게 socket 통신을 구현해봤다. 

728x90
반응형

'Golang > etc' 카테고리의 다른 글

tcp/ip 04  (0) 2022.06.28
tcp/ip -02,03  (0) 2022.06.27
Golang Apple Login (애플 로그인)  (0) 2022.06.14
Apple 로그인 JWT, JWK  (1) 2022.06.14
golang .env 파일 환경변수 셋팅  (0) 2022.04.13