네트워크 프로그래밍을 한번 쯤 해보신 분이라면 호출 순서가 조금 복잡하다고 느끼셨을지도 모릅니다. 그래서 개념적으로 설명을 드리고 함수를 설명하려 합니다. 하지만 여기서 말하는 함수를 모르셔도 아직은 상관 없습니다. 앞으로 다시 자세한 설명을 할테니까 말이죠.
우리가 네트워크 코드를 짤때 '전화기'에 비유해서 생각해 볼 수 있습니다. 네트워크 프로그래밍은 크게 둘로 '서버' 그리고 '클라이언트' 측으로 나눌 수 있습니다. 물론 서버와 클라이언트의 역할을 둘다 하는 프로그램을 만들 수도 있지만 그리 효율적인 방법은 아닙니다. 많은 사람들이 서버프로그램을 따로 만들어 놓고 다른 클라이언트들이 서버가 운영중일때 접속 할 수 있도록 하고 있습니다. 앞으로도 그런 방식으로 강좌를 진행할 겁니다.
그럼 서버는 무엇이고 클라이언트는 무엇일까요? 전화기에 비유한다면
서버 : 전화를 받는사람.
클라이언트 : 전화를 거는 사람.
이렇게 정의할 수 있겠습니다. 물론 아주 적절한 예는 아니지만 어느정도는 비유가 되지요
서버와 클라이언트가 서로 접속해서 데이터를 주고 받으려면(채팅이나 파일전송 등등..) 먼저 서버가 실행중인 상태여야 하고 이 서버 프로그램은 클라이언트의 접속을 허락하고 잘 관리할 수 있어야 합니다. 그래서 서버는 굉장히 중요한 네트웍 프로그래밍의 핵심이라 할 수 있겠네요.
그럼 서버는 어떻게 구현할 수 있느냐? 우리가 전화기를 사용할땐 아무런 일도 하지 않고 그냥 전화기만 코드에 꼽아놓기만 하면 저절로 전화가 걸려옵니다. 더이상 아무것도 신경쓰지 않아도 됩니다. 전화번호를 할당받는 일이나 전화가 걸려오게 만들어주는건 한국통신에서 다 알아서 해주기 때문에 우리는 걸려오는 전화를 받기만 하면 되죠. 하지만 우리가 만들 서버 프로그램은 전화번호를 할당하는 것에서 시작해서 전화가 걸려오도록 하는것까지 다 신경을 써서 만들어야 합니다.
그럼 어떤것 부터 해야 할까요? 당근 전화기가 있어야 겠죠? 전화기를 네트웍 프로그래밍에선 '소켓(Socket)' 에 비유할 수 있습니다. 그럼 소켓을 만들어주는 함수를 보도록 하죠.
SOCKET socket( int af, int type, int protocol );
이 함수는 SOCKET 데이터 타입을 리턴합니다. SOCKET은 '전화기'에 비유할 수 있습니다. 이제 우린 이 리턴된 SOCKET값을 잘 저장해서 데이터를 전송할 수 있게 됩니다.
하지만 아직 끝난게 아니죠. 전화기만 있으면 뭐합니까? 전화번호를 할당 받아야 합니다. 전화번호를 할당해야만 다른 곳에서 해당 전화번호를 눌러 전화를 걸려오게 할 수 있는 것이죠. 그럼 전화번호를 할당받는 함수를 볼까요?
int bind( SOCKET s, const struct sockaddr* name, int namelen );
이 함수 첫번째 인자를 보면 SOCKET데이터형을 인자로 받는군요. 여기에다가 socket함수가 리턴한 값을 넣어주면 될것같다는 생각이 들지요?
이제 전화번호 할당까지 받았으니 전화가 걸려올 수 있도록 전화기를 잭에 연결시켜야 겠네요. 즉, 네트웍프로그래밍으로 생각해본다면 소켓을 만들었고, 클라이언트들이 접속할 수 있는 주소까지 할당을 받았으니 이제 서버를 돌리는 일만 하면 되는겁니다. 이럴때 아래의 함수를 사용합니다.
int listen( SOCKET s, int backlog );
여기서도 첫번째 인자로 SOCKET 데이터형을 받는군요. 마찬가지로 socket함수의 리턴값을 적어주면 됩니다.
이제 우리의 서버 프로그램은 기나긴 여정을 지나 드디어 클라이언트의 접속을 수락받을 수 있게 되었습니다. 앞으로 클라이언트가 접속 요청을 해오면 우리는 이 접속요청을 수락해야 합니다. 접속요청을 허락해야 클라이언트와 데이터를 주고 받을수 있지 않겠어요? 즉, 전화기가 걸려오면 받아야 전화건 사람과 대화를 할 수 있다는 것입니다. 이럴때 사용하는 함수가 accept 함수 입니다.
SOCKET accept( SOCKET s, struct sockaddr* addr, int *addrlen );
클라이언트가 접속요청을 해오면 서버에서 accept로 수락을 합니다.
그다음에는 뭘해야 할까요? 물론 데이터를 주고 받아야 합니다. 여러분 중에서 전화를 받고 아무런 행동도 하지 않으신 분은 없을테죠. 전화가 오면 대화를 합니다. 다음은 대화를 하는 함수에 해당하는 함수들 입니다.
데이터를 전송하는 함수
int send( SOCKET s, const char* buf, int len, int flags );
데이터를 수신하는 함수
int recv( SOCKET s, char* buf, int len, int flags );
지금까지 함수를 나열하면서 함수에 대한 부연설명을 하지 않았는데, 아직은 중요하지 않습니다. 함수의 사용법은 앞으로 하나씩 차차 설명드릴 거구요 지금 중요한건 흐름입니다. 서버 프로그램을 어떤 순서로 프로그래밍을 하는지 순서를 파악하는게 중요합니다.
자 이제 전화기를 들여놓고 전화번호도 할당받았고, 전화기도 연결했으며 전화도 받고 대화도 했습니다. 그럼 이제 뭘 해야 될지 생각해 보세요. 당연히 전화를 끊어야 겠죠. 대화가 끝났으니 전화를 끊는건 당연한 겁니다.
int closesocket( SOCKET s );
여기까지가 서버의 구현 흐름도 입니다. 머리가 복잡한 분들을 위해서 다시한번 순서를 되새겨 봅시다.
1. socket함수 : 소켓을 생성한다( 전화기를 만든다 )
2. bind함수 : 주소를 바인딩 한다( 전화기에 번호를 할당한다 )
3. listen함수 : 클라이언트의 접속을 기다린다( 전화기가 울리도록 선을 연결한다 )
4. accept함수 : 클라이언트의 접속을 수락한다( 전화가 오면 받는다 )
5. send, recv함수 : 클라이언트와 데이터를 주고 받는다( 전화로 대화한다 )
6. closesocket함수 : 클라이언트와의 연결을 종료한다( 전화를 끊는다 )
이렇게 크게 6가지로 나눠볼 수 있겠네요. 참고로 여기서 설명드리는 방식은 TCP방식입니다.
여러분 TCP와 UDP란말 많이 들어보셨을 겁니다. 아직은 중요한 사항이 아니니 자세히 들어가진 않겠습니다만 TCP는 신뢰성이 있는 프로토콜 이라는 정도로만 기억을 해두십시오. 이유는 차차 알게 되실 겁니다.
- Reference
'[ Windows Program ] > Windows API' 카테고리의 다른 글
주소 사용법( 주소체계, 바이트순서 ) (0) | 2011.03.07 |
---|---|
주소 할당 함수 bind (0) | 2011.03.07 |
소켓 프로그래밍 흐름도(클라이언트) (0) | 2011.03.07 |
소켓 생성 함수 socket() (0) | 2011.03.07 |
윈속(WinSock)의 기초 (0) | 2011.03.07 |