little cat
소켓이란, 두 프로그램이 서로 데이터를 주고받을 수 있도록 양쪽에 생성되는 통신 단자를 의미한다. 즉, 소켓통신이란 이 소켓을이용한 통신을 의미하는것이다.
http 통신은 일단 html 파일을 전송하는 프로토콜이라는 의미를 가진다. 즉, 우리가 대부분 사용하는 html 기반 웹사이트들의 통신수단이다. 현재는 json파일이나 이미지 파일 역시 전송이 가능하다.
http는 클라이언트가 서버로 요청을 보내고, 서버가 이에 응답하는 방식으로 이루어진다. 클라이언트 요청에 반응하는 방식의 서버통신이다. 단방향 통신이라고도 한다.
http가 단방향 통신이라면, 소켓통신은 양방향 통신이다. 클라이언트와 서버가 양쪽에게 서로 데이터를 전달하고 전달받는다. 보통 라이브 스트리밍, 실시간채팅 등 실시간 데이터로 주고받아야할 때 connection을 끊어버리는것보다 유지하는게 더 비용이 싸게먹혀서 계속 유지하고 놔두는것이다. 대신 유지하는 만큼 리소스 소모는 더 크다.
그림에서 보이는것처럼, 양방향 통신이다. 따라서 게임서버, 채팅서버 등에 자주 이용된다.
서버 소켓은 서버쪽에서 사용하는 소켓이다. 클라이언트로부터 연락이 오기를 기다렸다가, 연락이 오는 순간 연결을 맺고 다른 소켓들을 만들어나간다 .
1) 생성 - 클라이언트쪽 요청이 오면 생성을 한다
2) 결합 - 서버가 사용할 ip 주소와 포트번호를 생성한 소켓에 결합한다
3) 주시 - 클라이언트로부터 연결요청이 수신되었는지 listen한 후
4) 받아들이기 - 클라이언트의 요청을 받아들인다음
5) 송수신 - 데이터를 송수신한다.
6) 닫기 - 모든 과정이 끝나면 서버를 닫는다.
클라이언트 소켓은 반대로 클라이언트 쪽에서 사용하는 소켓이다. 클라이언트 소켓이 먼저 보내야하기떄문에 우선 생성된다.
1) 생성 - 소켓 만들기
2) 연결 - 서버와 연결을 요청
3) 송수신 - 연결되면 송수신을 통한 통신
4) 닫기 - 완료되면 닫음
이제 좀 더 자세히 알아보도록 하겠다.
가장 먼저 수행되어야 하는일은 바로 클라이언트 소켓생성이다. 이 때, 소켓의 종류를 지정할수 있는데 TCP/IP는 Stream 타입, UDP는 datagram 타입을 지정할 수 있다.
최초로 생성된 소켓에는 연결대상에 대한 정보가 들어있지 않다. 이 소켓은 그냥 비어있고 이제 여기에다가 connect() 해서 API를 호출해야한다.
이 연결요청 API는 ip 주소와 포트번호로 식별되는 타겟으로 연결요청을 보낸다. connect() API는 블록방식으로 작동한다. 연결 요청에 대한 결과(성공, 거절, 시간초과)등이 결과값으로 나오기 전까지는 끝나지 않는다는뜻이다. 따라서 connect() API의 결과가 바로 리턴되지 않을 수도 있다.
이렇게 connect() API가 성공적으로 호출되면, send() / recv() API가 작동한다.
데이터 송수신을 담당하는 API이다. 두 API 모두 블록방식으로 작동한다. send의 경우 일단 보내면 작업이 끝나지만, recv의 경우 오류가 되었건 뭐가 되었건 받기 전까지 작업이 끝나지 않기때문에 API가 리턴되지 않는다.
송수신 소켓이 더이상 필요없게 되면 close API를 호출하여 소켓을 닫아버리고 더 이상 데이터를 송수신할 수 없게 만든다. 만약 다시 송수신을 원한다면 새로 socket() API를 호출해야한다.
서버쪽이 아무래도 클라이언트쪽보다는 조금 더 어렵다
클라이언트쪽과 마찬가지로 소켓을 생성하는 과정이다.
결합하는 API이다. 이 API는 포트번호 / 포트번호 + ip주소 로 구성된다.
소켓은 시스템이 관리하는 포트 0~ 65535 중 하나를 사용하게 되는데 이때 만약 소켓의 포트가 다른 포트와 중복된다면 어떨까? 어떤 소켓이 처리할 지 몰라서 데이터의 혼선이 발생할 것이다. 따라서 os는 소켓들이 중복된 포트를 사용하지 않도록 내부적으로 포켓번호와 소켓을 관리하는데 이를 요청하는것이 bind API이다.
즉 해당 소켓이 지정된 포트번호를 쓰겠다고 알리는것이다. 만약 이미 점유중인 포트라면 에러메세지를 리턴한다.
네트워크쪽에서 리슨이라는 단어를 자주 쓰는데 그 리슨과 일맥상통하다.
소켓이 bind API 까지 성공적으로 호출했다면 연결요청을 받아들일 준비가 끝난것이다. 그러면 연결요청이 수신될 때 까지 대기하는데 그 역할을 수행하는 API이다.
대기상태에 머무르면서 클라이언트의 연결요청이 있는지 확인한다. 클라이언트에서 호출된 connect API에 의해 연결ㅇ요청이 수신되는지 확인하다가 수신되면 상태를 종료하고 리턴한다.
혹은 에러가 발생하는 경우 대기상태를 깨고 리턴한다. 하지만 이 API가 통신의 어떤 정보를 가지고있지는 않다. 연결 요청이 수신되었는지, 아닌지에 대한 정보 (bool정보)만을 가지고있을 뿐이다.
listen API까지 완료했다면, 실질적인 연결을 수립해야한다. 이 API가 그 역할을 수행한다.
연결을 받아들이고 소켓간의 connection을 수립하는것이 이 API가 하는일이다.
다만 이때 연결되는 소켓은 앞서 bind listen 에 사용된 서버 소켓이 아니다. 이때 사용되는 소켓은 API내부에서 새로 만들어지는 소켓이다.
이는 서버소켓의 핵심 역할은 클라이언트의 연결요청을 수신하는것이지 통신하는것이 아니기 때문이다. 따라서 통신은 accept API 안에 있는 새로운 socket이 담당한다.
클라이언트 사이드와 동일하다.
이것도 클라이언트 사이드와 동일하지만, accept API에서 발생한 소켓도 관리해야한다.