# 리눅스 소켓과 동작방식

bhs9610·2020년 10월 20일
0
post-thumbnail

리눅스 소켓과 소켓프로그래밍에대해 정리한 글입니다.

소켓 (Socket)


소켓이란 뭘까? 소켓의 뜻은 단순 "연결", "콘센트" 등의 의미를 가진다. 예를들어, 냉장고에 전기를 공급받을 수 있게 공급해주는 연결부가 "소켓"이라고 생각하면 된다.

네트워크에서도 크게 벗어나지 않는다. 프로그래밍이 통신 할 수 있게 네트워크에 연결하는 연결부가 바로 "네트워크 소켓" 이라고 할 수 있다. 프로토콜 별로 "TCP/IP" 소켓 "UDP 소켓" 이라고도 부를 수 있다.

TCP/IP Socket Programming


소켓을 통해 시스템간 네트워크 연결을 할려면 IP주소와 포트번호를 보고 따라간다. 하지만, 무작정 요청한다고 해서 연결되는것이 아니다 요청을 받는 시스템에서 요청을 어떻게 받아드리고, 처리할 것인지 준비해아한다. 여기서 요청을 보내는 쪽이 "클라이언트 소켓" 요청을 받는 쪽이 "서버 소켓" 이락 볼 수 있다.

여기서, 서버 소켓은 직접 데이터를 주고 받는것이 아니라 요청에 연결을 수행할 뿐, 실제 데이터는 요청 수락의 결과로 만들어진 소켓에 이에 처리된다.

  • 클라이언트 소켓의 흐름
  1. 클라이언트 소켓은 소켓을 생성(Create) 한다.
  2. 서버 측에 연결 요청(Connect)을한다.
  3. 서버 소켓에서 연결이 받아지면 송수신(Send/recv)한다.
  4. 처리가 완료되면 소켓을 닫는다(Close)
  • 서버 소켓의 흐름
  1. 서버 소켓은 소켓을 생성(Create) 한다.
  2. 서버가 사용할 IP주소와 포트번호를 생성한뒤 결합(Bind) 한다.
  3. 요청이 수신되는지 주시(Listen) 한뒤, 수신되면 받아들여(Accept) 데이터 통신을 위한 소켓을 생성한다.
  4. 연결이 수립(ESTABLISHED) 되면 데이터를 송수신(send/recv) 할 수 있다.
  5. 마지막으로 송수신이 완료되면 소켓을 닫는다(Close).

클라이언트의 소켓 프로그래밍


  • 클라이언트 소켓 생성 socket()

소켓 통신을 하기위해 먼저 소켓을 생성한다. 이 때 TCP(Stream), UDP(Datagram) 타입을 지정할 수 있다. 최초 소켓이 만들어 지는 시점에서는 어떠한 Connect 정보도 들어있지 않다. 껍데기 뿐인 소켓이 하나 만들어 진거라 생각하면 된다. 연결대상을 지정하려면 connect() API 를 호출한다.

  • 연결 요청 connect()

connect() API 는 IP주소와 포트번호로 식별되는 대상(Target) 으로 연결 요청을 보낸다. connect() API은 블럭(Block) 방식으로 동작해 요청 결과가(성공, 실패, 거절, 시간초과 등) 결정되기 전에는 connect() 의 실행이 끝나지 않는다고 생각하면 된다. API 호출이 성공하면 이제 send() /recv API 를 통해 데이터를 주고 받을 수 있다.

  • 데이터 송수신 send()/recv()

연결된 소켓을 통해 데이터를 보낼 때, send(), 데이터를 받을때는 recv() API를 사용한다. 둘다 Block 방식으로 동작해 앞서 말한거와 같이 실행이 종료되기 전에는 API가 리턴되지 않는다. 특히, recv() 는 데이터가 수신되거나, 에러가 발생하기전에는 실행이 종료되지 않기때문에 수신작업을 단순하게 처리하기 쉽지 않다.

send() 에 경우, 데이터를 보내는 주체가 자신이여서 컨트롤하기 쉽지만, recv()는 받는 주체기때문에 통신대상, 데이터 종류를 특정할 수 없다.

  • 소켓 닫기 close()

데이터 송수신이 더이상 없으면, 소켓을 닫기위해 close() API 를 호출합니다. close() 에 의해 닫힌 소켓은 더이상 유효한 소켓이 아니기에 해당소켓을 사용할 수 없다.

close() 이후에, 다시 데이터를 주고받고 싶으면 위에 flow가 다시 진행되어야한다.

서버 소켓 프로그래밍


클라이언트에서 소켓을 처리하는 과정은 비교적 간단한 편이다. create, connect, send/recv, close 로 이루어진다. 하지만, 서버 소켓을 처리하는 과정은 이보다 복잡하다. 그 이유는 소켓에 IP와 Port 를 결합하는 bind()listen() API의 존재 때문이라 생각한다.

  • 서버 소켓 생성 socket()

클라이언트와 마찬가지로 소켓을 생성한다.

  • 서버 소켓 바인딩 bind()

bind() API에 사용되는 인자값은 소켓과 포트번호 또는 IP+Port이다. 여기서 알아야할 점은 소켓에 중복성이다. 만약, 프로세스가 TCP,UPD 를 사용한다면 RFC 표준에 따라 각 소켓은 시스템 관리포트(0~65535)중 하나를 사용하게 된다. 여기서, 소켓이 사용하는 포트가 중복된다면 문제가 발생한다. 이런 이유로 OS에서는 소켓들이 중복된 포트를 사용하지 않도록 내부적으로 포트 번호와 소켓 연결 정보를 관리한다.여기서 bind()API는 해당 소켓이 지정된 포트번호를 사용할 것이라는 운영체제에 요청하는 API인 것이다. 만약, 지정된 포트를 다른 소켓이 사용하고 있다면 에러를 리턴한다.

정리하자면, 특정 포트 번호를 서버 소켓이 사용하게 만들기위해 포트 번호를 결합(bind) 해야하는 API가 바로 bind()이다.

  • 요청 대기 listen()

서버 소켓에 포트번호를 결합(bind) 하고 나면, 소켓은 클라이언트 요청준비를 된것이다. 이 다음에 이제 연결 요청이 수신될 때 까지 기다리는것인데, 이때 사용하는 API 가 listenAPI이다.

listen() API는 클라이언ㅌ에서 호출된 connect()API에 의해 연결 요청이 수신되는지 귀 기울이고 있따가 요청이 수신되면, 그 때 대기상태를 종료하고 리턴하는 것이다. listen()API가 대기상태에서 빠져나오는경우는 두가지 이다. 클라이언트에 요청이 수신되는 경우와, 에러가 발생한 소켓이 close()된경우이다. listen()는 성공 리턴 값에 클라이언트에 요청에 대한 정보를 포함하지 않는다. 단지, "성공" 과 "실패" 만 반환한다.

클라이언트에대한 요청은 시스템 내부적으로 큐(Queue)에 쌓이게 되는데, 이 시점에서 클라이언트와 연결은 아직 "not ESTABLISHED state" 상태이다.

대기 중인 연결 요청을 큐에서 꺼내와, 연결하기 위에선 accept() API를 호출해야 한다.

  • 클라이언트 연결 수립 accept()

listen() 후 클라이언트와의 연결 과정을 완료하기위해 이제 소켓을 연결하고 받아들이는 역할을 수행하는 aeecpt()API를 알아보자.

accept() API는 연결요청을 받아드려 소켓 간 연결을 수립하는 것이다. 여기서 중요한점은 최종적으로 연결되는 소켓이 bind()listen() API에서 사용한 서버 소켓이 아니라는 점이다. 최종적으로 소켓과 연결이 만들어지는 소켓은 앞서 사용한 서버소켓이 아니라 accept API로 부터 실행되는 새로운 소켓이다. 서버 소켓의 핵심 역할은 클라이언트의 연결 요청을 수신하는 것이다. 이를 위해 앞서 말한 API들로 포트를 바인딩하고 요청 대기 큐를 생성하여 요청을 대기하고, accept()API에서 데이터 송수신을 위한 소켓을 만들고 서버 소켓에 대기 큐에 쌓여있는 첫번째 연결 요청을 매핑시킨다. 이 역할을 끝으로 서버 소켓은 다른 요청을 처리하기 위해 다시 listen 하거나 서버소켓을 close 것뿐이다.

가장 핵심은, accept()API에서 생성된, 연결(Connetcion)이 수립(Established)된 소켓(Socket)을 통해 처리된다.

이 다음 데이터 송수신 send()/recv() 은 앞서말한 클라이언트와 동일하다.

  • 소켓 연결 종료 close()

클라이언트 소켓 처리과정과 마찬가지로 소켓을 닫기 위해선 close API를 호출해야한다. 서버 소켓에서는 close()대상이 하나만 있는 것이 아니다. 최초에 연결 수립을 위한 서버 소켓에 더해, accept()API 호출에 생성된 소켓도 관리해야한다.

관련된 하고싶은 공부


  • 리눅스 커널 파라미터
  • 리눅스 네트워크 성능 관리

참고자료


https://recipes4dev.tistory.com/153

https://meetup.toast.com/posts/53

profile
@changhyuni

0개의 댓글