- 네트워크를 통해 서버로부터 데이터를 가져오기 위한 통신 방식 2가지 :
(ref: https://blog.naver.com/jamsuham75/222337979985)
> 단방향적 통신으로, 서버가 클라이언트 쪽으로 요청을 보낼 수 없음.
> 주로 콘텐츠 위주의 데이터를 사용할 때 유용 (인터넷으로 주소를 검색하여 연결)
* 작동방식 : 클라이언트가 평범한 HTTP Request를 서버로 계속 요청해 이벤트 내용을 전달받는 방식.
* 장점 : 가장 쉬운 방법
* 단점 : 클라이언트가 지속적으로 Request를 요청하기 때문에 클라이언트의 수가 많아지면 서버의 부담이 증가.
: HTTP Request Connection을 맺고 끊는 것 자체가 부답이 맣은 방식이고, 클라이언트에서 실시간 정도의 빠른 응답을 기대하기 어려움!
* 작동방식 : 클라이언트에서 서버로 일단 HTTP Requet를 요청
: 그 상태로 계속 대기하다가 서버에서 클라이언트로 전닳할 이벤트가 있다면 그 순간 Response 메시지를 전달하며 연결 종료
: 곧 클라이언트가 다시 HTTP Request를 요청해 다음 이벤트를 기다리는 방식
* 장점 : HTTP Poling 방식보다 서버의 부담이 줄어들 수 있음
* 단점 : 클라이언트로 보내는 이벤트들의 시간간격이 좁다면 Poling과 별 차이가 없음.
: 다수의 클라이언트에게 동시에 이벤트가 발생될 경우 서버의 부담이 급증.
* 작동방식 : 클라이언트 -> 서버로 HTTP Request를 요청
: 클라이언트로 이벤트를 전달할 때, 해당 요청을 해제하지 않고 핋요한 메시지만 보냄(Flush)
* 장점 : Long Poling과 비교하여 서버에 메세지를 보내지 않고도 다시 HTTP 연결을 하지 않아도 되어 부담이 줄어듬.
* Network Socket : 네트워크 환경에 연결할 수 있게 만들어진 연결부
* 서버와 클라이언트가 특정 port를 통해 연결하여 실시간 양방향 통신을 하는 방식.
* HTTP 통신과 달리 Socket 통신은 서버 역시 클라이언트로 요청을 보낼 수 있고, 실시간으로 연결이 유지됨.
* 실시간 스트리밍 혹은 채팅과 같은 즉각적인 양방향 정보 교환이 필요한 경우 많이 사용.
> 데이터를 주고받기 위해서는 먼저 소켓의 연결 과정이 선행되어야 함
> 클라이언트 소켓: 연결 요청
> 서버 소켓 : 수신
* 두 개의 시스템 (또는 프로세스)이 소켓을 통해서 Connection 하기 위해서는 최초 어느 한 곳에서 대상이 되는 곳으로 연결을 요청해야함.
* IP주소와 포트번호로 식별되는 대상에게 자신이 데이터 송수신을 위한 네트워크 연결을 수립할 의사가 있을을 알리는 것.
* 단, A에서 연결을 시도한다고 무조건 연결되어 데이터를 주고 받을 수는 없음
* 대상이 되는 곳에서 요청을 받아들일 준비가 되어있지 않으면 해당 요청은 무시되고 연결은 만들어지지 않음.
* 요청을 받아들이는 곳에서는 어떤 연결 요청(대부분 포트 번호)을 받아들일 것인지 미리 등록하여, 요청이 수신되었을 때 해당 요청을 처리할 수 있도록 준비해야 함
* 이 과정에서 두 개의 시스템(또는 프로세스)이 소켓을 통해 데이터 통신을 위한 연결(Connection)을 만들기 위해서는, 연결 요청을 보내는지 또는 요청을 받아들이는지에 따라 소켓의 역할이 나뉘게 됨
> 연결 요청을 보내는 소켓 : 클라이언트 소켓 (Client Socket)
> 연결 요청을 받아들이는 소켓 : 서버 소켓 (Server Socket)
* 클라이언트 소켓과 서버 소켓은 역할과 구현 절차 구분을 위해 다르게 부르는 것.
> 역할에 따라 처리되는 효율. 즉, 호출되는 API함수의 종류와 순서들이 다를 뿐 동일한 소켓(Socket)
* 소켓 연결이 되면 클라이언트소켓과 서버소켓이 직접 데이터를 주고받는 것이 아님.
> 서버 소켓은 클라이언트 소켓의 연결 요청을 받아들이는 역할만 수행.
> 직접적인 데이터 송수신은 서버소켓의 연결 요청 수락의 결과로 생성되는 새로운 소켓을 통해 처리.
(1) Client Socket
1. socket() : 소켓을 생성
* 소켓 통신을 위해 가장 먼저 해야 할 일
* 이 때, 소켓의 종류를 지정할 수 있음 (TCP 소켓 : Stream 타입/ UDP 소켓 : Datagram 타입 등)
* 최초 소켓이 생성될 때에는 "연결 대상"에 대한 어떠한 정보도 담고있지 않음. (껍데기 소켓만 생성)
* 연결 대상(IP PORT)을 지정하고 연결 요청을 전달하기 위해서는, 여기서 생성한 소켓을 사용하여 connect() API를 호출해야 함.
2. connect() : 서버측에 연결을 요청
* connect() API는 "IP주소"와 "포트 번호"로 식별되는 대상(Target)으로 연결 요청을 보냄
* connect() API는 블럭(block) 방식으로 동작.
> 연결 요청에 대한 결과 (성공/ 거절/ 시간 초과 등)가 결정되기 전에는 connect()의 실행이 끝나지 않음
> connect() API가 실행되자마자 실행 결과와 관계 없이 무조건 바로 리턴 될 것이라고 가정해선 안됨.
* connect() API 호출이 성공하면, send() / recv() API를 통해 데이터를 주고 받음
3. send() / recv() : 서버측에서 연결이 받아들여지면 데이터를 주고 받음
* 연결된 소켓을 통해 데이터를 보낼 때는 send(), 받을 때는 recv() API를 사용
* 두 API 모두 (block)방식으로 동작
> 결과 (성공/ 거절/ 시간 초과 등)가 결정되기 전에는 API가 리턴되지 않음
> 특히, recv()의 경우 데이터가 수신되거나, 에러가 발생하기 전에는 실행이 종료되지 않기 때문에, 데이터 수신 작업을 생각만큼 단순하게 처리하기 쉽지 않음
- send()의 경우 데이터를 보내는 주체가 자기 자신이기 때문에, 얼마만큼의 데이터를 보낼 것인지, 언제 보낼 것인지 알 수 있음.
- 그러나 recv()의 경우 통신 대상이 언제, 어떤 데이터를 보낼 것인지를 특정할 수 없기 때문에 recv() API가 한번 실행되면 언제 끝날지 모르는 상태가 되는 것.
> recv() API는 별도의 스레드에서 실행.
(소켓의 생성과 연결이 완료된 후, 새로운 스레드를 하나 만든 다음 그 곳에서 recv()를 실행하고 데이터가 수신 되는 것을 기다림)
4. close() : 모든 처리가 완료되면 소켓을 닫음
* 데이터 송수신이 필요 없게 되면 close() API로 소켓을 닫음.
* 닫힌 소켓은 유효한 소켓이 아니기 때문에, 해당 소켓을 사용하여 데이터를 송수신 할 수 없음.
* 또 다시 생성 하려면 socket()과 connect() 과정을 통해서 send/recv 준비가 되어야 함.
(2) Server Socket
1. socket() : 소켓을 생성
2. bind() : 서버가 사용할 IP주소와 포트번호를 생성한 소켓을 연결
* bind() API에 사용되는 인자는 소켓과 포트번호(또는 IP주소+포트번호)
* 일반적으로 서버 소켓은 고정된 포트번호를 사용하고, 그 포트 번호로 클라이언트의 연결요청을 받아들임.
* 운영체제가 특정 포트 번호를 서버 소켓이 사용하도록 만들기 위해 소켓과 포트 번호를 결합하는 API
> 운영체제는 소켓이 사용하는 포트 번호가 다른 소켓의 포트 번호와 중복되지 않도록 내부적으로 포트 번호와 소켓 연결 정보를 관리.
> bind() API는 해당 소켓이 지정된 포트 번호를 사용할 것이라는 것을 운영체제에 요청하는 API
> 만약 지정된 포트 번호를 다른 소켓이 사용하고 있다면 bind() API는 에러를 return
3. listen() : 클라이언트로부터 연결 요청이 수신되는지 확인
* isten() API는 서버 소켓(Server Socket)에 바인딩된 포트 번호로 클라이언트의 연결 요청이 있는지 확인하며 대기
* 클라이언트에서 호출된 connect() API에 의해 연결 요청이 수신되는지 대기하다가, 요청이 수신되면, 그 때 대기 상태를 종료하고 리턴
* listen() API가 대기 상태에서 빠져나오는 경우
(1) 클라이언트 요청이 수신
(2) 에러가 발생(소켓 close() 포함)
* listen() API가 성공한 경우라도, 리턴 값에 클라이언트의 요청에 대한 정보는 들어 있지 않음
> isten()의 리턴 값으로 판단할 수 있는 것
(1) 클라이언트 연결 요청이 수신되었는지(SUCCESS)
(2) 그렇지 않고 에러가 발생했는지(FAIL)
* 클라이언트 연결 요청에 대한 정보는 시스템 내부적으로 관리되는 큐(Queue)에서 쌓이게 되는데, 이 시점에서 클라이언트와의 연결은 아직 완전히 연결되지 않은(not ESTABLISHED state) 대기 상태.
> 대기 중인 연결 요청을 큐(Queue)로부터 꺼내와서, 연결을 완료하기 위해서는 accept() API를 호출해야 함.
4. accept() : 요청이 수신되면 요청을 받아들이고 데이터 통신을 위한 소켓을 생성
* 소켓 연결(Connection)을 수립하는 절차
* 최종적으로 연결 요청을 받아들이는 역할을 수행
* 주의할 점은 최종적으로 데이터 통신을 위해 연결되는 소켓이, 앞서 bind() 또는 listen() API에서 사용한 서버 소켓(Server Socket)이 아님.
> 최종적으로 클라이언트 소켓(Client Socket)과 연결이 만들어지는 소켓은 앞서 사용한 서버 소켓(Server Socket)이 아니라, accept API 내부에서 새로 만들어지는 소켓(Socket)
- 서버 소켓(Server Socket)의 핵심 역할은 클라이언트의 연결 요청을 수신하는 것
- 이를 위해서 bind() 및 listen()을 통해 소켓에 포트 번호를 바인딩하고 요청 대기 큐를 생성하여 클라이언트의 요청을 대기
- accept() API에서, 데이터 송수신을 위한 새로운 소켓(Socket)을 만들고 서버 소켓의 대기 큐에 쌓여있는 첫 번째 연결 요청을 매핑. 하나의 연결 요청 처리 종료.
- 이후에는 다른 연결 요청을 대기(listen) or 서버 소켓을 닫을(close()) 수 있음
* 실질적인 데이터 송수신은 accept API에서 생성된, 연결(Connection)이 수립(Established)된 소켓(Socket)을 통해 처리.
5. send()/recv() : [4]에서 생성된 소켓을 통해 연결이 수립되면 데이터를 주고 받음
6. close() : 데이터 송수신이 완료되면 소켓을 닫음
> 기존의 단방향 HTTP 프로토콜과 호환되어 양방향 통신을 제공하기 위해 개발된 프로토콜.
> 일반 Socket 통신과 달리 HTTP Port를 사용하므로 방화벽에 제약이 없으며 통상 WebSocket으로 불린다.
> 접속까지는 HTTP 프로토콜을 이용. 그 이후 통신은 자체적인 Web Socket 프로토콜로 통신.
* WebSocket이 기존의 TCP Socket과 다른 점은 최초 접속이 일반 HTTP Request를 통해 HandShaking 과정을 통해 이뤄진다는 점.
* HTTP Request를 그대로 사용하기 때문에 기존의 80, 443 포트로 접속을 하므로 추가 방화벽을 열지 않고도 양방향 통신이 가능하고, HTTP 규격인 CORS 적용이나 인증 등 과정을 기존과 동일하게 가져갈 수 있는 것이 장점
* 변경 사항의 빈도가 자주 일어나지 않고, 데이터의 크기가 작은 경우
> Ajax, Streaming, Long polling 기술이 더 효과적일 수 있음.
* 실시간성을 보장해야 하고, 변경 사항의 빈도가 잦다면, 또는 짧은 대기 시간, 고주파수, 대용량의 조합인 경우
> WebSocket이 효과적일 수 있음.
* TCP/IP 접속 그리고 웹소켓 열기 HandShake 과정으로 나눌 수 있음.
* 웹소켓도 TCP/IP위에서 동작하므로, 서버와 클라이언트는 웹소켓을 사용하기 전에 서로 TCP/IP 접속이 되어있어야 함
* TCP/IP 접속이 완료된 후 서버와 클라이언트는 웹소켓 열기 HandShake 과정을 시작
> 웹소켓 열기 (HandShake)
- 클라이언트가 먼저 핸드셰이크 요청을 보내고 이에 대한 응답을 서버가 클라이언트로 보내는 구조
- 서버와 클라이언트는 HTTP 1.1 프로토콜을 사용하여 요청과 응답을 보냄
* 문제점 : 모든 클라이언트의 브라우저에서 WebSocket을 지원한다는 보장이 없다.
또한, Server/Client 중간에 위치한 Proxy가 Upgrade헤더를 해석하지 못해 서버에 전달하지 못할 수 있다.
마지막으로 Server/Client 중간에 위치한 Proxy가 유휴 상태에서 도중에 Connection 종료시킬 수도 있다.