미국 공학박사 아빠의 코딩 연구소

네트워크 통신하기

요즘 웹 사이트, 모바일 게임, 클라우드(Cloud) 등 많은 컨텐츠나 기술이 인터넷을 기반으로 되어 있지요?
이처럼 네트워크를 기반으로 소통/통신을 하는 방법에는 여러가지가 있는데요. 그 중에 하나인 소켓(Socket)을 이용한 방법을 공부해 보려고 해요. 이것을 이용하면, 본인만의 채팅 프로그램도 만들 수 있는 등 이용할 수 있는 곳이 많답니다.
코딩을 하는 중에 튜플(Tuple)이라는 데이터 타입도 공부할 거예요.


강의들은 모두 무료입니다. 단, 저작권은 키클 코딩랩 에 있으며, 무단 복제 및 배포를 엄금합니다.
이를 어길 시, 본 사이트의 서버가 미국에 있으므로, 미국법에 의해 처벌될 수도 있습니다.




소켓(Socket)이란?

소켓(Socket)은 네트워크 상에 존재하는 각각의 지점/노드(node)들을 의미해요. 질문들에 응답하는 서버(Server)나 서버에 질문을 문의하는 클라이언트(Client)들 모두 각각이 소켓인 것이죠. 단, 어떤 소켓이 서버이냐 혹은 클라이언트냐에 따라서 역할이 달라질 뿐이랍니다.
이 소켓을 이용한 네트워크 통신 방법을 알면, 본인이나 가족만의 새로운 채팅 프로그램을 개발할 수도 있고, 현재 세상을 촘촘히 연결하는 인터넷 등의 네트워크에서 어떠한 방식으로 의사소통이 이루어지는지 알수도 있어요. 또한, 어떤 대상과 원격으로 소통할 수도 있답니다. 실제로, 저는 로봇을 원격으로 조종할 수 있는 프로그램을 이 소켓을 이용해서 만든적이 있지요.
이번 강의에서는 소켓을 이용하여 서버와 클라이언트를 만들고 이 둘이 어떻게 통신을 하는지 알아보고자 해요.




서버(Server) 코딩하기

이번 강의에서는 서버와 클라이언트를 각각의 Python파일에 코딩할 거랍니다. 그러니, 총 2개의 Python파일을 만들건데요.
먼저, 서버부터 만들어 보지요.
원하는 폴더에 새로운 Python파일을 만들고, "lec11_server.py"라고 파일 이름을 지어 보지요. 그리고 아래와 같이 서버 소켓을 생성하고 설정하는 코딩을 할게요.

줄1은 socket라이브러리를 import하는 것이예요. 이 라이브러리가 없으면, 소켓 프로그래밍을 못하니 꼭 import해야 한답니다.
줄3은 socket라이브러리 내에 있는 socket클래스를 아무런 전달인자 없이 생성하는 것이랍니다. 라이브러리 내에 있는 대상을 이용할 때에도, 클래스의 속성이나 메서드를 이용할 때와 같이 "."을 이용하는 점 기억해 두세요.
줄4에서는 네트워크 상에서 서버가 갖는 호스트명 혹은 주소를 변수 host에 할당하는 건데요. 보통 각 컴퓨터마다 유일하게 가지고 있는 IP주소를 사용하지요. 가령, 서버의 IP주소가 "1.2.3.4"이고, 클라이언트의 IP주소가 "4.3.2.1"이라면, 줄4에서는 당연히 "1.2.3.4"를 사용하여야 겠지요. 그런데, 서버와 클라이언트를 같은 컴퓨터에서 돌려야 한다면, 편의상 "컴퓨터 자신"을 의미하는 IP주소인 "127.0.0.1"를 사용한답니다. 숫자로 된 IP주소를 사용하기 귀찮다면, "localhost"라고 해도 같은 의미를 나타내지요. 줄4에서는 "localhost"를 사용했고, "127.0.0.1"도 사용할 수 있다는 의미에서 뒤에 주석으로 붙여놨어요.
줄5에서는 Port번호를 변수 port에 저장하고 있지요. 한 컴퓨터에 여러가지의 네트워크 프로그램을 돌릴수가 있는데요. 각 프로그램을 식별하게 위해 부여하는 번호가 port번호랍니다. 우리는 5000을 포트 번호로 사용하는 것으로 해볼게요.
줄6에서는, bind() 메서드를 이용하여, 위에서 생성한 소켓에 줄4~줄5에서 지정한 IP주소와 Port번호를 설정하는 코드랍니다. 이제 이 소켓은 "127.0.0.1"이라는 주소를 갖는 컴퓨터에서 돌아가는 "5000"의 포트번호를 갖는 서버가 되는 것이죠.
그런데, 자세히 보면, 전달인자로 변수 host와 port를 "( )"로 묶어서 전달하고 있지요? 이런 데이터 타입을 튜플(Tuple)이라고 하는데요. 한 번 알아보고 가지요.

[문법] 튜플 (Tuple)

다시 코드로 돌아와서, 줄7은 소켓에게 외부에서의 연결을 받을 수 있다는 것을 알리는 코드예요. 전달인자로 주어지는 숫자는 연결이 이뤄지기를 기다리는 연결 요청의 최대 허용 갯수를 의미한답니다. 우리는 1개까지만 대기 허용을 하는 것으로 하지요.

이어서 서버의 코딩을 아래와 같이 완성해 보지요.

줄9는 서버 소켓이 본격적으로 연결 요청을 받기 전에, 연결 요청을 기다리고 있다는 문구를 터미널에 출력하기 위한 코드랍니다.
줄10의 accept()는 서버 소켓에게 클라이언트로부터 연결 요청을 기다리라고 명령하는 함수/메서드예요. 이후 코드들의 실행을 멈추고 클라이언트에게서 연결 요청이 올때까지 기다리게 된답니다. 연결 요청이 오고, 연결이 이루어지면, 해당 클라이언트와 연결을 이루는 새로운 소켓 객체를 생성하고, 클라이언트의 주소 정보와 함께 리턴한답니다. 우리는 이 두개의 리턴값을 conn과 addr이라는 변수에 저장하지요.

클라이언트와 연결이 이루어지면, 줄12가 실행이 되는데요. 줄12에서는 연결된 클라이언트에 보낼 메세지를 정의하고 있어요.
줄13에서는, 클라이언트와 연결을 이루는 소켓 conn의 send()을 이용해서 줄12에서 정한 메세지를 클라이언트에게 전송하는 코드예요. 그런데, 전달인자로 "msg"가 아닌 "msg.encode('utf-8')"이라는 것을 전달하고 있지요? 이것은 컴퓨터가 데이터를 전송할 수 있도록 인코딩(encoding)을 하기 위한 것인데요. 인코딩이란, 사람이 다룰 수 있는 정보를 컴퓨터가 다룰 수 있는 형태로 변환하는 것을 의미해요. 예를 들어, 사람이 듣는 음악은 mp3나 wav같은 형태로 인코딩하여 컴퓨터에 저장하지요. 마찬가지로, 문자열은 utf-8 이나 ASCII 등과 같은 포맷으로 인코딩을 한답니다. 그래서, "msg.encode('utf-8')"은 사람이 읽을 수 있는 문자열이 담긴 msg를 utf-8형태로 인코딩하고 그 정보를 되돌려(리턴) 주는 것이지요.

줄15는 클라이언트에서 전송하는 데이터를 받기 위한 코드예요. 곧 클라이언트도 코딩을 하겠지만, 클라이언트에서 줄12의 메세지를 서버로부터 받으면, 곧장 클라이언트도 서버에게 메세지를 보내게 할 거랍니다. 줄15는 그 메세지를 받기 위함인 것이죠. 소켓의 recv()메서드를 사용했는데요, 전달인자로는 한 번에 받을 수 있는 메세지의 크기를 byte단위로 받는 답니다. 즉, 1024는 한 번에 받을 수 있는 메세지의 크기가 최대 1024 byte 라는 것을 의미하지요.
줄16은 클라이언트로부터 받은 메세지를 터미널에 출력하는 코드예요. 그런데, 이번에는 "data"가 아닌 "data.decode('utf-8')"을 전달인자로 부여하고 있어요. 이것은 컴퓨터가 다룰 수 있게 utf-8포맷으로 인코딩된 데이터를 사람이 볼 수 있도록 디코딩(decoding)하는 코드랍니다.
줄18은 모든 통신이 끝나고, 클라이언트와 연결을 유지하는 소켓 conn을 close()메서드로 닫는 코드예요.




클라이언트(Client) 코딩하기

이제, 클라이언트 코딩을 해볼까요?
서버 Python파일과 같은 폴더에 새로운 Python파일을 만들고, "lec11_client.py"라고 파일 이름을 지어 보지요.
클라이언트는 서버를 코딩한 것과 차이점이 별로 없이 비슷하답니다. 아래 보이는 코드가 이번 강의에서 만들 클라이언트 코드의 전부랍니다. 서버 코드랑 대동소이하지요?

줄1은 socket라이브러리를 import 하는 것이고, 줄3은 클라이언트를 위한 소켓 객체를 생성하는 것이죠.
줄4에서는 서버의 IP주소를 적는 것인데, 클라이언트와 같은 컴퓨터에서 돌릴 것이기 때문에, "localhost" 혹은 "127.0.0.1"을 이용해야 한답니다.
줄5에서는, 서버 프로그램이, 위에서 설명했듯이, Port번호 5000을 사용할 것이기 때문에, 서버를 정확히 찾아가기 위해서 동일한 번호를 설정해야 되지요.
줄6은 서버 코드와 다른 부분인데요. 서버와 달리 bind()대신 connect()메서드를 사용한답니다. 이것은 host주소에서 작동하고 있는 port의 포트번호를 갖고 있는 서버에게 연결요청을 하는 코드인 것이지요. 단, 서버의 bind()메서드를 사용할 때와 마찬가지로 host와 port를 튜플로 묶어서 전달한답니다.
줄8~줄9는 서버로부터 전송되는 메세지를 받아서 터미널에 출력하는 코드인데요, 서버에서와 똑같은 방식이지요. 즉, 줄8에서 최대 1024 byte 크기의 메세지를 recv()메서드를 이용해서 받고 있구요. 줄9에서는 utf-8로 인코딩되어서 온 그 메세지를 디코딩하여 출력하는 것이랍니다.
줄11에서는 send()메서드로 서버에 메세지를 보는 것인데, 역시나 서버에서와 마찬가지로 utf-8로 인코딩하여 전송하고 있지요. 단, 주목할 점은, 서버에서와 다르게, "message from client"라는 문자열을 다른 변수에 저장하지 않고 곧장 인코딩을 했다는 것이랍니다.
줄13에서는 모든 통신이 끝나서 소켓을 닫고 있지요.




통신 실행하기

서버와 클라이언트를 모두 코딩을 했으니 실행해 보지요.
서버와 클라이언트 Python파일들을 저장하고, 터미널에서 "python3 lec11_server.py"로 서버를 실행해 보세요. 그럼, "waiting..."이라는 메세지가 출력이 되면서 클라이언트의 연결 요청을 기다리는 상태로 들어가게 되지요.
이제, 아래 보이는 것처럼 터미널 창 위의 아무 지점에서나 마우스 오른쪽 버튼을 누르면 나오는 팝업 메뉴에서 "Open Terminal"을 클릭해서 새 터미널 창을 여세요.

그리고, "python3 lec11_client.py"로 클라이언트를 실행해 보세요. (실행 방법에 대해서 복습이 필요하면 여기로)
실행하면, 서버 터미널에서는 클라이언트에서 전송된 "message from client"라는 메세지가 출력되고, 클라이언트에서는 서버에서 전송된 "message from server"라는 메세지가 출력되지요.

강의에서 작성된 소스 코드 (source code)를 다운받으려면, 다음 링크를 클릭하세요: 서버 소스 코드 다운로드, 클라이언트 소스 코드 다운로드
혹시, 이해가 잘 안되는 부분에 대한 질문이 있거나 다루어 줬으면 하는 주제가 있으면, 화면 오르쪽 하단에 "질문하기" 버튼을 이용해 주세요.






발자취

2019-09-11 "키클 코딩랩 - 미국 공학박사 아빠의 코딩 연구소"로 이름 변경
2019-06-28 코딩 교실 공개
2019-03-18 코딩 교실 제작 시작

바로가기
About
Contact
Privacy Policy
강의목록
질문하기
처음으로