Skip to content

Commit

Permalink
Merge branch 'ming/main' of https://github.com/SSUMC-6th/Spring_Boot_C
Browse files Browse the repository at this point in the history
…into ming/#9

chore: ming/main 브랜치의 최신 상태 반영
  • Loading branch information
qzzloz committed Apr 29, 2024
2 parents f6290a1 + 9770ecd commit 2bee11a
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 0 deletions.
135 changes: 135 additions & 0 deletions docs/chapter1/Ch01Keyword.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Ch01. Server (Socket & Multi-Process)
1주차 워크북 - 서버란 무엇인가(소켓&멀티 프로세스)

<br>

# TCP
[TCP 공식문서](https://www.rfc-editor.org/rfc/rfc793)

TCP란 서버와 클라이언트 간 데이터를 송/수신을 위한 프로토콜이다.

TCP의 특징
1. reliable data
2. connection-oriented(3-way handshake)
3. in-order data
4. congestion control
5. flow control
6. 양방향 전송
7. 1:1통신 (unicast) 등

TCP 소켓은 source의 IP, port와 destination의 IP, port로 구분한다.
<br>
<br>

# UDP

[UDP 공식문서](https://www.rfc-editor.org/rfc/rfc768.txt)

속성
1. unreliable data
2. out-of-order → 순서가 뒤바뀐채 전달될 가능성O
3. 비연결성
4. checksum
5. 단방향 통신
6. 속도 빠름

UDP 소켓은 source의 IP, destination의 IP로 구분한다.
<br>
<br>

# 시스템 콜?
응용프로그램이 요청할 때 커널에 접근할 수 있는 인터페이스이다. 파일을 읽어오거나 파일을 쓰거나 화면에 메시지를 출력하는 등의 서비스를 수행하기 위해 OS는 하드웨어를 직접 관리한다. 반면 응용프로그램은 OS가 제공하는 인터페이스를 통해서만 자원을 사용할 수 있는데, OS가 제공하는 인터페이스를 시스템콜 이라고 한다.

운영체제가 하드웨어, CPU를 사용하는 모드를 커널 모드라고 한다. 애플리케이션 코드를 실행하는 모드를 사용자 모드라고 한다. 사용자 모드에서 하드웨어 자원에 접근하기 위해서는(=커널 영역의 기능을 사용하기 위해서는) 시스템 콜을 사용해야 한다.

보통 API를 통해 사용한다.

[Ref](https://didu-story.tistory.com/311)
<br>
<br>

# 하드웨어 인터럽트
CPU 외의 장치에서 예외상황이 발생하여 처리가 필요한 경우에 컨트롤러가 인터럽트 신호를 발생시켜 CPU에게 알리는 것을 말한다.

하드웨어 장치가 CPU에게 어떤 사실을 알려주거나 CPU의 서비스를 요청해야 하는 경우, "인터럽트 발생~" 하면서 인터럽트 라인을 세팅한다. 다른 명령을 수행하고 있던 CPU는 인터럽트 라인을 확인하면 하던 일을 멈추고 인터럽트를 처리하러 간다.

- **인터럽트 벡터**

인터럽트가 발생하면 CPU는 운영체제에게 제어권을 주고 인터럽트 처리를 위해 정의된 코드(=루트)를 찾는다. 운영체제는 인터럽트가 발생하면 정의된 코드를 찾을 수 있도록 처리해야 할 루틴의 주소를 보관하고 있는 테이블을 갖고 있다. 이 테이블을 인터럽트 벡터라고 한다.

- **인터럽트 핸들러**

운영체제 커널은 인터럽트가 들어왔을 때 처리해야 할 코드를 포함하고 있다.(이미 프로그래밍 되어 있다!~) 이 코드를 인터럽트 처리루틴(ISR), 인터럽트 핸들러라고 한다.

[Ref](https://baebalja.tistory.com/354)

<br>
<br>

# 리눅스의 파일과 파일 디스크립터
- 리눅스의 파일

리눅스는 모든 것을 파일로 취급한다. 디렉토리, 소켓 등등 모든 것을 파일로 관리한다.

- 파일 디스크립터

리눅스에서 파일을 다룰 때 사용하는 개념이다. 프로세스에서 특정 파일에 접근할 때 사용하는 추상적인 값이다. stdin(표준입력, 0), stdout(표준출력, 1), stderr(표준에러, 2)는 이미 파일 디스크립터 값이 할당되어 있기 때문에 3 이상부터 사용할 수 있다.

리눅스에서 프로세스가 소켓에 접근한다면, 소켓은 파일이므로 파일 디스크립터를 이용할 것이다.
<br>
<br>

# socket() 시스템 콜
새로운 소켓을 생성하고 파일 디스크립터를 반환한다.

socket(domain, type, protocol)
- domain: IPv4는 AF_INET, IPv6는 AF_INET6
- type: TCP 소켓인 경우 SOCK_STREAM, UDP 소켓인 경우 SOCK_DGRAM
- protocol: 0은 시스템이 선택, 6은 TCP, 17은 UDP
<br>
<br>

# bind() 시스템 콜
생성한 소켓에 특정 IP주소와 포트 번호를 할당한다.

bind(sockfd, sockaddr, socklen_t)
- sockfd: 바인딩 할 소켓의 파일 디스크립터
- sockaddr: 소켓에 바인딩 할 주소
- socklen_t: sockaddr의 크기
<br>
<br>

# listen() 시스템 콜
클라이언트의 connect 요청을 대기하는 상태가 된다. connection-oriented인 TCP에서만 사용한다.

listen(sockfd, backlog)
- sockfd: listen을 시작할 소켓의 파일 디스크립터
- backlog: connection 요청을 대기하는 큐의 크기, 만약 큐가 꽉 차면 운영체제는 큐가 빌 때까지 새로운 connection 요청을 받지 않는다.
<br>
<br>

# accept() 시스템 콜
listening을 하다가 새로운 connection이 열리면 accept()는 새로운 소켓을 만들어서 리턴한다.

accept(sockfd, sockaddr, socklen_t)
- sockfd: connection을 받을, listening 하고 있던 소켓의 파일 디스크립터
- sockaddr: 클라이언트의 주소 정보
- socklen_t: 위 주소 정보의 크기
<br>
<br>

# 멀티 프로세스
여러 개의 CPU가 하나 이상의 작업을 동시에 처리(병렬처리)한다.
(*프로세서=CPU코어)

자식프로세스 생성 - fork()

pid_t fork()
리턴값이 0이면 자식 프로세스, 0이 아니면 부모 프로세스를 의미한다.

멀티 프로세스는 각 프로세스가 독립적이기 때문에 한 프로세스가 죽어도 다른 프로세스에는 영향을 주지 않아 안정성이 높다. 하지만 그만큼 많은 메모리 공간과 CPU 시간을 차지한다.
<br>
<br>

# 병렬 처리
여러 CPU에서 각 프로세스를 병렬적으로 실행한다.
79 changes: 79 additions & 0 deletions docs/chapter2/Ch02Keyword.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Ch02. AWS (VPC & Internet Gateway & EC2)
2주차 워크북 - AWS (VPC & Internet Gateway & EC2)

<br>

# AWS 리전
AWS는 수많은 컴퓨팅 서비스를 지원하고 있고, 이를 위한 대규모의 서버용 컴퓨터가 필요할 것이다. 이 때 서버용 컴퓨터를 모두 한 곳에 몰아둔다면

1. 서버가 있는 지역에 자연재해가 발생할 경우, 모든 서비스가 마비되고
2. 자원이 한 곳에 몰려있는 경우, 그곳과 지구 반대편에 위치한 지역은 거리가 멀기 때문에 서비스 속도가 느릴 것이다.

위와 같은 문제를 해결하기 위해 서비스를 여러 곳에 분산하여 배치하였고, 이를 리전이라고 한다.
- 가용영역

리전을 한 번 더 분산시켜 배치한 것이다. 1개 이상의 물리 데이터센터를 묶은 그룹이라고 볼 수 있다.

- 리전과 가용영역의 차이

가용영역은 데이터센터 그룹이고 리전은 가용영역 여러 개로 구성된 **지리적 영역**이다.
<br>
<br>


# 라우팅
- source -> dest까지 패킷의 이동경로를 설정하는 것

<br>
<br>

# VPC
VPC(Virtual Private Cloud): AWS에서 제공하는 가상 네트워킹 환경, 사설 IP주소를 가진다.

<br>
<br>

# VPC 실제 사용
VPC는 실제 사용 될 때, VPC 자체에서도 서브넷을 나눠서 원하는 가용영역에 배치하여 사용한다. 이 때,

1. 첫 주소 : 서브넷의 네트워크 대역
2. 두번째 : VPC 라우터에 할당
3. 세번째 : Amazon이 제공하는 DNS에 할당
4. 미래를 위해 예약
5. 브로드 캐스트 주소

로 사용되기 때문에 이들은 호스트 주소로 할당할 수 없다. (예약되어 있다.)

1. 첫 번째 주소

만약 서브넷의 CIDR 블록이 10.0.0.0/24라면, 여기서 10.0.0.0은 네트워크 주소이다.

2. 두 번째

VPC 라우터에 할당되는 주소이다. 만약 CIDR 블록이 10.0.0.0/16이고 서브넷의 CIDR 블록이 10.0.1.0/24라면 두 번째 주소는 10.0.1.1이 된다.

3. 세 번째

이 주소는 인스턴스가 도메인 이름을 해석할 때 사용된다.

<br>
<br>

# 포트포워딩
포트포워딩은 패킷이 라우터나 게이트웨이를 통과하는 동안 이 패킷이 어느 포트로 가야할 지 결정해주는 것을 말한다.

예를 들어,

공유기를 설치하고 공유기와 연결된 여러 PC가 있다고 하자. PC들은 공유기로부터 IP를 부여받는다. 외부에서 들어오는 패킷은 많은 PC 중 어느 PC로 가야하는지 길을 찾지 못할 것이다. (이 때는 공유기의 포트 번호르 들고 오기 때문에)

그래서 패킷이 올바른 PC를 찾아갈 수 있도록 공유기는 NAT table을 참고하여 해당 PC의 포트번호를 패킷에게 알려준다.

<br>
<br>

# NAT 프로토콜
게이트웨이에서 외부 네트워크로 나갈 때는 네트워크 내부의 사설IP가 아닌 공인IP주소로 바뀐다.
<br>
<br>

# Private Subnet
36 changes: 36 additions & 0 deletions src/practice/chapter1/client.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

int main(){
int client_socket;
struct sockaddr_in clientAddr;
int clientAddr_len, readLen, recvByte, maxBuff;
char message[BUFSIZ];
char rBuff[BUFSIZ];

client_socket = socket(AF_INET, SOCK_STREAM, 0);

printf("===client===\n");

clientAddr.sin_family = AF_INET; // IPv4 주소 체계
clientAddr.sin_addr.s_addr = INADDR_ANY; // 모든 가능한 IP 주소
clientAddr.sin_port = htons(9002);

if(connect(client_socket, (struct sockaddr *)&clientAddr, sizeof(clientAddr)) == -1){
perror("연결 오류\n");
return 1;
}

while(1){
printf("메시지 입력: ");
fgets(message,BUFSIZ-1,stdin);

if(strncmp(message, "END", 3) == 0) break;

send(client_socket, message, BUFSIZ-1, 0);
}
close(client_socket);

return 0;
}
59 changes: 59 additions & 0 deletions src/practice/chapter1/server.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

int main(){
int server_socket, client_socket;
int readLen;
char rBuff[BUFSIZ];

// 소켓 생성
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if(server_socket == -1){
perror("소켓 생성 실패\n");
return 1;
}

//
struct sockaddr_in serverAddr, clientAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET; // IPv4 주소 체계
serverAddr.sin_addr.s_addr = INADDR_ANY; // 모든 가능한 IP 주소
serverAddr.sin_port = htons(9002);

// 서버 소켓 바인딩 설정
if (bind(server_socket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
perror("바인딩 실패");
return 1;
}

// listen: 클라이언트의 connect 요청을 기다림
int backlog = 5; // 최대 클라이언트 수
if(listen(server_socket, backlog) == -1){
perror("대기 실패");
return 1;
}

printf("서버가 클라이언트의 connect 요청을 기다리는 중...\n");

socklen_t clientAddr_len = sizeof(client_socket);
client_socket = accept(server_socket, (struct sockaddr *)&clientAddr, &clientAddr_len);

if(client_socket == -1){
perror("연결 실패\n");
return 1;
}
printf("클라이언트 연결 성공\n");

while(1){
readLen = read(client_socket, rBuff, sizeof(rBuff)-1);
rBuff[readLen] = '\0';
printf("Client: %s", rBuff);
write(client_socket, rBuff, strlen(rBuff));
}
close(client_socket);
close(server_socket);

return 0;
}

0 comments on commit 2bee11a

Please sign in to comment.