본문 바로가기

리눅스 소켓 프로그래밍 TCPIP

TCP 서버 - 클라이언트

TCP 서버 - 클라이언트 개념

 

서버 - 클라이언트 예시

웹 서버와 웹브라우저가 동작하는 모습

브라우저는 사용자가 입력한 주소를 해석하여 대기중인 웹서버 접속

-> HTTP 이용하여 요청 보냄

-> 서버는 웹브라우저에서 받은 요청을 분석 후 HTTP를 이용해 응답

-> 브라우저는 서버에서 받은 데이터를 처리해 화면에 표시

 

웹 서버 - 클라이언트 는 대표적인 TCP 서버 - 클라이언트 응용 프로그램이다.

 

 

TCP서버의 핵심동작

1. 서버는 먼저 실행하여 클라이언트 접속을 기다린다 listen( )

2. 클라이언트는 서버에 접속 connect( ) 하여 데이터를 보낸다 send( )

3. 서버는 클라이언트 접속을 수용 accept( ) 하고 클라이언트가 보낸 데이터를 받아 recv( ) 처리한다.

4. 서버는 처리한 데이터를 클라이언트에 보낸다 send( )

5. 클라이언트는 서버가 보낸 데이터를 받아서 recv( ) 처리한다.

6. 데이터를 주고받는 과정을 모두 마치면 접속을 끊는다. close( )

 

 


TCP 서버 - 클라이언트 동작원리

 

서버는 소켓을 생성한 후 클라이언트의 접속대기

서버 소켓은 특정 결합된 포트 번호가 있어 이 포터 번호로 접속하는 클라이언트만 수용할 수 있다.

 

클라이언트 서버접속

TCP 프로토콜 수준에서 연결 설정을 위한 패킷 교환이 일어난다.

TCP 연결 시 세개의 파일을 주고 받음 SYN,SYN / ACK,ACK 라고 부른다

 

 

 

TCP 수준에서 연결 절차가 끝나면 서버는 접속한 클라이언트와 통신할 수 있는 소켓 생성

새로운 소켓을 이용해 통신한다.

 

기존 소켓은 다른 클라이언트 접속을 위해 대기 소켓 

두 클라이언트도 접속 가능하다. 

기존의 소켓은 새로운 클라이언트를 위해 대기한다.


 

 

서버와 클라이언트 소켓은 일대일로 대응한다

하나의 클라이언트가 둘 이상의 소켓을 사용하여 서버에 접속 할 수 있다.

 

 


TCP 서버 - 클라이언트 분석

  • 프로그램 관점에서 소켓은 운영체제의 TCP/IP 구현에서 제공하는
    데이터 구조체를 참조하기 위한 매개체이다.
  • 응용 프로그램이 통신하려면 결정되어야 하는 요소
    - 프로토콜 : 통신 규약으로 소켓을 생성할 때 결정한다.
    - 지역 : IP주소와 지역포트번호 (서버 또는 클라이언트의 주소)
    - 원격 : IP주소와 지역포트번호 (서버 또는 클라이언트가 통신하는 주소)

 

TCP서버 함수

 

 

1. socket( )

    -> 함수로 소켓을 생성하므로써 사용할 프로토콜을 결정한다.

2. bind( )

    -> 소켓의 지역 ip 주소와 지역 포트 번호를 결정한다.

3. listen( )

     -> 소켓의 TCP 상태를 LISTENING 상태로 변경한다.

4. accept( )

     -> 클라이언트 접속을 수용, 클라이언트와 통신할 새로운 소켓을 생성한다.

          -> 원격 ip 주소와 원격 포트번호가 결정된다.

5. send( ), recv( )

     -> 데이터 전송 함수로 통신을 수행한 수 close( )로 함수 소켓을 닫는다.

6. 새로운 클라이언트 접속이 들어올 때 마다 4~5 과정을 반복한다.

 

bind( ) 함수

소켓의 지역 IP주소와 지역 포트번호를 설정한다

 

 

C

#include <sys/type.h>
#include <sys/socket.h>

int bind ( 
         ① int sock
         ② const struct sockaddr *addr
         ③ socklen_t addrlen
);

1. 클라이언트 접속을 수용하는 소켓으로 지역ip, 포트번호가 아직 결정되지 않은 상태
2. 소켓 주소 구조체로 통신 프로토콜에 따른 주소 정보를 담고 있다
     -> TCP/IP 의 경우 sockaddr_in ,sockaddr_in6 지역 주소ip와 포트번호를 초기화하여 전달함
3. 소켓 주소 구조체의 길이 (바이트)

 

 

 

listen( ) 함수

소켓의 TCP 상태를 LISTENING 으로 바꾼다.

LISTENING -> 클라이언트 접속을 받아들일 수 있는 상태가 되었다는 의미

 

C

#include <sys/type.h>
#include <sys/socket.h>

int listen ( 
          ① int sock
          ② int backlog
);

1. 클라이언트 접속을 수용하는 소켓으로 이전에 bind( ) 함수를 호출하여
    지역 IP주소와 지역 포트번호를 설정한 상태이다.
2. 접속가능한 클라이언트의 개수 클라이언트의 접속 정보는 연결큐에 저장된다
     backlog 는 이 연결큐의 길이를 나타낸다.
     지원 가능한 최대 값은 SOMAXCONN

 

accept( ) 함수

  • 클라이언트의 접속을 수용하고, 접속한 클라이언트와 통신할 수 있는
    새로운 소켓을 생성하여 리턴한다.
  • 클라이언트의 주소 정보 (서버입장에서 원격IP)도 함수 인수를 통해 얻을 수 있다.
C

#include <sys/type.h>
#include <sys/socket.h>

int accept ( 
          ① int sock
          ② struct sockaddr *addr
          ③ socklen_t *addrlen
);

1. 클라이언트 접속을 수용하는 소켓이다. 이전에 bind( ) 함수로 지역 IP주소와
    포트번호를 설정하고 listen( ) 함수로 TCP 상태를 LISTENING 상태로 만든다.
2. 소켓 주소 구조체를 전달하면 접속한 클라이언트의 주소정보로 채워진다.
3. addr 이 가리키는 소켓 주소 구조체의 크기로 초기화 하여 전달한다

accept( ) 함수가 리턴하면 *addrlen에는 accept( )함수가 채워넣은 주소의 크기(바이트)가 저장된다.

 

TCP 클라이언트 함수

 

1. socket( )

     -> 함수로 소켓을 생성함으로써 사용할 프로토콜을 결정한다.

2. connect( )

     -> 함수로 서버에 접속한다. 원격 포트번호, 원격IP주소, 지역 포트번호, 지역IP주소 를 결정한다

3. send( ), recv( )

     -> 데이터 전송 함수로 서버와 통신한 후 close( ) 함수로 닫는다.

 

connect( ) 함수

TPC 프로토콜 수준에서 서버와 논리적 연결을 설정

 

C

#include <sys/type.h>
#include <sys/socket.h>

int connect ( 
          ① int sock
          ② struct sockaddr *addr
          ③ socklen_t *addrlen
);

1. 서버와 통신하는 소켓
2. 소켓 주소 구조체 (sockaddr_in)를 서버주소로 초기화 하여 전달한다.
3. 소켓의 주소길이 (바이트)

 

bind( ) 함수를 호출하지 않음

connetc( ) 함수를 호출하면 운영체제가 자동으로 지역IP주소와 포트번호를 할당해준다.

 

 

 

TCP 데이터 전송 함수

지역, 원격 주소정보 이외 데이터 송수신 버퍼가 있다.

     송신버퍼 : 데이터를 전송하기 전 임시로 저장하는 영역

     수신버퍼 : 데이터를 프로그램이 읽어가기 전까지 임시로 저장해두는 영역

     소켓버퍼 : send( ), recv( ) 는 소켓버퍼에 접근하는 함수

 

send( ) 함수

 

C

#include <sys/type.h>
#include <sys/socket.h>

int send( 
          ① int sock
          ② const void *buf
          ③ size_t len
          ④ int flag
);

1. 통신할 대상과 연결 된 소켓
2. 보낼 데이터를 담고 있는 응용 프로그램 버퍼의 주소
3. 보낼 데이터의 크기
4. send( ) 함수의 동작을 바꾸는 옵션 거의 항상 0을 사용한다.

 

 

recv( ) 함수

 

C

#include <sys/type.h>
#include <sys/socket.h>

int recv( 
          ① int sock
          ② void *buf
          ③ size_t len
          ④ int flag
);

1. 통신할 대상과 연결ㄹ된 소켓이다
2. 받은 데이터를 저장할 응용 프로그램 버퍼 주소
3. 운영 체제 수신버퍼로부터 복사할 최대 데이터의 크기
     -> 이 값은 buf 가 가리키는 응용 프로그램 보다 크지 않아야한다.
4. recv( ) 함수의 값을 바꾸는 옵션
     - MSG_PEEK : 수신 버퍼에 데이터가 계속 남는다.
     - MSG_WAITALL : len크기 만큼 데이터가 수신 버퍼에 도착해서
                                   응용프로그램 버퍼에 전부 복사할 때 까지 기다린다.



-> recv( ) 함수의 기본 동작은 수신 버퍼의 데이터를 최대 len 크기만큼
응용 프로그램 버퍼에 복사한 후 해당 데이터를 수신 버퍼에서 삭제하는 것이다
수신버퍼가 len보다 작으면 데이터가 다 도착하지 않아도 현재 있는 만큼만 데이터를 복사하고 리턴함

 

recv 함수는 두 종류의 성공적인 리턴이 있음

-1 수신 버퍼에 데이터가 도달한 경우

len보다 크지 않은 범위에서 응용 프로그램 버퍼에 최대한 복사 한후 실제 복사한 바이트 수를 리턴함

-> 이 경우 recv( )의 리턴값은 최소 1 최대 len 임

-2 

접속을 정상 종료한 경우

close로 호출하여 정상종료 하면 패킷 교환 절차가 일어난다

-> 이 경우 recv( ) 의 리턴값은 0을 리턴한다

 

 

recv 함수 사용 시 len으로 지정한 것보다 적은 데이터가 응용 프로그램 버퍼에 복사 될 수 있다.

   -> TCP 가 데이터 경계를 구분하지 않아서 발생

 

받아야하는 데이터의 크기를 알고 있다면 다 받을 때 까지 기다리도록 WAITALL 을 사용하면 좋다.