728x90

멀티 프로세스 -> 멀티 스레드

select -> epoll(리눅스), iocp(window)

 

멀티 프로세스 서버의 단점

- 프로세스의 빈번한 생성은 성능은 저하

- 멀티 프로세스의 흐름을 고려해서 구현해야 하기 때문에 구현이 쉽지 않다.

- 프로세스간 통신이 필요한 상황에서는 서버의 구현이 더 복잡해진다.

 

멀티 프로세스 서버의 대안

- 하나의 프로세스가 다수의 클라이언트에게 서비스 할 수 있도록 한다

- 이를 위해서는 하나의 프로세스가 여러 개의 소켓을 핸들링 할 수 있는 방법이 존재 해야 한다.

- 바로 이것이 IO 멀티플렉싱이다.

 

 

멀티 플렉싱의 의해

- 기존 멀티 프로세스에서는 클라이언트 갯수 만큼 프로세스를 1대1로 대응 시켰지만, 멀티 플렉싱은 하나의 프로세스에서 다수의 클라이언트 접속을 처리 한다(하나의 리소스를 통해서)

 

select 모델 flow

-> 멀티플렉싱

-> 다수 클라이언트 fd를 하나의 프로세스가 관리 해야 한다

-> 게임을 예로 들면 서버-클라이언트 사이에서 처리할 데이터의 크기가 굉장히 작기 때문에 하나의 프로세스에서도 충분히 처리가 가능하다 때문에 하나의 프로세스가 모든 클라이언트 요청을 처리하는 것이 '프로세스를 여러개 만드는 것보다' 효율적일 수 있다.

-> 다수의 클라이언트 소켓 fd를 저장할 fd_set 자료구조가 존재

-> fd_set에 클라이언트 소켓 fd를 등록

-> select() 함수를 통해서 fd_set에 변경된 데이터가 있으면  값을 반환한다(그전 까진 블로킹 상태)

-> 변경된 fd 정보 하나에는 나머지는 다 리셋된다.

-> 해당되는 fd를 찾아서)(fd_set을 반복문을 통해서 찾아야 한다.) 이용 read/write

-> 다시 fd_set에 fd를 등록하고 다시 select() 함수 호출 후 대기(블로킹)

 

fd_set 관련 함수

FD_ZERO

FD_SET

FD_CLR

FD_ISSET

 

 

 

728x90
728x90

프로세스 별로 별도의 메모리 공간을 가지기 때문에 기본적으로 프로세스 간의 데이터 공유는 불가능 하다.

 

solution

OS가 별도의 메모리 공간(프로세스들이 접근 가능한)을 만들어 준다.

 

step

1. 부모프로세스가 os에게 자식프로세스와 통신할 것이라고 한다

2. os는 부모프로세스와 자식프로세스가 공유 가능한 메모리 공간(파이프)를 생성하고 파이프에 읽고 쓰기가 가능한 파일스크립터를 부모프로세스에게 제공한다.

3. 자식프로세스가 생성된다.

4. 자식프로세스는 부모프로세스의 모든 걸 복사하기 때문에 자식프로세스 역시 읽고 쓰기가 가능한 파일스크립터를 얻는다.

5.이로써 읽고 쓰기가 가능한 파일스크립터를 이용한 파이프 사용을 통해 부모-자식간에 통신이 가능하다.

 

 

pipe()

파이프 생성

송수신 가능한 디스크립터 반환

 

주의점

파이프가 하나 일 때 부모 프로세스는 본인이 파이프에 wirte하고 본인이 직접 read가 가능하기 때문에 문제가 된다.

이를 해결하기 위해 애초에 부모->자식, 자식->부모용 파이프를 두개 생성하여 사용하는 것이 일반적이다.

728x90
728x90

둘 이상의 프로세스는 운영체제의 의해 실행 된다.

이때, CPU 스케줄링 -> context switching 발생 -> 이전 데이터 스왑영역에 이동(하드디스크에 보관 됨)

 

이런 작업의 부담으로 경량화 된 프로세스 등장 (스레드)

 

다중 접속 서버의 구현 방법들

1) 멀티 프로세스 기반 서버 : 다수의 프로세스를 생성하는 방식

2) 멀티 플렉싱 기반 서버 : 입출력 대상을 묶어서 관리하는 방식으로 서비스 제공

3) 멀티 쓰레딩 기반 서버 : 클라이언트 수만큼 쓰레드를 생성하는 방식으로 서비스 제공

 

프로세스란

- 간단하게는 실행 중인 프로그램을 뜻한다.

- 실행중인 프로그램에 관련된 메모리, 리소스 등을 총칭하는 의미이다.

- 멀티프로세스 운영체제는 둘 이상의 프로세스를 동시에 생성 가능하다.

 

프로세스 ID

운영체제는 생성되는 모든 프로세스에 id를 할당한다.

 

fork()

함수가 호출되면 호출한 프로세스가 복사되어 fork함수 호출 이후를 각각의 프로세스가 독립적으로 실행하게 된다.

부모 프로세스 에서는 자식 프로세스의 ID(0이 아닌 값). 자식  프로세스에서는 0값을 반환.

 

좀비 프로세스

프로세스 간에는 부모-자식 계층 관계를 갖는다. 

자식 프로세스가 종료되면 실제로 메모리 상에서 사라지지 않고 OS에게 값을 반환하게 된다. 부모 프로세스가 자식 프로세스가 반환한 값을 확인 할 때 비로서 자식 프로세서는 사라진다. 그 값이 int main 함수의 return 값이다. 

 

자식 프로세스가 종료되었지만 아직 소멸되지 않은 상태(부모 프로세스가 자식 프로세스가 종료하면서 반환한 값을 확인하지 않은 상태)

 

자식 프로세스가 좀비 상태에서 부모 프로세스가 소멸 되면 자식 프로세스도 자동으로 같이 소멸 된다.

 

wait()

자식 프로세스의 반환값을 확인한다. 

자식 프로세스가 종료되지 않았으면 블로킹 상태에 놓인다.

 

waitpid()

특정 프로세스 id를 지정하여 반환값을 얻는다.

블로킹 상태가 되지 않는다.

 

시그널 핸들링

좀비 프로세스가 생성되는 것을 os가 부모 프로세스에게 알려 줄 수 있다.

부모 프로세스 -> os (요구) => 등록

os -> 부모 프로세스 (알림) => 콜백함수 호출

 

시그널 종류

SIGALRM : alarm 함수호출을 통해서 등록된 시간이 된 상황

SIGINT : crtl + c 가 입력된 상황

SIGCHILD : 자식 프로세스가 종료된 상황

 

signal(), 

시그널 등록 함수

타입과, 함수포인터 전달

 

sigaction() 

운영체제와 상관 없이 동일한 인터페이스 제공

실제로 signal() 대신 sigaction() 함수 사용

 

프로세스 기반 다중접속 서버 모델

1단계 - 에코서버(부모프로세스)는 accpet 함수 호출을 통해서 연락 요청을 수락한다.

2단계 - 이때 얻게 되는 소켓의 파일 디스크립터를 자식 프로스에게 생성해서 넘겨준다.(실제로 fork 함수로 프로세스가 생성되면 부모프로세스의 모든 정보가 자식프로세스에도 복사 된다.)

3단계 - 자식 프로세스는 전달 받은 파일 스크립터(클라이언트와 연결에 필요한)를 바탕으로 서비스를 제공한다.

4단계 - 부모 프로세스는 클라이언트 소켓을 자식 프로세스는 리스닝 소켓을 제거한다(필요 없으므로) => 하나의 커널 오브젝트를 두개의 디스크립터가 가리키게 되면 모든 디스크립터가 제거되야 커널 오브젝트가 소멸 되므로 미리 관리 해주는 것이 좋다.

 

 

728x90
728x90

getsockopt()

: socket option을 얻기 위한 함수

 

setsockopt()

: socket option을 세팅 하기 위한 함수

 

모든 옵션이 get, set이 되는 건 아니다.

 

입출력 버퍼 사이즈 또한 세팅, 반환이 가능 (하지만 실제로 세팅해도 적극적 참고만 이루어질 뿐 정확하게 반영되진 않는다.)

 

time-wait

- 주소할당 에러의 원인

- tcp 소켓 종료 시 4-way shaking의 첫 번째 메시지를 전달하는 호스트의 소켓은 time-wait 상태를 거친다. 그 동안에는 소켓이 소멸되지 않아서(프로그램이 종료 됐지만, 여전히 운영체제에서는 사라지지 않음) 할당 받은 port를 다른 소켓이 할당 할 수 없다.

- 먼저 메시지를 보낸 호스트의 마지막 ack 메시지가 상대방 호스트에게 제대로 전달되지 않은 경우를 대비해서 time-wait를 가지게 된다. ack 메시지가 제대로 전달 되지 않았으면 상대 호스트는 다시 fin 메시지를 보낼 것이기 때문이다.

- 이러한 time-wait는 상대 호스트의 fin 메시지를 받으면 timer가 재가동 되기 때문에 경우에 따라서는 굉장히 길어질 수 도 있다. 

 

reuseaddr (소켓 옵션)

- time-wait를 무시하고 port를 할당이 가능하도록 코드를 수정해야 한다.

 

nagle 알고리즘

-인터넷의 과도한 트래픽을 막기 위해서 디자인 된 알고리즘

-일반적으로 목적이 분명한 게 아니라면 nagle알고리즘을 적용하는 것이 좋다.

-데이터에는 헤더가 붙기 때문에 모아서 보내는 게 효율적이다.

-앞서 전송한 데이터에 대한 ack 메시지를 받아야만 다음 데이터를 전송하는 알고리즘이다.

-nagle 알고리즘을 적용하면 마치 tcp가 udp와 같은 퍼포먼스를 발휘 할 수도...?

-socket option 이름은 TCP_NODELAY (기본값이 on 상태이다)

 

728x90
728x90

도메인 이름

DNS에 의해 IP로 변환어 접속이 가능하다.

 

DNS서버

도메인 이름을 IP로 변환해주는 서버. 

DNS는 일종의 분산 데이터베이스 시스템이다.

계층적으로 구성되어 있다.

 

gethostbyname함수

도메인 이름으로 IP 주소(hostent 구조체) 얻어 오기

 

gethostbyaddr함수

IP주소로 도메인 정보 (hostent 구조체) 얻어 오기

 

hostent 구조체 

h_name : 공식 도메인 이름

h_aliases : 별칭의 도메인 이름들.

h_addrtype : 반환된 IP의 정보가 IPv4인 경우, AF_INET이 반환

h_length : 반환된 IP 정보의 크기, IPv4의 경우 4, IPv6의 경우

h_addr_list : IP 주소 정보들(char** 타입인 이유는 ipv4는 4바이트 ipv6는 16바이트 이기 때문에 일관되게 1byte 배열로 취급하기 위해서 char* 배열을 쓴다. 현대 였으면 VOID**를 했겠지만 구조체 디자인될 시점에는 void** 가 없었기 때문에) 

 

 

 

 

 

 

728x90
728x90

close 및 closesocket 함수

소켓의 완전 소멸을 의미

소켓이 소멸되므로 더 이상의 입출력은 불가능

상대방의 상태에 상관없이 일방적인 종료의 형태를 띤다.

상대 호스트의 데이터 송수신이 아직 완료되지 않은 상황이라면 문제가 발생한다.

이를 위한 대안이 half-close 기법이 존재  

 

half close : 어플리케이션 계층에서 동작

4way close : 버퍼 수준에서 동작

 

shutdown(socket, SHUT_WR) : 해당 소켓의 write 버퍼 스트림을 닫음. EOF를 보낸다.

728x90
728x90

UDP 소켓과 TCP소켓의 데이터 송수신 비교

flow control이 없음

ip와 크게 다르지 않다.

데이터 분실 및 손실 위험이 있다.

전송이 빠르다.

 

코드레벨에서의 TCP와 UDP의 차이점

tcp는 연결과 해제의 개념이 있음

udp는 연결 개념이 없다. 때문에 보낼 때 마다 목적지 정보를 담아서 보내야 함.

udp는 서버 소켓(listen socket)과 클라이언트 소켓의 구분이 없다.

 

주요 함수

sendto(+주소포함)

recvfrom(+주소정보확인)

 

데이터 경계가 있기 때문에 recvfrom 과 sendto 함수 호출이 1대1이다.

 

unconnected udp 소켓의 sendto 함수 호출 과정

1단계 : UDP 소켓에 목적지 IP와 port번호 등록

2단계 : 데이터 전송

3단계 : UDP 소켓에 등록된 목적지 정보 삭제

매번 반복적인 1,3단계가 발생하기 때문에 이를 줄이기 위해 connected UDP 소켓 등장

 

connected udp 소켓

connected UDP 소켓은 TCP 처럼 실제 연결을 의미 하지 않는다. (connect 함수 호출로 주소등록 가능(실제 접속은 아님))

단순히 목적지 정보를 등록해서 이후에는 read, write를 통해 데이터 송신을 가능하게 한다.

 

 

728x90
728x90

TCP와 UDP에 대한 이해

 

TCP/IP 프로토콜 스택

- 인터넷 기반의 데이터 송수신을 목적으로 설계된 프로토콜 스택

- 큰 문제를 작게 나눠서 계층화 한 결과

- 7계층을 세분화가 되며, 4계층으로도 표현함

 

4계층 '애플리케이션 -> TCP/UDP (전송)-> IP(라우터, 인터넷) -> LINK(네트워크 엑세스)

7계층 '애플리케이션 -> 표현 -> 세션 -> 전송 -> 네트워크 -> 데이터 링크 -> 물리 계층

 

4계층

Link 계층 : 물리적인 연결 (라우터 자체는 물리계층)

Ip 계층 : 라우팅(경로설정)은 ip 계층 --> 이 단계만 되어도 물리적, 논리적 연결이 가능함

전송 계층 : 데이터를 어떻게 보낼 것 인가.

어플리케이션 계층

 

IP만으로도 데이터를 주고 받는 것은 가능하다.

 

프로그래머에 의해서 완성되는 application 계층

- 응용프로그램의 프로토콜을 구성하는 계층

- 소켓을 기반으로 완성하는 프로토콜을 의미함

- 소켓을 생성하면 link,ip,tcp/udp 계층에 대한 내용은 감춰진다.

- 응용 프로그래머는 application 계층의 완성에만 집중하면 된다.

 

tcp기반 서버, 클라이언트의 구현

 

tcp 서버의 기본적인 함수 호출 순서

socket(소켓생성) -> bind (소켓에 주소할당)-> listen(연결요청 대기상태) -> accept(연결허용) -> read/write -> close

 

서버측 >>

listen 함수를 통해서 소켓을 listen socket으로 지정하는 셈. 

listen socket이 하는 일은 지정된 socket을 이용해서 연결요청을 대기 큐에 전달하는 역할을 한다.

listen socket은 실질적인 연결(서비스)를 제공하진 않음. 단순히 대기 큐에 등록해주는 역할

추가적인 소켓이 생성되면서 연결이 되는 상황은 accept 함수가 호출 됐을 경우다. accept 함수에서 listen socket을 인자라 받는 이유는 listen socekt 과 연결요청 대기큐가 쌍을 이루기 때문입니다. (listen socekt이 두개면 대기큐도 두개가 된다) 

 

!! 소켓 프토번호에 관해서

소켓은 기본적으로 중복된 포트번호를 갖을 수 없다. 왜냐하면 외부로 부터 접속 할 때 포트번호는 어떤 소켓에 접속 될지 결정하는 요소가 이기 때문이다.

그러면 Listen scoekt 포트가 80이라면 클라이언트는 서버 ip + listen 포트로 접속 요청을 하게 된다. 클라이언트는 물리적으로 다르기 때문에 다른 클라이언트도 같은 80포트로 접속하는 소켓을 갖는 건 문제가 되지 않는다.

그렇다면 accept를 통해 실질적으로 클라이언트와 통신하게 되는 소켓은 어떤 포트를 갖을까?

정답은 accept로 인해 생성된 소켓은 인자로 넣어준 listen socket의 포트번호가 맵핑된다. 프로그래머가 임의로 같은 포트를 지정할 순 없지만 이 경우엔 운영체제에 의해 맵핑 되기 때문에 가능하다.

그렇다면 클라이언트가 다수 연결이 되면 다수의 동일한 포트(listen socket 포트와 같은)의 소켓이 존재하게 되는데 이때 클라이언트 소켓과 서버측 소켓은 어떻게 맵핑 되는걸까? 이떄는 단순히 포트 번호로만 맵핑하는 것이 아니라 ip 주소까지 확인하여 적절한 socket으로 read/write를 하게 된다.

 

클라이언트측>>

socket -> connect -> read/write -> close

 

Iterative 기반의 서버, 클라이언트 구현

데이터의 경계가 존재하지 않기 때문에 read 와 write 함수의 사용시 유의.

윈도우와 리눅스의 차이점 : read/write 대신 send/recv, WSAStarup 과 같은 윈속 라이브러리 로드 필요.

 

데이터를 어떻게 주고 받고 해석할지를 프로그래밍 하는 것이 어플리케이션 프로토콜이다.

 

TCP 소켓에 존재하는 입출력 버퍼

wrtie read 되는 상황이 실제로 쓰고 읽는 상황이 아니라 write 되면 출력버퍼에 데이터를 전달하고 이를 tcp 프로토콜에 근거해서 물리적인 전달이 이루어진다. 이와 같은 버퍼 때문에 슬라이딩 윈도우 프로토콜(패킷 흐름제어)이 적용 가능하다.

 

TCP의 내부 동작 원리

- 상대 소켓과의 연결 : 3way handshaking

소켓 a : hey socket B bro, 할말 있으니까 연결 좀 하자

소켓 b : okay bro. I am ready.

소켓 a : okay thanks

 

- 데이터 통신 : ack = seq + 전송된 bytes + 1 => 이 값을 통해 데이터를 완전히 받았는지 아닌지 확인 가능.

 

- 연결 정료 : 4way handshaking (일반적인 종료로 인한 데이터의 손실을 막기 위함)

소켓 a : 연결 끊으려는데..

소켓 b : 어 잠시만

소켓 b : okay 끊으세요.

소켓 a : okay bye~

 

728x90

+ Recent posts