본문 바로가기
기타/아카이브

[Network Basic] 2. Socket

by Riverandeye 2020. 5. 27.

Socket

소켓은 communication link의 endpoint이고, 어플리케이션은 소켓을 통해 모든 네트워크 정보를 전송한다.

Unix 계열에선 POSIX 소켓을 주로 사용한다. Unix Socket, BSD Socket, POSIX Socket은 같은 것을 의미한다.

윈도우 소켓 API는 Winsock 으로 불린다. Berkeley socket에 compatible 하도록 구성되어있다.

 

소켓은 두가지 타입으로 분류된다.

Connection-oriented

Connectless

Connectionless라는게 실제로 Connection이 존재하지 않는 상태에서 데이터를 전송한다는 의미는 아니다. 

 

요즘 시대에 자주 쓰이는 프로토콜은 두가지이다

- TCP -> Connection-oriented

- UDP -> Connectionless Protocol

 

Connectionless & Connection-oriented Protocol

UDP와 같은 connectionless Protocol에서는, 각 데이터 패킷이 개별적으로 구분되며, 전후 오는 패킷과 완전히 독립적이며 무관하다. 그러다보니 패킷이 도착하는지에 대한 보장을 하지 않고, 같은 패킷이 여러번 전달될 수도 있다. 

 

reliable communication이 필요하면, 각 패킷을 넘버링하고, receiver로 하여금 해당 패킷이 도착했다고 하는 ACK 를 요청할 수 있을 것이다. 수신자가 한 패킷을 받으면, 리턴 메세지를 보낸다. 이런 식으로 수신자로 하여금 전달자가 패킷을 올바른 순서로 받았는지를 확인할 수 있다. 같은 패킷이 두번 전달되면 중복된 패킷을 무시하고, 전달이 안됬으면 ACK 메세지가 오지 않았으니 수신자가 패킷을 받지 못했다고 인지할 수 있다. 

 

위 방식은 Connection-Oriented Protocol의 한 예시인 TCP에서 사용하는 방식이다. TCP는 보내진 순서 그대로 사용자가 패킷을 전달받는걸 보장하는 프로토콜이다. 중복해서 패킷을 수신하는 것을 막고, 수신되지 않은 데이터를 다시 전송한다. connection이 중단됬을 때 notification을 보내거나, network congestion를 방지하는 알고리즘이 있다던지.. 다양한 기능을 제공한다. 그것도 Custom UDP 보다 더 효율적으로 말이다. 

 

TCP를 이용하는 high-level protocol들은 다양하다. HTTP, FTP, SSH, SMTP 등..

 

UDP는 패킷 하나에 request와 response를 담을 수 있어 DNS에 사용되고, 스트리밍, 멀티플레이 게임과 같은 real-time application 에 주로 사용된다. 그런 어플리케이션은 굳이 연결성을 보장할 필요가 없다. 예를 들어 비디오 스트리밍에서 패킷 몇개가 drop 되었을 때, 다음 패킷이 오면 다시 resume 되기 마련이다. 

 

UDP는 또 Response를 기대하지 않고 보낼때 유용하다. IP broadcast 혹은 multicast를 적극 활용할 수 있는것이다. 

반면 TCP는 connection을 보장해야하고, IP multicast와 broadcast를 지원하지 않는다. 

 

결국 Packet numbering과 순서를 보장하기 위해 드는 delay가 아무 쓸모 없는 상황에서는 UDP가 사용되고, Connection이 보장되고 순서가 보장되는 상황에선 TCP가 사용되는 것이다.

 

Socket Apis (functions)

기본적으로 알아두면 좋은 api들의 목록이다. 

 

socket() -> socket 초기화 및 생성

bind() -> 소켓을 Local IP address와 Port로 지정하는 것

listen() -> 서버가 새로운 Connection 요청을 기다리는 것

connect() -> client로 하여금 remote address와 port를 설정하게 한다. 

accept() -> 들어온 TCP 요청을 연결함.

send() -> 데이터를 보냄 , recv() -> 데이터를 받음

sendto() recvfrom() -> bound remote address 없이 데이터를 주고 받음

close(), closesocket() -> 소켓을 닫음 (terminate connection)

shutdown() -> TCP connection 한쪽을 제거함 -> orderly connection teardown을 보장

select() -> 하나 이상의 소켓에 이벤트가 발생하는 걸 기다림

getnameinfo(), getaddrinfo() => hostname과 주소를 protocol과 관계 없이 제공

setsockopt() => 소켓 옵션 설정

fcntl() , ioctlsocket() => 소켓 옵션 설정

 

Flow of Socket Program

TCP

 

TCP Connection Flow

 

아주 일반적인 Flow는 다음과 같다. 

서버가 먼저 본인 hostname과 port 번호를 지정하고, 소켓을 초기화하고, 해당 소켓을 특정 IP와 Port에 바인드한다. 

그 후 listen을 통해 새로운 connection을 받을 수 있는 상태로 전환한 후 accept를 통해 서버로 커넥션이 올 때 까지 기다린다. 

커넥션이 연결되면 accept는 새로운 소켓을 반환하고, 해당 소켓과 send, recv 메소드를 통해 데이터를 주고받을 수 있따. 이전 소켓은 새로운 connection을 기다리며 여러 연결을 동시에 처리할 수 있다. 

 

TCP client도 connect 이전에 bind를 할 수 있다. 네트워크 인터페이스를 명시해줘야 하는 상황에 (network interface가 여러개일때) 적합하다. 

 

UDP

UDP Connection Flow

TCP는 누가 먼저 send하고 recv 하는지 관계 없는데, UDP는 먼저 sendTo를 해야한다. Remote Peer가 데이터를 받아보지 않는 이상 어디서 요청이 오는지를 모르기 떄문이다 (TCP의 connect 과정(handshake)이 없다)

UDP 서버도 마찬가지로 네트워크 인터페이스의 주소값을 확인하고 소켓 초기화 후 바인드 해야 한다. 

recvfrom 을 통해 client로부터 데이터가 전송될때까지 기다린다. 데이터가 들어오면 그 떄 reply 하거나 더 많은 데이터가 들어오길 기다릴 수 있다. 

 

 

 

댓글