본문 바로가기

Node.js

Node.js(3) - Network, TCP Server/Client 구축

728x90
반응형

1) Node.js

   1-1) Network

   1-2) TCP Server, Client 구축

 

 

 

 

 

1) Node.js

1-1) Network
Shell(사용자 혹은 user mode)
프로세스를 만들 때도 결국 이는 shell에서 시작한다!
파일을 실행한다거나 파일을 편집할 때 사용하는 명령어를 "shell script"라 한다!
프로세스 또한 여기 영역이라고 보면 된다!

Kernel
shell이 명령어를 내려주면 이를 실제로 동작시켜주는 역할을 하는 것이 Kernel이다!
하드웨어를 조작하는 역할을 하는 것이 "Kernel"이다!
실제로 하드디스크에 저장하는 내용들이 Kernel에서 처리된다고 보면 된다!



OSI 7계층

계층 구조는 아래에서 위로 처리(Physical Layer부터 Application Layer로 순차적으로 올라가면 처리되는 것을 의미함)된다!

프로세스에서 처리할 수 있는 Layer의 영역 : 5 ~ 7 layer(Session Layer ~ Application Layer)

1. Physical Layer
NIC(Network Interface Card) : LAN 카드를 의미하며, 이는 Physical Layer에 해당함!
LAN 선을 꽂으면 여기로 전기 신호가 들어오고, 이를 컴퓨터가 받아들인 후 이것을 다시 0, 1의 값으로 치환해줌(실제로 물리적으로 연결되는 장치를 뜻함)

2. DataLink Layer
DataLink Layer : 아날로그 데이터를 디지털 데이터로 바꿔줌!

driver : 하드웨어가 알아볼 수 있는 소프트웨어(일종의 OS로 하드웨어에 직접적으로 연결해주는 일종의 중간다리 개념의 소프트웨어)

3. Network Layer

인터넷이라고 보면 됨!

driver(DataLink Layer)에서 전기신호를 받아서 IP(Network Layer와 같음)를 통해 데이터를 받아가지고 이를 다시 TCP(Transport Layer와 같음)에 올린다는 개념!

4. Session Layer
session : 내가 나임을 증명하는 처리 방식

 


백엔드 작업 시 이는 5 ~ 7 Layer의 영역이다!
Transport Layer와 Network Layer는 Kernel 영역이기에 우리가 컨트롤할 수 없다!!
(중요!!) 프로세스 간에는 데이터 공유가 안되기에 프로세스 간의 데이터를 공유하기 위해서는 네트워크가 필요하다!!

socket은 단순히 파일이라고 보면 된다!
프로세스는 socket을 통해 데이터를 넘겨주고,
socket이 TCP에 해당 데이터를 넘겨준다!

socket의 역할
프로세스까지 데이터가 도달하기 위해서는 socket(내 프로세스가 누구와 통신할지 어떻게 통신을 받을 것인지에 대한 통신 경로를 만들어주는 역할)을 통해 주고받아야 한다!

 


식별자
"식별자"는 내가 나임을 증명하는 방법이라 보면 된다!!
ex) 카카오 아이디 등
하나의 element가 존재할 때 이에 대해 고유값을 부여하기 위해 ID를 사용했다는 것을 기억할 것!

 

  • DataLink Layer : 내 컴퓨터가 LAN 선에 꽂힌 단계로 보면 됨! 이에 해당 layer를 주로 Ethernet(유선 인터넷)이라 부름!
  • Network Layer : 실제로 우리가 사용하는 Internet이다!
  • Transport Layer : 데이터를 어떻게 받을지에 대한 프로토콜(통신을 하기 위한 약속의 개념)에 대한 내용이다!
  • Session Layer : SSL(인증에 대한 프로토콜과 관련된 개념)이 대표적이다!
  • Application Layer : HTTP 프로토콜이 대표적이다!

네트워크는 기본적으로 데이터가 짤려져서 들어간다!
실질적으로 데이터 조각(packet)을 던진다.

컴퓨터 간에는 고유한 키 값을 공유하고 있다!
=> 이때 식별자를 사용하여 특정 컴퓨터에 데이터를 보낼 수 있다!

Mac Address: 하드웨어에 박혀있는 고유한 키 값(LAN 카드의 고유한 키 값)
컴퓨터에 2개의 LAN 카드를 꽂을 수 있기에(유/무선) 이 경우 Mac Address 또한 2개라 볼 수 있다!

IP : Network Layer, 즉 인터넷이 통하는 네트워크에서 어떤 데이터를 수신하고 송신하는 통신에 대한 규약

host : 컴퓨터와 동일한 개념으로 즉, LAN 카드(NIC)가 연결된 컴퓨터를 말함!
localhost : 인터넷이 되는 본인 컴퓨터를 의미함!

Transport Layer의 고유한 키 값은 "port"이다!!
port : IP 내에서 프로세스를 구분하기 위해 사용하는 번호

MacOS의 경우, ifconfig를 터미널에 입력하여 ip 정보 확인 가능!
(Windows의 경우, "ipconfig /all")

  • MacOS의 경우, "ether"가 Mac Address(물리적 주소)이다!
  • MacOS의 경우, "inet"이 ip이다!

 

 

 

 

 

host는 기본적으로 컴퓨터와 인터넷이 연결된 것이다!
host는 "End-point"와 "Switch"의 2가지 형태로 나뉜다!


Switch : 컴퓨터를 연결할 수 있게 도와주는 컴퓨터(인터넷 연결을 도와주는 컴퓨터)
ex) LAN 선이 물리적으로 연결(LAN 카드가 꽂혀 있는 것을 의미)되어 있어야 서로 다른 컴퓨터끼리 통신이 가능하다!

      => 이를 위해 공유기가 사용되는데 여기서 공유기가 Switch라 볼 수 있다!

host : 인터넷이 되는 디바이스(인터넷이 되는 기계)
ex) 스마트폰 등이 host에 해당됨!

end-point

  • 도착지점과 시작지점 둘다를 의미하는데 즉, 단말기가 end-point라 볼 수 있다!
  • 블록체인의 P2P 시스템을 생각했을 때 여기서 peer 하나하나를 end-point로 볼 수 있다!!
  • client와 server가 각각 end-point이다!

 

Switch

 

 

네트워크의 좋은 예시 - 고속도로
고속도로 또한 일종의 망이므로 마치 네트워크 망과 같다!

Switch : 경로를 찾아가기 위해 꺾는 것

  • L2 Switch : Mac Address 기준으로 Switch한 것
  • Router(L3 Switch) : IP 기준으로 Switch한 것
  • L4 Switch : port 기준으로 Switch한 것

packet : 데이터 조각들
routing table이 host마다 있음!

모든 컴퓨터가 연결되어 있는 것이 Internet이다!

<정리>

  • Routing Table : 이정표
  • Router : 교차로(L3)
  • Packet : 데이터
  • Internet : 도로망

 

TCP
socket을 통해 TCP에 데이터를 넘기고 TCP는 다시 IP로 데이터를 넘기고 이를 다시 driver를 거쳐 NIC로 보냄!
packet이 데이터 조각 형태로 온다!

 


L4
TCP, UDP
- TCP에서는 연결이라는 개념이 있음!(TCP는 이미 물리적으로 연결되어 있기에 여기서 말하는 연결은 데이터적 연결이라고 보면 됨!)

  • Connection, Session

- 연결은 순차적으로 진행된다!(TCP의 특징)
- 상태라는 개념을 가지고 있다!(여기서 상태란 로그인, 로그아웃을 데이터적으로 표현했다라고 보면 됨!!)

즉, 네트워크란 관계 혹은 연결의 개념이기에 한 대의 컴퓨터로는 불가능하고 최소 2대 이상의 컴퓨터가 필요하다!

- 전화를 거는 사람: client
- 전화를 받는 사람: Server

>> 전화를 받는 사람은 핸드폰을 가지고 있어야 하는 것처럼 Server 컴퓨터는 언제든 데이터를 받을 준비(Listen: 핸드폰 개통했으니 전화를 걸 수 있다는 개념, port: 전화번호의 개념)가 되어 있어야 한다!!

 

SYN : Client가 Server 컴퓨터에 보낼 때 임의의 넘버값(시퀀스 넘버)을 보낸다!(Client가 요청을 보낼 때, Server가 응답할 때 시퀀스 넘버를 생성한다!)

 

데이터적으로 서로 주고받는 것을 "연결"이라 한다!

즉, Server 컴퓨터에서 Client에 응답을 보낼 시 SYN과 더불어 ACK를 만들어 보내는데 이때 client는 연결됐다고 인식한다!

이후 client에서 다시 ACK를 보내주는데 Server 컴퓨터는 이때 연결됐다고 인식한다!

이처럼 Server 컴퓨터와 Client가 연결을 인식하는 시점이 서로 다른데 이 과정을 "3-way Handshaking"라 한다!

 

TCP의 3-way Handshaking 과정

[STEP 1]

A클라이언트는 B서버에 접속을 요청하는 SYN 패킷을 보낸다. 이때 A클라이언트는 SYN 을 보내고 SYN/ACK 응답을 기다리는SYN_SENT 상태가 되는 것이다.

 

[STEP 2] 

B서버는 SYN요청을 받고 A클라이언트에게 요청을 수락한다는 ACK 와 SYN flag 가 설정된 패킷을 발송하고 A가 다시 ACK으로 응답하기를 기다린다. 이때 B서버는 SYN_RECEIVED 상태가 된다.

 

[STEP 3]

A클라이언트는 B서버에게 ACK을 보내고 이후로부터는 연결이 이루어지고 데이터가 오가게 되는것이다. 이때의 B서버 상태가 ESTABLISHED 이다.

위와 같은 방식으로 통신하는것이 신뢰성 있는 연결을 맺어 준다는 TCP의 3 Way handshake 방식이다.

 

 


연결 시도와 연결을 끊는 것 모두 client에서 처리된다(active적인 요소는 모두 client에서 처리한다)!
이것이 "4-way Handshaking"이다!

즉, 3-Way handshake는 TCP의 연결을 초기화할 때 사용한다면, 4-Way handshake는 세션을 종료하기 위해 수행되는 절차이다!

 

TCP의 4-way Handshaking 과정

[STEP 1]

클라이언트가 연결을 종료하겠다는 FIN플래그를 전송한다.

 

[STEP 2] 

서버는 일단 확인메시지를 보내고 자신의 통신이 끝날때까지 기다리는데 이 상태가 TIME_WAIT상태다.

 

[STEP 3]

서버가 통신이 끝났으면 연결이 종료되었다고 클라이언트에게 FIN플래그를 전송한다.

 

[STEP 4]

클라이언트는 확인했다는 메시지를 보낸다.

 

그런데 만약 "Server에서 FIN을 전송하기 전에 전송한 패킷이 Routing 지연이나 패킷 유실로 인한 재전송 등으로 인해 FIN패킷보다 늦게 도착하는 상황"이 발생한다면 어떻게 될까요? 

 

Client에서 세션을 종료시킨 후 뒤늦게 도착하는 패킷이 있다면 이 패킷은 Drop되고 데이터는 유실될 것입니다. 

이러한 현상에 대비하여 Client는 Server로부터 FIN을 수신하더라도 일정시간(디폴트 240초) 동안 세션을 남겨놓고 잉여 패킷을 기다리는 과정을 거치게 되는데 이 과정을 "TIME_WAIT" 라고 합니다.


(중요!!) port를 사용하고 Listen, 즉 server를 대기시킨다!!
client는 연결할 때 port를 생성한다!
연결과 끊김이 있는 것을 "상태"라 하는데 TCP에는 이러한 상태가 있다!!

 

 

 

 

1-2) TCP Server, Client 구축

Node.js 환경에서 구축할 예정.

내장모듈 "net"을 사용함!
=> port(L4), IP(L3)를 기준으로 구분하기 위한 용도!

Server, Client를 구축하는 경우, 기본적으로 Server를 먼저 구축함!!

$ npm init => package.json 파일을 생성해줌!!

 

 

 

server.js

const net = require("net");
const PORT = process.env.SERVER_PORT || 3000;
const HOST = process.env.SERVER_HOST || "127.0.0.1";

const body = Buffer.from(`<h1>Hello World!</h1>`);
const response = `HTTP/1.1 200 Ok
Connection: keep-alive
Keep-Alive: timeout=5
Content-type: text/html
Content-length: ${body.length}

${body.toString()}`;

/*
    true || false = true
    false || true = true
    true || true = true
*/

const server = net.createServer((client) => {
  // createServer : port를 열겠다는 의미와 같음!
  client.setEncoding("utf8");

  // Buffer : 2진수 데이터를 16진수로 담아서 보여줌!
  // 문자셋(문자집합) : utf-8
  // on : 연결을 인식하면 callback 함수를 실행시킨다는 의미
  client.on("data", (data) => {
    console.log(data); // "data" 변수의 데이터 타입: Buffer
    // console.log(data.toString("hex")); // hex는 글자를 16진수로 표현한 것이다!
    // console.log(data.toString("utf8"));

    // client.write("나 응답 보냄!!");
    client.write(response);
  });

  client.on("close", () => {
    console.log("good bye!");
  });
});

/*
	1: port : 3000
	2: host : 127.0.0.1 => 내 컴퓨터의 host(localhost)
	3: listen될 경우, 실행할 함수(callback)
*/

server.listen(PORT, HOST, () => {
  console.log(`Server Listening Port : ${PORT}`);

  server.on("connection", () => {
    console.log("client가 접속함!");
  });
});

 

client.js

const net = require("net");

const config = { port: 3000, host: "127.0.0.1" };
// const config = { port: 3000, host: "192.168.192.1" };
const socket = net.connect(config);

socket.on("connect", () => {
  console.log("connected to server!");

  socket.write("나 데이터 보낸다!");
});

socket.on("data", (chunk) => {
  console.log(`Received : ${chunk}`);
  socket.end();
});

// HTTP는 기본적으로 TCP 통신을 한다!!
// TCP 통신은 쌍방향 통신이 가능하다!
// HTTP 프로토콜의 규격을 지키기만 한다면 우리는 브라우저의 요청만으로도 처리를 할 수 있다!
// 브라우저에서 "http://127.0.0.1:3000"
// 나의 TCP Server가 받을 수 있게 처리를 한다!