kakasoo

[TCP/IP] TCP 기반 서버, 클라이언트 구현 본문

프로그래밍/네트워크

[TCP/IP] TCP 기반 서버, 클라이언트 구현

카카수(kakasoo) 2020. 7. 17. 12:59
반응형

오늘은, TCP 기반의 서버를 만들어보고자 한다, 당연히 서버만 있으면 동작 여부를 모르니 클라이언트도 만든다.

복습 겸, 지금까지 소켓의 생성부터 데이터 송수신에 관한 것을 정리해보도록 하자.

 

 


server측

0. WSAStartup() // 함수 요청

1. socket() // 소켓을 생성한다.

2. bind() // 소켓에 정보를 바인드한다, 즉 주소 정보를 할당한다.

3. listen() // 소켓을, 연결 요청 대기 상태로 한다.

4. accept() // 정보가 들어왔을 경우에 연결을 허용한다.

5. read() / write() // 데이터를 송수신한다. 이는 리눅스일 경우의 함수이고, 윈도우일 때에는 send()와 connect()였다.

6. close() // Socket을 close() 한다.

 

이런 순서였다.

마찬가지로, 서버도 동일한 순서로 이루어지게 될 것인데, 각 함수마다 추가적인 기능들이 요구될 것이다.

이전에 설명했던 함수들에 대해서 더 자세한 설명을 하고, 넘어가는 것으로 하자, 지금부터 할 얘기는,

리눅스 기반 / 윈도우 기반에 관해서, 동시에 이야기할 것이고 되도록이면 공통점 위주로 설명할 것이다.

만약에 차이가 있다면 윈도우 기반으로 작성하겠다. (애초에 내가 윈도우 기반으로 만들고 싶기 때문이다.)

 

  • int listen(int sock, int backlog) == Linux
  • int listen(SOCKET sock, int backlog) == Window
  • 성공 시에는 0을 반환하고 실패 시에는 -1을 반환한다, 모든 함수가 실패 시 -1 (또는 -1인 매크로상수)를 반환하므로 이에 관한 설명은 이제 생략하도록 하겠다.
  • sock은 server의 파일 디스크립터, 또는 서버 소켓 객체를 의미한다, 같은 의미이나 리눅스와 윈도우에서 사용하는 표현이 차이가 있어 모두 작성하였다.
  • backlog는 연결요청 대기 큐의 크기 정보로, 만약 5를 전달하면 큐에 연결 요청 5개까지 대기한다, 서버 소켓 하나에 여러 개의 클라이언트가 오는 경우가 당연하니, 만약 소켓이 이미 연결된 상태에서도 오류가 발생하지 않도록, 대기할 수 있는 공간을 만든 것이 연결 요청 대기 큐이다.
  • 서버처럼 잦은 요청이 오는 경우에는 5도 작다, 15 이상으로 만들어야 안정적인데, 이 숫자는 실험에 의거해야 한다.

 

클라이언트의 연결 요청 수락

사실, 서버와 클라이언트 소켓을 두 개 만드는 것에 의문을 품어 본 적은 없지만, 책에서는 다음과 같이 설명한다.

왜 서버 소켓을 사용하지 않느냐고. (여기서 말하는 것은 server.c 에서 소켓이 2개여야 하는 거 아니냐는 뜻이다.)

풀어 설명하자면, 서버 측에 연결 됐을 때, 정상적인 연결인 경우 정보를 주는 소켓이 하나 더 필요한데,

이 때 서버 소켓을 쓰면 안되는 이유가 있냐는 것이다.

답은 서버 소켓은 끝까지, 서버 측 데이터를 지키는 문지기 역할로 남아 있어야 하기 때문.

그런데 우리는 서버 file에서 서버 소켓 말고 다른 소켓을 만든 기억이 없을 텐데, 이는 아래의 함수가 해결해준 것이다.

 

  • int accept (int sock, struct sockaddr* addr, socklen_t* addrlen) == Linux
  • SOCKET accpet(SOCKET s, struct sockaddr* addr, int* addrlen) == Window
  • 성공 시 반환 값으로, 생성된 소켓의 디스크립터를 반환한다, 윈도우에서는 반환이 socket 객체이다! 이제부터 반환 값이 int인 경우와 SOCKET인 경우를 구태여 구분하지 않고, 하나로 작성하겠다.
  • sock or s == 서버 소켓의 파일 디스크립터 또는 객체
  • addr은 연결 요청한 클라이언트 소켓의 주소를 담아놓을 변수, 당연히 미리 선언되어 있어야 한다.
  • addrlen은  addr 주소 값 변수의 크기를 의미한다. (함수 호출 완료 훙는 주소정보 길이가 바이트단위로 변환된다.)

여기서 생성된 소켓은 자동적으로 server 측의 데이터 송수신 소켓이 되고, 클라이언트와 연결된다.

 

client측

TCP 클라이언트의 기본적인 함수 호출 순서는 다음과 같다.

1. socket() // 클라이언트 측 소켓을 생성한다.

2. connect() // 연결한다.

3. read() / write() // 데이터를 송수신한다. 이는 리눅스일 경우의 함수이고, 윈도우일 때에는 send()와 connect()였다.

4. close() // Socket을 close() 한다.

마찬가지로 각 함수들에 대해서 보도록 하자. (봐야할 것은 이제 connect() 뿐이지만.)

 

  • int connect(int sock, struct sockaddr* servaddr, socklen_t addrlen)
  • sock은 클라이언트 소켓의 파일 디스크립터, 당연히 window에서는 SOCKET s를 의미한다.
  • servaddr는 연결 요청할 서버의 주소정보를 담은 변수의 주소 값이며,
  • addrlen은, 앞서 설명한 accep와 마찬가지로 두번째 매개변수의 크기 값을 의미한다.
  • 앞서 listen()에서 말한 것과 같이, 연결 대기 큐가 listen() 함수에 있어서 connect()가 동작한다고 해서 바로 연결이 이루어지는 것은 아니다, 경우에 따라서는 오래 대기할 수도 있다.
반응형