본문 바로가기
시작/TIL(Today I Learned)

221208 - TCP/UDP, TCP서버

by 백씨네 2022. 12. 9.

오늘 내가 배운 것

1. TCP/UDP

 

2. TCP 통신하기

 

 

 

 

TCP(Transmission Control Protocol)

데이터를 중요하게 생각하여 확실히 주고받고 싶을 때는 `TCP`를 사용한다.

TCP는 통신할 컴퓨터끼리 ‘보냈습니다’, ‘도착했습니다’라고 서로 확인 메시지를 보내면서 데이터를 주고받음으로써 통신의 신뢰성을 높인다.

UDP(User Datagram Protocol)

데이터의 신뢰성은 보다 빨리 보내고 싶을 때는 ‘UDP’를 사용한다.

UDP는 데이터를 보내면 그것으로 끝이므로 신뢰성은 없지만 확인 응답과 같은 절차를 생략할 수 있으므로 통신의 신속성을 높인다.

 

TCP / IP

TCP : L4(Transport layer) - 식별자 : Port
IP :  L3((Network layer) - 식별자 : IP

 

 프로그램과 프로세스의 차이

프로그램은 코드들을 모아놓은 상태를 말하는 것이고, 프로세스는 코드를 실행시킨 상태라고 간단하게 설명할 수 있다.

 

 

프로세스, 스레드의 차이

데이터 공유 여부
프로세스는 데이터 공유가 안되지만, 스레드는 데이터를 공유할 수 있다.

프로세스끼리 데이터를 공유하기 위해서 어떻게 해야 할까?

- 통신으로 데이터를 공유해야 한다.

https://baekspace.tistory.com/79

 

221206 - Node 기초, 프로그램과 프로세스와 스레드

오늘 내가 배운 것 1. 프로그램과 프로세스 2. 스레드 3. 논 블로킹 I/O 4. 모듈 1. 프로그램과 프로세스 프로그램 (Program) 작업을 위해 실행할 수 있는 파일(코드의 집합) ex) .exe 파일, 크롬, 카카오톡

baekspace.tistory.com

 

 

TCP 특징

Layer 4 계층으로서 식별자는 port


1. 연결이 있다. 
    - 논리적 연결 (3-way) / 연결이 성공해야 통신이 가능하다.

 

2. 순서가 있다.
- 데이터의 전송 순서를 보장한다.

 

 

TCP 통신하기


TCP는 커널단에 위치해 있고 OS 깊은 곳에 있기 때문에 우리가 조작할 수 없다. NodeJS에 TCP 통신을 할 수 있는 내장 모듈이 있는데 그 내장 모듈이 `net`이다. 그래서 'net을 이용해서 TCP통신을 한다.

 

프로세스 2개를 만들 것이다.
서버 역할의 server.js와 클라이언트 역할의 client.js

 

TCP 통신 연결을 위한 3way Handshake , 연결 해제를 위한 4way Handshake

 

3-way Handshake

3 way 방식은 코드로 진행을 살펴볼 예정이다. 위에 그림의 순서에 따라 진행된다.

 

1. 서버를 대기 상태로 둔다. 요청을 받아들이기 위해서 실행이 되는 상태여야 한다.

//server.js 파일

const net = require('net')
const server = net.createServer()


//3가지 인자가 필요하다. 
// 1. Port, 2. Host, 3. 실행할 함수(callback)
server.listen(port, host, ()=>{
    console.log('server start')
})

변수를 지정해서 인자로 넣고 대기상태를 만들어준다.

//server.js 파일

//process.env.SERVER_PORT가 없으면 3000
const port = process.env.SERVER_PORT || 3000

//process.env.SERVER_HOST가 없으면 "127.0.0.1"
const host = process.env.SERVER_HOST || "127.0.0.1"

>> 실행을 해보면 서버가 꺼지지 않고 대기상태로 열려 있다.

 

 

2. 통신을 보낼 client를 만든다

//Client.js

// client도 tcp 통신을 하기 위해 net을 이용해서 만든다.
// server와 연결하기 위해서 server의 주소를 알아야한다.

const net = requier('net')
const config = {port : 3000, host : "127.0.0.1"}
net.connect(config)  

//server와 client 파일을 둘 다 실행시키는 순간 3way가 진행됐다.

>> 아무런 오류가 없다면 이 상태에서 이미 연결이 끝났다.

하지만 눈으로 확인할 수 없기 때문에 연결 시에 나타나는 메시지를 만들어 줄 것이다.

VSCode에서 Server용 터미널과 Client용 터미널을 따로 열어서 실행시켜준다.

 

3. ACK를 받았을 때 콘솔에 연결됐다는 것을 알기 위해 server에 확인을 위한 코드를 작성한다.

 

//server.js

//클라이언트가 접속한 것을 서버에 알려줘야하기 때문에 서버에 작성한다.
server.on("connection", ()=>{
    console.log("client가 접속하였습니다.")
})

 

4. syn + ack를 받았을 때 Client도 접속이 되었다는 것을 콘솔에 확인하기 위해 코드를 작성한다.

 

net 은 client와 server 두 가지에 대한 내용을 알고 있어야 하기 때문에
net.createSever() 로 server에 대한 메서드를 구분해놓고, net.connect() 로 client에 대한 메서드를 구분해놨다.

 

//client.js

//소켓 변수 설정 
//net.connect에 변수 설정을 안해도 연결까지는 되지만 결과물을 받기 위해 변수로 설정해서 사용한다.
//커넥트가 되었을 때 클라이언트에 출력이 되길 원하기 때문에 클라이언트에 작성해준다.

const socket = net.connect(config) 
socket.on('connect', ()=>{
    console.log('server에 접속이 되었습니다.')
})

이렇게 해서 3-way Handshake 가 이루어지면서 확인 메시지까지 출력이 완료되었다.

조금 더 나아가서 Client에서 Server로 데이터를 보내고 그 데이터를 Server에서 출력해보자.

 

5. Client에서 'aaa' 를 Server로 보내는 통신을 하고 싶다.

//client.js

//소켓이 연결 되었을 때 클라이언트에서 데이터를 보내야하기 때문에
//클라이언트에 작성을 해준다.

socket.on('connect', ()=>{
    console.log('server에 접속이 되었습니다.') 
    
    socket.write("aaa")
})

>> 아무런 오류 없이 전송은 됐지만 콘솔에 출력하여 주는 코드가 없기 때문에 콘솔에 변화는 없다.

 

 

6. Client에서 data를 보낼 때마다 그 데이터를 콘솔에 출력시켜준다.

//server.js

//Client는 접속한 Server가 한 개지만 Server는 접속된 Client가 많기 때문에 
//각 클라이언트가 누구인지 알아야한다.
const server = net.createServer((client)=>{
    //data가 올 때마다 콜백함수를 실행하겠다는 뜻.
    client.on("data",(data)=>{
        console.log(data)
    }) 
})

7. Client에서 보낸 data를 Server에 출력시켜 주었지만 16진수로 표현이 되어 쉽게 확인할 수 없다.

//server.js

//데이터를 받을 때 16진수로 표현 되는 문제를
//utf-8 방식으로 표현하기 위해 변환시켜주는 코드를 작성한다.

const server = net.createServer((client)=>{
    client.setEncoding('utf8')
    client.on("data",(data)=>{
        console.log(data)
    })
})

>> 이제 우리가 읽을 수 있는 데이터로 변환이 되었다.

 

더보기
Server.js
const net = require('net')
const port = process.env.SERVER_PORT || 3000
const host = process.env.SERVER_HOST ||'127.0.0.1'

const server = net.createServer((client)=>{
    client.setEncoding('utf8')
    //data가 올 때마다 콜백함수를 실행하겠다는 뜻.
    client.on("data",(data)=>{
        console.log(data)
    }) 
})

server.on("connection", ()=>{
    console.log("client가 접속하였습니다.")
})

server.listen(port, host, ()=>{
    console.log('서버 열림')
})

 

Client.js
const net = require('net')
const config = {port : 3000, host : "127.0.0.1"}
const socket = net.connect(config) 

socket.on('connect', ()=>{
    console.log('server에 접속이 되었습니다.')

    socket.write("aaa")
})

 

 

 

4-way Handshake

3 way handshake가 TCP 연결을 위한 최초 초기화 방식이라고 하면
4 way handshake는 연결 해제를 위한 방식이다.
연결 해제를 위한 방법이기 때문에 초기 상태는 당연히 연결 상태에 있어야 한다.

1. Client가 연결을 종료하겠다는 FIN플래그를 Server에게 전송한다.


2. Server는 FIN플래그를 받고 확인을 위해 ACK를 Client에게 다시 보낸다.

Server는 자신의 통신이 끝날 때까지 기다리고 Client는 ACK를 받은 후 Server의 FIN을 기다린다.


3. 통신이 다 종료된 후 연결을 해제하겠다는 FIN플래그를 Client에게 보내고 다시 Client가 보낼 ACK를 기다린다.


4. Client는 FIN 신호를 받고 ACK를 다시 Server에게 보내주며 TIME_WAIT상태에 들어간다. 이 상태는 갑작스러운 연결 종료로 인해 데이터의 손실을 막기 위해 close가 지연되는 상태이다. (초기값 240초)


5. Server는 ACK를 받고 연결을 완전히 끊는다.


6. TIME_WAIT시간이 끝난 후 Client도 연결을 끊는다.

댓글