삽질의 현장/- 네트워크 프로그래밍

#007_Window_Network_클라이언트 서버 통신 (TCP 서버)

shovelman 2015. 10. 3. 17:30


안녕하세요 삽잡이입니다.


아... 속이 안좋고... 머리가 아픕니다...

오늘은 평소보다 더!

중간 중간 했던 말들을 또 하는 루프 사태가 발생할 수 있으니... 양해바랍니다....

허허허....



이번 시간에는 클라이언트 서버 프로그램에 대해서

즉, 클라이언트와 서버가 어떻게 통신을 하는지에 대해 알아보겠습니다.


우선, 이 글은 TCP/IP 프로토콜 기반의 통신 방식을 알아보려고 하는 글입니다.

TCP/IP 프로토콜 말고도 여러가지 프로토콜들이 존재하는데,

각 프로토콜마다 통신하는 방법이 다릅니다.

그 여러가지 프로토콜 중에서 일반적으로 가장 널리 사용하는 TCP/IP 프로토콜 통신을 배워보겠다는 것이지요.


네트워크에서 많이 사용하는 모델 중 하나가 바로 '크라이언트와 서버 모델'입니다.


일반적으로 서버는 한 대가 존재하고, 클라이언트는 여러대가 존재합니다.

클라이언트에서는 서비스를 요청하고 서버에서는 서비스에 응답하는 형태를 가지는

네트워크 모델 혹은 방식을 가리켜 클라이언트 서버 모델이라고 하는 것이지요....


클라이언트 서버 모댈을 구축하는 대표적인 프로토콜은 바로 TCP/IP 프로토콜입니다.

가장 일반적으로 사용하는 프로토콜로써 

해당 프로토콜을 사용하여 구축하는 클라이언트 서버 모델 또한 아주 많이 사용합니다.


클라이언트 서버 모델은 순차 서버와 다중 처리 서버로 나뉠 수 있습니다.

거의 99%는 다중 처리 서버, 병렬 서버로 동작하게 됩니다.

무작위로 처리할 수 있도록 구현하는 것이지요...


본론으로 넘어와서,

프로세스간에 네트워크 통신을 어떻게 하는지 알아보도록 하겠습니다.


A PC와 B PC간 통신을 하려고하는데,

항상 이 프로세스들은 OS 위에서 동작함을 인지하셔야합니다.

즉, 개념상 A 라는 PC와 B라는 PC가 통신한다는 뜻은

A PC의 프로세스와 B PC의 프로세스가 통신하는 것이라는 뜻입니다.

컴퓨터마다 수 많은 프로세스가 있을 것입니다. 

그 많은 프로세스 중 하나의 프로세스가 통신을 하는 것이라는 뜻이지요....


서비스를 제공하는 제공하는 프로세스를 서버라고 부르고,

서비스를 요청하는 프로세스를 프로세스라고 부른다는 것입니다...

정확하게 서버라는 것은 PC를 서버라고 부르는 것이 아니라, PC내의 프로세스가 서버라는 것이지요.



A라는 서버가 있다고 해봅시다. 

그리고 클라이언트 프로세스 하나가 A서버에 붙었다고 가정하겠습니다.

또한, B라는 서버도 한 PC 안에 같이 있다고 해봅시다.

(서버/ 클라이언트들은 모두 프로세스로써 전혀 문제가 되지 않는 상황입니다.)

다른 클라이언트가 만약 B서버에만 관심이 있다면 

B와 서버 클라이언트 관계를 갖겠지요.


이처럼 네트워크 프로그램 간에는 프로세스와 프로세스 사이의 통신임을 알 수 있습니다.


컴퓨터 외부로 통신을 할 수 있지만,

자신의 컴퓨터 내에서도 클라이언트 서버 관계를 이룰 수 있습니다.

물론, 통신간에는 무조건 라우터가 있어야겠지요.

네트워크 통신에서는 필수적인 존재임을 기억하시길 바랍니다.

(물론 자신의 PC에서 통신하는 경우 라우터를 경유하지 않고 

자신의 TCP 스택만 경유하는 루프백 주소를 제공하고 있긴합니다.)


A라는 TCP 서버가 있다고 해봅시다. TCP 프로토콜을 사용하고 있는 서버 말입니다.

이 놈은 통신하기 위해서는 커널 객체가 하나 필요합니다.

그 커널 객체를 '소켓'이라고 부르는 것입니다.

'소켓'이란, 통신하기 위한 수단, 통신하기 위한 객체라고 생각하시면 됩니다.

우리는 소켓만 있으면 통신할 수 있습니다.

통신을 하기 위해 우리는 OS에 요청을 할 뿐이지요.


TCP 서버에는 두 가지 종류의 소켓이 있습니다.

하나는 대기를 위한 소켓인 listen socket, 다른 하나는 통신을 위한 소켓인 communication socket입니다.

통신을 위해서는 이 둘이 필요하다는 것입니다.




대기 소켓을 가장 먼저 생성해야합니다.

socket() 함수를 통해 생성하게 되는 것입니다.

아직까지는 대기 소켓이 아니라 소켓만을 생성한 것 뿐입니다.


통신 소켓은 직접 생성할 수 없습니다.

통신 소켓은 대기 소켓에 의해서만이 만들어질 수 있습니다.

한마디로 자동으로 만들어진다 이것입니다.

즉, 클라이언트가 만들어지면 대기소켓에 의해 자동으로 생성된다는 것입니다.


그 다음으로 bind() 함수를 통해 소켓에 속성을 부여해야합니다. bind는 '연결'이라는 뜻을 가지고 있습니다.

소켓을 생성했을 때 몇번 포트번호를 사용하고 몇번 IP를 사용할지 등의 결정이 되있지 않습니다.

그 결정을 bind() 함수를 통해 하는 것입니다.


다음으로 listen() 함수가 실행됩니다.

즉, 클라이언트 '접속 대기 큐'를 생성하는 것입니다.

listen() 은 대기인데, 실질적으로 여기서 대기하는 것이 아닌 

listen() 함수가 성공하는 순간 socket이 대기 소켓이 됩니다.


bind() 함수까지 성공이 되면 클라이언트를 받을 수 없습니다.

listen() 함수까지 성공을 해야 클라이언트를 받을 수 있게 되는 것입니다.


여담으로 말씀드리자면 C++의 경우 이렇게 세개의 과정으로 나뉘어져 있는데,

C#이나 자바는 하나로 이루어져있습니다....


아무튼... listen() 함수가 성공하면 이제 진짜 '대기 소켓'이 됩니다.

다시한번 정리하자면, 

socket() ~ listen() 함수 성공 전까지는 대기 소켓을 생성하기 위해 준비과정일 뿐,

listen()함수가 성공하면 그때부터 대기소켓이 되는 것입니다.

접속 할 수 있는 틀 즉, 환경을 만들어주기 때문입니다.


그 다음 대기소켓의 핵심 작업은 accept() 함수입니다.

해당 함수는 '통신 소켓'을 생성하는 작업을 수행합니다. 

사실, 이미 생성되어 있고 가져오는 작업을 하는 것이지만요...


만약, 서버가 다중 서버라면... 즉, 여러개의 클라이언트를 처리할 수 있는 서버라면

accept() 함수는 일반적으로 루프를 돌아야합니다.

한대만 처리하고 그만 하는 것이 아닌, 또 다른 클라이언트가 있는지 확인해야되기 때문이지요.

즉, 루프를 돌리는 이유는 여러 대의 클라이언트를 처리하기 위해서입니다.


listen() 함수는 대기 큐를 만드는 작업을 합니다.

그리고 accept() 함수는 접속 대기큐에 클라이언트가 들어오는지 뚫어지게 보는 놈입니다.

즉, accept()함수는 클라이언트가 접속하는지를 지켜보다가 접속을 하면

정보를 가지고 return 하는 것입니다.

accept()함수가 return 하는 놈은 통신 소켓의 핸들이구요...

참고로, socket()함수가 return 하는 놈은 대기 소켓으로 사용 할 소켓의 핸들을 말합니다.


다시 말씀드리지만, 클라이언트가 접속하게 되면 accept()함수가 반환하는 핸들은

통신할 수 있는 통신 소켓의 핸들이라는 것입니다.


클라이언트가 대기할 수 있는 접속 대기큐가 넓다면 여러대의 클라이언트가 동시 접속할 수 있습니다.

물론, 순차적으로 처리하겠지만요.

실질적으로는 동시에 접속할 수 있다는 사실을 말씀드립니다.


클라이언트 입장에서는 접속 대기큐에 들어가면 성공을 했다고 생각합니다.

클라이언트가 접속하게 되면 대기큐에 들어간 뒤 성공 return 값을 보내는 것이지요...

접속 대기큐에 들어가는 것 까지가 클라이언트의 연결을 시도하는 함수의 임무라는 것입니다!

클라이언트를 불러들여와 통신 소켓을 만드는 몫은 서버의 accept()의 몫이니깐요.


클라이언트와의 만남을 주선하고 대기 소켓은 다시 무한 루프를 도는 작업을 진행하겠지요.

만약, 서버에서 문제가 생겨 accept() 함수를 실패하면 close를 합니다.

close() 함수는 대기 소켓을 닫는 함수입니다.


아무튼.... 대기 소켓에 사용되는 함수들에 대해 소개했습니다.


accept() 함수가 성공하게 되면 return 값으로 통신 소켓 하나의 핸들을 던져줍니다.

접속 대기 큐에 아무 클라이언트도 없다면 block 상태에 있지요....

아하 accept() 함수도 block 함수이군요...


어찌됬건, 통신소켓이 만들어지면 통신 소켓은 recv() 함수 혹은 send() 함수를 날릴 수 있게 됩니다.

recv()함수는 데이터를 받아오고, send()함수는 데이터를 보내는 역할을 담당합니다.


마지막으로,

클라이언트와 통신을 하다가 문제가 발생하거나 접속을 끊고자할 경우

통신용 소켓의 리소스를 제거해줘야하기 때문에 closesocket()함수를 사용합니다.


이와 같이 통신 소켓은 세 가지의 작업을 수행합니다.

그 중에서 recv() 함수와 send() 함수는 무한 수행을 하고, 

끝날 때에는 closesocket()함수를 수행하는 것이구요.


그런데 통신 소켓은 하나일까요?

아닙니다. 대기 소켓이 루프를 돌다가 클라이언트가 있다면, 또 통신 소켓을 만듭니다.

따라서 recv(), send(), closesocket() 함수들 묶음이 또 다른 클라이언트와 이야기를 해야합니다.

따라서, 통신 소켓은 독립적이니 여러개가 있을 수 있습니다.


일반적으로 클라이언트 서버 모델에서 

서버쪽 대기 소켓은 하나지만, 통신 소켓은 0개 이상입니다.

즉, 없을 수도 있고 혹은 그 이상의 개수를 생성할 수 있다 이겁니다.


지금까지 TCP 서버에 대해서 알아봤습니다.

아직 클라이언트가 기다리고 있지요....

그 친구는 다음시간에 뵙도록 하겠습니다.


이상 삽잡이였습니다!