Network

[컴퓨터 네트워킹 하향식 접근] 2. 애플리케이션 계층

noahkim_ 2025. 3. 27. 17:55

이석복 님의 KOCW 강의 "컴퓨터 네트워크"를 정리한 글입니다.

* James F. Kurose님과 Keith W. Ross 님의
"컴퓨터 네트워킹 하향식 접근" 책을 교제로 한 강의입니다. (책의 내용은 포함되지 않았습니다)

 

1. 네트워크 애플리케이션의 원리

Client-Server Architecture

구분 서버 (Server) 클라이언트 (Client)
상태 항상 대기 상태 (대기 서버) 요청 시에만 활성화됨
IP 주소 영구적인 고정 IP 주소를 가짐 동적 IP 주소를 가질 수 있음
데이터 센터 보통 데이터 센터나 서버실에 존재 개인 컴퓨터, 스마트폰 등 다양한 장치에서 존재
역할 서비스를 제공하고 요청을 처리 서버에 요청을 보내고 서버의 응답을 처리
예시 웹 서버, 데이터베이스 서버, 파일 서버 등 웹 브라우저, 이메일 클라이언트, 모바일 앱 등

 

Process Communicating

  • 프로세스 간 통신을 의미

 

socket
  • 네트워크에서 프로세스 간 통신을 위한 엔드포인트 (통신 인터페이스)
  • 소켓을 통해 메시지를 주고받음
  • identifier: ip + port

 

2. 웹과 HTTP

HTTP

항목 설명
프로토콜 HTTP (HyperText Transfer Protocol)
모델 Client-Server 모델
계층 Application Layer (응용 계층)
기반 TCP 기반
상태
Stateless (과거 클라이언트 요청에 대한 정보를 유지하지 않음)
TCP 연결 재사용
1. Non-persistent: 요청이 끝날 때마다 TCP 연결을 끊음.
2. Persistent: 한 문서에 여러 리소스 요청이 있을 때 TCP 연결을 재사용

 

rtt
  • 요청-응답 간의 소요 시간

 

response time 
  • 요청-응답까지 걸리는 총 시간
  • Non-persistent
    • rtt * 2 + file transmission time
    • 연결을 끊고 새로운 연결을 설정하므로
  • persistent
    • rtt + file transmission time

 

3. 인터넷 전자메일 

4. DNS: 인터넷의 디렉터리 서비스

5. P2P 파일 분배

6. 비디오 스트리밍과 콘텐츠 분배 네트워크

7. 소켓 프로그래밍: 네트워크 애플리케이션 생성

  • 전송 계층 프로토콜 별 소켓 타입이 달라짐

 

SOCK_STREAM (TCP)

  • 연결 지향적인 방식으로 데이터 전송의 신뢰성을 보장합니다
  • 순서대로 전송되며 손실된 데이터는 재전송됩니다.

 

절차
단계 서버 클라이언트 설명
1. 소켓 열기 socket() socket()
서버와 클라이언트가 각각 소켓을 생성합니다.
2. IP 주소 바인딩 bind() -
서버는 자신이 사용할 IP 주소와 포트를 바인딩합니다.
3. 서버 모드로 전환 listen() -
서버는 클라이언트의 연결을 기다리는 상태로 대기합니다.
4. 연결 수락 accept() -
클라이언트로부터 연결이 오면 서버는 연결을 수락하고, 새로운 소켓을 반환합니다.
(블로킹 상태)
5. 클라이언트 연결 - connect()
클라이언트는 서버에 연결을 시도합니다.
이때 3-way handshake가 발생합니다.
6. 요청 전송 - write()
클라이언트는 서버로 데이터를 전송합니다.
7. 서버 응답 read() -
서버는 클라이언트의 요청에 응답합니다.
8. 응답 받기 - read()
클라이언트는 서버의 응답을 받습니다.

 

예시
더보기

서버 코드

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 12345
#define MAX_BUFFER_SIZE 1024

int main() {
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len;
    char buffer[MAX_BUFFER_SIZE];

    // 서버 소켓 생성
    if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("소켓 생성 실패");
        exit(1);
    }

    // 서버 주소 설정
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY; // 모든 IP에서 접속 가능
    server_addr.sin_port = htons(PORT);

    // 소켓에 주소 바인딩
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("바인딩 실패");
        exit(1);
    }

    // 연결 대기 상태로 설정
    if (listen(server_socket, 5) < 0) {
        perror("리스닝 실패");
        exit(1);
    }
    printf("서버 대기 중...\n");

    // 클라이언트 연결 수락
    client_len = sizeof(client_addr);
    if ((client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len)) < 0) {
        perror("클라이언트 연결 수락 실패");
        exit(1);
    }
    printf("클라이언트 연결됨: %s\n", inet_ntoa(client_addr.sin_addr));

    // 클라이언트로부터 데이터 받기
    ssize_t n = recv(client_socket, buffer, MAX_BUFFER_SIZE, 0);
    if (n < 0) {
        perror("데이터 수신 실패");
        exit(1);
    }
    buffer[n] = '\0'; // null-terminate
    printf("클라이언트로부터 받은 데이터: %s\n", buffer);

    // 클라이언트에게 응답 보내기
    send(client_socket, "서버가 응답을 보냅니다.", 24, 0);

    // 소켓 닫기
    close(client_socket);
    close(server_socket);

    return 0;
}
더보기

클라이언트 코드

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 12345
#define MAX_BUFFER_SIZE 1024

int main() {
    int client_socket;
    struct sockaddr_in server_addr;
    char buffer[MAX_BUFFER_SIZE];

    // 클라이언트 소켓 생성
    if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("소켓 생성 실패");
        exit(1);
    }

    // 서버 주소 설정
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 서버 IP (로컬호스트)

    // 서버에 연결
    if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("서버 연결 실패");
        exit(1);
    }
    printf("서버에 연결됨\n");

    // 서버로 데이터 전송
    const char *message = "안녕하세요, 서버!";
    send(client_socket, message, strlen(message), 0);
    printf("서버로 데이터 전송: %s\n", message);

    // 서버로부터 응답 받기
    ssize_t n = recv(client_socket, buffer, MAX_BUFFER_SIZE, 0);
    if (n < 0) {
        perror("서버로부터 응답 수신 실패");
        exit(1);
    }
    buffer[n] = '\0'; // null-terminate
    printf("서버로부터 받은 응답: %s\n", buffer);

    // 소켓 닫기
    close(client_socket);

    return 0;
}

 


출처