Application 계층은 application개발에 필요한 것들을 지원한다.
그 중 하나가 socket이다.
socket은 Application 계층과 Transport 계층 사이의 인터페이스다.
Transport 계층은 운영체제에 의해 제어되기 때문에 개발자를 직접적으로 지원해주지 않는다.
그렇기 때문에 개발자는 socket을 통해 Transport 계층의 프로토콜을 간접적으로 사용한다.
Transport 계층의 프로토콜은 UDP와 TCP가 있다.
두 프로토콜은 프로세스간의 통신을 지원한다.
차이점은 TCP는 연결기반이고, UDP는 그렇지 않다.
그렇다면 개발자가 어떻게 socket으로 통신 프로토콜을 사용하는지 알아보자.
먼저 서버는 메시지를 받기위한 socket을 하나 생성한다.
서버로 오는 메시지는 모두 이 socket을 통해 들어온다.
// node에서 udp를 사용하기 위해 제공하는 모듈
const dgram = require("dgram");
// IPv4타입의 주소를 사용
const server = dgram.createSocket("udp4");
// 에러가 발생했을 때
server.on("error", function (err) {
err?.console.log(err);
server.close();
});
// 메시지를 수신했을 때
server.on("message", function (msg, rinfo) {
console.log(msg.toString());
const reply = "hohoho";
server.send(reply, 0, reply.length, rinfo.port, rinfo.address);
});
// 소켓이 생성되어 메시지를 받을 준비가 완료됨.
server.on("listening", function () {
console.log("listening...");
});
// 6000번 포트에서 메시지를 기다림
server.bind(6000);
공개된 서버의 주소와 포트번호로 클라이언트는 메시지를 보낼 수 있다.
이때 운영체제에 의해 클라이언트 메시지에는 ip주소와 포트번호가 추가 된다.
서버측에서 클라이언트를 식별하기 위해서다.
const dgram = require("dgram");
const client = dgram.createSocket("udp4");
const SERVER = "서버의 ip주소 또는 도메인";
const PORT = 6000;
const init = () => {
const msg = "hahahaha";
setTimeout(() => client.send(msg, 0, msg.length, PORT, SERVER), 500);
setTimeout(() => client.send(msg, 0, msg.length, PORT, SERVER), 1000);
};
// 메시지를 수신했을 때
client.on("message", (msg) => {
console.log(msg.toString());
});
init();
클라이언트에서 찍힌 콘솔이다. 로컬에서 클라이언트를 실행하였다.
서버에서 찍힌 콘솔이다. 서버는 ec2 인스턴스에서 실행 중이다.
tcp는 연결기반 프로토콜이다.
tcp서버는 웰컴 소켓이 돌아가고 있다.
클라이언트쪽에서 연결을 요청하면 웰컴 소켓에서 클라이언트를 위한 전용 소켓을 할당한다.
그리고 그 소켓을 통해서만 통신을 한다.
// node에서 tcp를 지원하는 모듈
const net = require("net");
// 클라이언트가 연결을 요청하면 통신을 위한 전용 소켓이 생성된다.
const server = net.createServer((socket) => {
// 메시지 발신
socket.write("Welcom");
// 메시지 수신.
socket.on("data", (msg) => {
console.log(msg.toString());
socket.write("hohoho");
});
// 연결해제를 요청했을 때
socket.on("close", () => {
console.log("closed.....");
});
});
// 웰컴 소켓이 5000번 포트에서 실행 중이다.
server.listen(5000, () => {
console.log("listening 5000...");
});
클라이언트는 먼저 서버로 connection을 요청한다. ( 3 way hand shake )
connection이 완료되면 소켓을 통해 서버와 메시지를 주고 받는다.
const net = require("net");
const SERVER = "서버의 ip주소 또는 도메인";
const PORT = 5000;
// 3 way hand shake를 통해 연결을 요청한다.
const socket = net.connect(PORT, SERVER);
// 전용 connection을 통해 통신한다.
socket.on("connect", () => {
console.log("connection success");
setTimeout(() => socket.write("hahahahaha"), 500);
setTimeout(() => socket.write("hahahahaha"), 1000);
setTimeout(() => socket.write("hahahahaha"), 1500);
setTimeout(() => socket.write("hahahahaha"), 2000);
setTimeout(() => {
socket.write("hahahahaha");
// 연결을 해제한다.
socket.end();
}, 2500);
});
// 메시지 수신 시 호출된다.
socket.on("data", (msg) => {
console.log(msg.toString());
});
클라이언트측 로그다. 클라이언트는 로컬에서 실행 중이다.
서버측 로그다. 서버는 ec2에서 실행 중이다.
참고
컴퓨터 네트워크 - 이화여대 이미정 교수님
Computer Networking: A Top-Down Approach 6th ed. J.F. Kurose and K.W. Ross