티스토리 뷰


연결형 서버 프로그램 작성 절차

그림 2-11 TCP(연결형) 서버를 iterative 형태 , 서비스 요구가 들어오는 순서대로 처리해 주는 형태로 구축하는 절차를 나타냈다.

서버는 socket()으로 소켓을 개설하고 bind() 수행한 listen()으로 소켓을 수동 대기모드로 만든다.

다음에는 accept() 호출하여 자신에게 연결을 요청하는 클라이언트의 연결을 처리하도록 한다.

하나의 서비스를 완료하면 다음 요청을 반복적으로 처리한다.


1. socket() 소켓 생성

socket(PF_INET, SOCK_STREAM, 0);

서버에서도 마찬가지로 소켓을 생성해야 한다.


(2) bind(), 소켓번호와 소켓주소 구조체 연결

▶서버에서도 마찬가지로 socket()시스템 콜을 하면 소켓번호를 리턴한다.

그러나 번호는 응용 프로그램만 알고 사용하는 번호이므로 프로그램이 컴퓨터 외부와 통신하려면 

    이 소켓번호와 TCP/IP 시스템이 제공하는 소켓주소(IP 주소 + 포트번호) 연결해 두어야 하며 이를 위하여 bind() 사용한다.

▶ 위 오른쪽 그림에 bind() 호출시의 IP 주소, 포트번호, 그리고 소켓번호의 관계를 나타냈다.

bind() 소켓번호와 서버의 아이피, 포트번호를 연결하는 시스템 콜이다.

서버에서 bind() 반드시 필요한 이유는 임의의 클라이언트가 서버의 특정 프로그램이 만든 소켓과 통신을 하려면 소켓을 찾을 있어야 하며, 따라서 서버는 소켓번호와 클라이언트가 알고 있을 서버의 IP 주소 포트번호(, 서버의 소켓주소) 미리 서로 연결(bind)시켜 두는 것이 필요하기 때문이다.


bind() 시스템콜은 성공시 0 실패시 -1을 리턴하며 bind(소켓번호,소켓구조체,소켓구조체길이)형식으로 호출한다.


int bind (int s, struct sockaddr *addr, int len);



아래의 프로그램 코드는 소켓을 만들고 이것을 IP 주소가 203.252.65.3이고 포트번호가 3000번인 소켓주소 구조체와 bind()하는 것이다.


inet_addr()은 문자열로 된 dotted decimal IP 주소 203.252.65.3를 2진수 IP 주소 1010111 10110010 00101101 00000011로 바꾸는 함수htons()->host to network short는 호스트 바이트 순서의 숫자 3000번을 네트웍 바이트 순서로 바꾸기 위하여 사용되었다.


네트워크 바이트 순서와 호스트 바이트 순서

네트워크 프로그래밍을 할때에는 나와 다른 시스템을 사용하는 컴퓨터와 통신을 해야 할 수 도 있기 때문에 송수신되는 바이트의 순서를 신경 써줘야 한다. 즉 바이트의 맨 왼쪽이 MSB LSB 냐가 시스템마다 달라질수 있다는 얘기이다. 

우리가 흔히 쓰는 인텔 씨피유는 리틀엔디안으로 처리하지만 네트워크 표준은 빅엔디안 이기 때문에 호스트의 바이트순서를 네트워크 표준 바이트 순서로 바꿔야 한다 그때 사용하는것이 htons이다.


(참고)엔디언(Endianness)은 컴퓨터의 메모리와 같은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법을 뜻하며, 바이트를 배열하는 방법을 특히 바이트 순서(Byte order)라 한다.빅엔디안은 우리가 흔히 알고있는 MSB가 맨 왼쪽에 있는 형태이고 리틀엔디안은 맨 왼쪽에 LSB가 있다.


bind()문에서 소켓주소 구조체를 나타내는 함수 인자로 server_addr 바로 사용하지 않고(struct sockaddr*) &server_addr 사용한 것을 있다.

대부분의 인터넷 소켓 프로그램에서는 인터넷 주소를 편리하게 다루기 위하여(, IP 주소와 포트번호를 직접 기록하거나 읽을 있도록) sockaddr_in 구조체를 사용하고 있다.

▶ bind() 함수를 비롯한 각종 소켓 함수의 정의에서는 일반적인 소켓주소 구조체인 sockaddr 사용하도록 정의되어 있기 때문에 구조체 타입을 바꾸는 casting 필요한 것이다. (sockaddr_in 타입에서 -> sockaddr타입으로)

위에서 자신의 IP 주소로 203.252.65.3 구체적으로 지정하였다.

응용 프로그램이 수행되는 컴퓨터 자신의 IP 주소를 자동으로 가져다 쓰려면 INADDR_ANY라는 변수를 다음과 같이 사용하면 된다.

즉 server_addr.sin_addr.s_addr = inet_addr("203.252.63.3") 을 server_addr.sin_addr.s_addr = htonl(INADDR_ANY);으로 대체 가능하다는뜻


위에서는 포트번호로 3000번을 지정하여 사용하였지만, 포트번호를 0으로 하고 bind()를 호출하면 시스템(즉, TCP/IP)이 포트번호를 자동으로 배정해 준다.


▶ 서버에서 socket()과 bind()를 호출하여 통신을 할 준비가 된 후, 데이터를 송수신하는 절차는 소켓 개설시 지정한 트랜스포트 프로토콜의 종류에 따라 다르다.

▶ 연결형 통신(TCP)에서는 listen(), accept()의 호출이 필요하고 비연결형 통신(UDP)에서는 바로 데이터의 송수신이 가능하다.

▶ 우선 연결형 서버의 경우에 대하여 설명하겠다.


(3) listen(), 클라이언트로부터의 연결요청을 기다리기

▶ 서버는 클라이언트로부터의 연결요청을 받아들이기 위하여 이를 기다리고 있어야 하는데 이를 위하여 listen()을 호출한다.

int listen (int s, int log); //s는 소켓번호 , log는 몇명의 클라이언트 까지 요청을 받아줄것인가

예를들어 아래의 코드는 서버가 최대 2개의 connect() 요청을 대기시킬 있으며, 번째 이후의 connect() 요청은 거절하여 클라이언트가 사실을 바로 있도록 해준다.

listen(s, 2);

한편 listen() 소켓을 단지 수동 대기모드로 바꾸는 것이므로 listen() 호출은 즉시 리턴되는데 성공시에는 0, 실패시에는 -1 리턴된다.


(4) accept(), 클라이언트로부터의 연결요청 수락

▶ 서버가 listen()을 호출한 이후에 어떤 클라이언트에서 connect()로 이 서버에 연결요청을 보내오면 이를 처리하기 위해 서버는 accept()를 호출해 두어야 한다.

▶ accept()의 수행이 성공한 경우에는 접속된 클라이언트와의 일 대 일 통신에 사용할 새로운 소켓이 만들어지고 accept()는 이 소켓번호를 리턴하며 실패시에는 -1을 리턴한다.

▶ accept()는 또한 접속된 클라이언트의 소켓주소 구조체와 구조체의 길이의 포인터를 함수인자 addr과 addrlen으로 각각 리턴한다.

▶ accept()의 사용 문법은 아래와 같고 accept() 호출시에 얻는 값들을 그림 2-13에 나타냈다.


int accept (
int s,struct sockaddr *addr,int *addrlen); 

s->소켓번호 addr->클라이언트의 소켓주소 구조체, addrlen->구조체 크기


(5) close(), 소켓 종료

▶ 소켓을 닫을 때 close()를 호출하는데 데이터그램(UDP) 소켓에서 close()를 호출하면 단순히 사용하던 소켓을 닫는 작업만 수행한다.

▶ 그러나 스트림(TCP)소켓은 연결형 서비스이므로 현재 미처리된 패킷들(송신 버퍼에 있으나 아직 송신이 안 된 패킷 또는 현재 송수신중에 있는 패킷)을 모두 처리한 후에 소켓을 닫게 된다.




댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함