번외A: TCP/IP 소켓 프로그래밍 (1:1)

Hyeonmin Han·2022년 5월 5일
0

Computer Network

목록 보기
8/8

프로그램들은 Ubuntu 20.04 LTS 버전에서 POSIX API에서 제공하는 함수들을 기반으로 C 코드로 작성하였다.

TCP/IP 소켓 프로그래밍의 흐름

  1. 소켓 생성 단계 (socket(), bind())

    • 서버는 클라이언트가 연결할 수 있도록 서버 소켓을 만들어 주어야 한다. 서버 소켓을 만들기 위해서는 서버가 열릴 IP 주소 (자기 자신의 IP 주소)와 포트 번호, IP 주소 종류 (IPv4 혹은 IPv6. IPv4를 보편적으로 사용한다.)가 필요하다. 클라이언트 소켓에서는 연결하려는 서버의 IP 주소, 포트 번호, IP 주소 종류가 필요하다.

    • socket() 함수는 연결 및 데이터 송수신을 위한 매개체인 소켓을 생성하는 함수이다. 클라이언트는 이 함수만 호출하고 다음 단계로 넘어가지만, 서버는 이에 추가적으로 bind() 함수를 호출해 주어야 한다.

    • bind() 함수는 서버 측 소켓에 서버에 대한 정보 3가지 (IP 주소, 포트 번호, IP 주소 종류)를 주입하는 함수다. bind() 함수를 호출함으로써 서버를 열 수 있고, 클라이언트는 서버 소켓에 주입된 정보를 통해 연결할 수 있다.

  2. 연결 시도 단계 (listen(), connect())

    • 서버 측: listen() 함수는 서버 측에서 호출하는 함수로, bind() 함수로 소켓에 정보를 주입하면 소켓이 클라이언트와의 연결 대기하는 상태가 된다. 클라이언트가 서버에 연결하면 서버 측에서는 connect() 함수가 호출된다. connect() 함수가 호출되면 서버 내에서는 연결된 클라이언트와 데이터를 주고받기 위한 새로운 소켓이 생성된다. 이를 connect 소켓이라고 한다. listen() 단계까지 쓰였던 서버 측 소켓은 listen 소켓이라는 별개의 소켓이며, listen 소켓의 역할은 단순히 클라이언트와의 연결을 대기하기 위한 소켓인 것이다. 연결 이후 데이터 송수신을 담당하는 소켓은 connect() 함수에서 생성된 connect 소켓이다.

    • 클라이언트 측: 서버 측과 달리, 클라이언트 측은 connect 소켓 하나만 존재한다. 클라이언트에서는 socket() 함수를 호출하여, connect 소켓을 생성한다. 이후 connect() 함수를 서버 측 정보 (IP 주소, 포트 번호, IP 주소 종류)를 인자로 하여 호출함으로써 서버와의 연결을 시도한다. 서버 측에서 listen 소켓을 통해 클라이언트가 연결 시도 중임을 확인하면, connect() 함수를 통해 연결을 승인하고, 연결된 클라이언트와의 데이터 송수신을 담당하는 connect 소켓을 생성한다.

  3. 데이터 송수신 단계 (read() (=receive()), write (=send()))

    read 함수와 write 함수는 항상 짝지어져 있어야 한다.
    즉, 어느 한 쪽이 read 또는 write 함수라면, 반드시 반대 쪽은 write 또는 read 함수여야 한다는 것이다.

    • read() 함수와 write() 함수는 서버 및 클라이언트 각각의 buffer 변수에 저장된 데이터를 통해 보내거나 받는다. buffer는 주로 동적 할당을 통한 char * 자료형이 쓰인다.

    • read() 함수와 write() 함수는 짝지어져 있다. 즉, read() 함수 또는 write() 함수 중 어느 한 쪽이 먼저 호출되면, 다른 한 쪽에서 이와 짝지어진 함수를 호출할 때까지 계속해서 대기 상태 (blocking)에 놓이게 된다는 것이다. 대기 상태에 놓인 프로그램은 어떤 작업도 하지 않는 유휴 상태가 된다. 따라서, TCP/IP 소켓 프로그래밍을 할때에 가장 조심해야 할 부분이 이러한 부분이다. read() 함수와 write() 함수는 같이 따라가야지 프로그램이 제대로 동작하기 때문이다.

  4. 연결 종료 단계 (close())

    • 서버, 클라이언트가 작업을 종료하여 프로그램을 종료하고자 할 때, 양쪽의 소켓을 닫아 주어야 한다. 서버에서는 listen 소켓과 connect 소켓을 닫아 주고, 클라이언트에서는 connect 소켓을 닫아주면 된다. 각 소켓을 닫을 때에는 close() 함수를 사용하면 된다.

1. 단순 Echo Program

1번 소스 코드 보러가기

  • 프로그램 소개

    • 클라이언트에서 입력받은 값을 서버에서 그대로 클라이언트에 돌려주는 단순한 1:1 Echo Program이다.
  • 실행 방법

    • 터미널 창 2개를 켜고 두 터미널 모두 실행 가능한 바이너리 파일이 있는 디렉토리로 이동한다.
    • 창 하나는 ./01_server를 입력하여 서버를 실행하고, 다른 창 하나는 ./01_client를 입력하여 클라이언트를 실행한다.
    • client 측에서 문자열을 입력한다. 이 프로그램은 exit를 입력하여 종료할 때까지 서버에서 문자열을 받고, 다시 클라이언트로 이를 보내는 동작이 수행된다.
  • 실행 결과

2. 알파벳 대소문자 변환 Echo Program

2번 소스 코드 보러가기

  • 프로그램 소개

    • 클라이언트에서 입력받은 값을, 서버에서 알파벳 부분만을 대소문자 변환하여 클라이언트에 돌려주는 1:1 Echo Program이다.
  • 실행 방법

    • 터미널 창 2개를 켜고 두 터미널 모두 실행 가능한 바이너리 파일이 있는 디렉토리로 이동한다.
    • 창 하나는 ./02_server를 입력하여 서버를 실행하고, 다른 창 하나는 ./02_client를 입력하여 클라이언트를 실행한다.
    • client 측에서 문자열을 입력한다. 이 프로그램은 exit를 입력하여 종료할 때까지 서버에서 문자열을 받고, 문자열 중 알파벳이 있다면 그 부분만을 대소문자 변환한다. 변환한 결과 문자열을 다시 클라이언트로 보낸다.
  • 실행 결과

3. 아이디/비밀번호 등록 프로그램

3번 소스 코드 보러가기

  • 프로그램 소개

    • 처음 실행할 때, 사전에 데이터베이스에 ID, 비밀번호 몇 쌍이 저장된다.
    • 클라이언트가 서버에 연결하면, 먼저 클라이언트가 ID를 입력한다.
    • 서버는 클라이언트로부터 ID를 입력받으면, 이를 서버 내의 데이터베이스에 존재하는 ID들과 비교하여, 저장되어 있는 ID인지 아닌지를 판별한다.
    • ID에 저장되어 있지 않다면, 계정 등록을 시도한다. 서버 측에서 계정 등록을 위해 클라이언트에 새 비밀번호 작성을 요구한다. 클라이언트가 비밀번호를 보내면 ID와 비밀번호를 데이터베이스에 추가한다.
    • ID에 저장되어 있다면, 로그인을 시도한다. 서버 측에서 클라이언트에 비밀번호 입력을 요구한다. 클라이언트가 서버에 비밀번호를 보내면 데이터베이스에 있는 ID에 귀속된 비밀번호와 대조하여, 일치하면 로그인 성공 메시지를 클라이언트에 보내고, 일치하지 않으면 로그인 실패 메시지를 클라이언트에 보낸다.
  • 실행 방법

    • 터미널 창 2개를 켜고 두 터미널 모두 실행 가능한 바이너리 파일이 있는 디렉토리로 이동한다.
    • 창 하나는 ./03_server를 입력하여 서버를 실행하고, 다른 창 하나는 ./03_client를 입력하여 클라이언트를 실행한다.
    • client 측에서 로그인할 ID를 입력한다. 이때 exit를 입력하면 프로그램이 종료된다. 로그인할 ID를 입력하면, 입력한 ID에 따라서 계정 등록을 할지 로그인을 할지 결정된다. 둘 중 어떤 것이든, 클라이언트는 비밀번호를 입력해야 한다. 비밀번호 입력 시 계정 등록 여부, 로그인 여부에 따른 결과 메시지가 클라이언트에 출력된다.
  • 실행 결과

    클라이언트

    서버

profile
네트워크, 클라우드에 관심이 있는 시스템 엔지니어 지망생

0개의 댓글