ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Socket Programming에 대해
    Network 2023. 3. 24. 21:37
    728x90

    - Socket

    : 사전적으로 구멍, 연결, 콘센트를 의미

    - 전기 공급 인프라 환경에 연결할 수 있게 만들어진 연결부

    - 프로그램이 네트워크에서 데이터를 송수신할 수 있도록 네트워크 환경에 연결할 수 있게 만들어진 연결부

     

    소켓은 정해전 통신 프로토콜에 맞게 만들어져야 하며, 보통 OSI 7계층 중 4계층 Transport 계층 상에서 동작하는 소켓을 사용한다. 이때 소켓을 "TCP/IP 소켓" 또는 "UDP 소켓"이라고 부른다.

     

    소켓으로 네트워크 통신 기능을 구현하기 위해서는 소켓을 만들고, 소켓을 통해 데이터를 주고 받는 절차에 대한 이해가 필요하며, 운영체제 및 프로그래밍 언어에 종속적으로 제공하는 소켓 API 사용법을 숙지해야 한다. 또한 소켓 프로그래밍 중 케이블 분리로 인한 네트워크 단절, 트래픽 증가에 따른 데이터 전송 지연, 시스템 리소스 관리 문제로 인한 에러 등 네트워크 환경에서 발생할 수 있는 다양한 예외사항에 대해서도 처리가 필요하기에 난이도가 쉬운 편은 아니다.

     

    그럼에도 소켓 프로그래밍은 그 역사가 긴 만큼, 대부분의 프로그래밍 언어와 개발 플랫폼에서 소켓 관련 API가 제공되고 있으며, 많은 양의 문서와 예제를 책과 인터넷을 통해 참고할 수 있다.

     

    Client Socket과 Server Socket

    - Connection을 위해 IP 주소와 포트 번호로 식별되는 대상에게, 자신이 데이터 송수신을 위한 네트워크 연결을 수립할 의사가 있음을 알린다.

     

    - 이때 요청을 보내기 위해 사용되는 소켓을 Client Socket, 요청을 받아들이는 소켓을 Server Socket이라고 한다. 두 소켓은 구조가 동일하지만, 소켓의 역할과 구현 절차 구분을 위해 다르게 부르는 것임을 감안하자.

     

    Socket API 실행 흐름

    Socket API 실행 과정

    [Client Socket 처리 과정]

    - Client에서 Socket 생성

    - Server 측에 Connect 요청

    - Server의 연결 수락 -> 데이터를 송수신(Send/Recv)

    - 모든 처리가 끝난 후 Socket Close

     

    [Server Socket 처리 과정]

    - Server Socket 생성

    - Server가 사용할 IP 주소와 포트 번호를 생성한 소켓에 결합

    - Client로부터 연결 요청이 수신되는지 LISTEN

    - 요청 수신 시 Accept, 데이터 통신을 위한 Socket 생성

    - 새로운 Socket을 통해 연결 수립(ESTABLISHED)

    - 데이터 송수신(Send/Recv)

    - 데이터 송수신 완료 시 Socket Close

     Client Socket Programming

    socket()

    - 소켓을 생성하고, 소켓 종류를 지정한다.

    (TCP : Stream 타입 /  UDP : Datagram 타입 지정)

     

    이 시점에는 연결 대상에 대한 어떠한 정보도 들어있지 않는다.

     

    connect()

     

    connect() API는 IP 주소와 Port 번호로 식별되는 대상으로 연결 요청을 보낸다.

    연결 요청에 대한 결과(성공, 거절, 시간 초과 등)가 결정되기 전까지 실행이 끝나지 않는 Block 방식으로 동작

     

    send()/recv()

     

    send()와 recv() API 모두 실행 결과가 결정되기 전까지는 API가 리턴되지 않는다.

    특히, recv()의 경우 데이터가 수신되거나 에러가 발생하기 전까지는 실행이 종료되지 않는다. 왜냐하면 통신 대상이 언제, 어떤 데이터를 보낼 것인지 특정할 수 없기 때문이다. 그래서 별도외 스레드에서 실행하는 것이 낫다.

     

    send()의 경우 데이터를 보내는 주체가 자기 자신이기에, 데이터의 양과 실행 시간을 알 수 있다.

     

    close()

     

    더 이상의 데이터 송수신이 필요하지 않으면 소켓을 닫기 위해 close() API를 호출한다.

    close()에 의해 닫힌 소켓은 더 이상 유효하지 않으므로, 해당 소켓을 사용하여 데이터를 송수신할 수 없다.

    다시 데이터를 주고받고자 하면, socket() API와 connect() API를 재실행하여 소켓이 데이터를 송수신할 수 있는 상태가 되어야 한다.

     

    Server Socket Programming

     

    socket()

     

    클라이언트 소켓과 마찬가지로, 서버 소켓을 사용하기 위해 최초로 소켓을 생성한다.

     

    bind()

     

    Server Socket과 Port 번호를 결합한다.

    OS에서 소켓들이 중복된 포트 번호를 사용하지 않도록, 내부적으로 포트 번호와 소켓 연결 정보를 관리한다.

    bind() API는 해당 소켓이 지정된 포트 번호를 사용할 것이라는 것을 운영체제에 요청하는 API이다.

    bind()의 Error 는 지정된 포트 번호를 다른 소켓이 사용하고 있음을 시사한다.

     

    Server Socket은 일반적으로 고정된 포트 번호를 사용하고, 그 포트 번호로 클라이언트의 연결 요청을 받아들인다.이를 위해 OS가 특정 포트 번호를 서버 소켓이 사용하도록 만들기 위해 소켓과 포트 번호를 결합하는 bind() API를 사용한다.

     

    listen()

     

    bind() 이후, Server Socket은 클라이언트의 연결 요청을 받아들일 준비가 되어있다.

    이제 클라이언트에 의한 연결 요청이 수신될 때까지 기다리는 API가 listen() API이다.

    - 클라이언트에서 호출된 connect() API에 의해 연결 요청이 수신되는 지 확인할 때까지 대기

    - Client 요청이 수신되는 경우 / Socket Close() 포함 에러 발생 시 listen() API가 대기 상태에서 빠져나온다.

     : 수신 시 SUCCESS ,  에러 발생 시 FAIL

     

    클라이언트 연결 요청에 대한 정보는 시스템 내부적으로 관리되는 Queue에서 쌓이고, 이 떄 클라이언트와의 연결ㅇㄴ 아직 완전히 연결되지 않은 not ESTABLISHED state, 대기 상태 이다.

     

     

    accept()

     

    listen() API에서 수신한 클라이언트의 연결 요청을 받아들여 소켓 간 연결을 수립

     

    이 때 주의해야 할 점은, 데이터 통신을 위해 연결되는 소켓이 앞서 bind() 또는 listen() API에서 사용한 Server Socket이 아니라는 점이다. 클라이언트 소켓과 연결이 만들어지는 소켓은 앞서 사용한 서버 소켓이 아닌, accept() API 내부에서 새로 만들어지는 소켓이다.

     

    *Server Socket의 핵심적인 역할  : 클라이언트의 연결 요청을 수신

    (그 외에는 연결 요청을 처리하기 위해 다시 대기(listen)하거나, 서버 소켓을 닫는(close) 것 뿐이다)

     

    따라서 accept() API에서 데이터 송수신을 위해 새로운 소켓을 만들고 서버 소켓의 대기 큐에 쌓여있는 첫번째 연결 요청을 매핑시킨다. 즉 실질적인 데이터 송수신은 accept() API에서 생성된 연결이 수립된 소켓을 통해 처리된다.

     

    send()/recv()

     

    close()

    서버 소켓에서는 close()의 대상이 하나만 있는 것이 아니라는 것에 주의해야한다. 최초 socket() API를 통해 생성한 서버 소켓에 더해 accept() API 호출에 의해 생성된 소켓도 관리해야 한다.

     

     

    Python으로 간단하게 구현하기

    # Server.py
    import socket
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((socket.gethostname(), 10061)) # IP 번호 및 포트 번호 확인
    s.listen(5) # 서버 대기 시간 설정
    
    while True:
        clientSocket, address = s.accept()
        print(f"Connection established from address {address}")
        clientSocket.send(bytes("Welcome to the server", "utf-8"))
        clientSocket.close()
    # Client.py
    import socket
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((socket.gethostname(), 10061))
    
    message = s.recv(2048)
    
    print(f"Message received: {message}")

     

    각각 server.py와 client.py 파일을 만들고, server.py부터 실행하고 다른 터미널로 연 다음 client.py를 실행하면 각각 다음의 화면이 나타난다.

     

    client.py

     

    server.py

    참고

    https://recipes4dev.tistory.com/153 소켓 프로그래밍

    728x90

    'Network' 카테고리의 다른 글

    [Cloud] UDR : User Defined Route  (1) 2024.04.19
    Apache JMeter(Thread Group, Sampler, Listener)  (2) 2023.12.29
    Azure VMSS  (0) 2023.12.29
    [Internship] 개발자 도구 - Network Tab  (1) 2023.12.22
    LS Routing, DV Routing  (0) 2023.03.23
Designed by Tistory.