이번 프로젝트는 Node.js를 사용하여 TCP 에코 서버와 클라이언트를 구축하고, 네트워크를 통해 기본적인 문자열 데이터를 전송하고 처리하는 방법을 시도하였습니다.
|-- handler10.js
|-- handler11.js
|-- index.js
|-- client.js
|-- server.js
|-- utils.js
|-- constants.js
|-- package.json
서버는 들어오는 메시지를 처리하며, 각 메시지를 미리 정의된 핸들러에 따라 변환합니다.
handler10.js: 들어온 메시지를 모두 대문자로 변환합니다.
const handler10 = (data) => {
const processedData = data.toString().toUpperCase();
return Buffer.from(processedData);
};
export default handler10;
handler11.js: 들어온 메시지의 문자를 역순으로 변환합니다.
const handler11 = (data) => {
const processedData = data.toString().split('').reverse().join('');
return Buffer.from(processedData);
};
export default handler11;
index.js: 핸들러를 ID별로 관리합니다.
import handler10 from "./handler10.js";
import handler11 from "./handler11.js";
const handlers = {
10: handler10,
11: handler11,
};
export default handlers;
클라이언트는 서버에 연결하여 메시지를 보내고, 서버의 응답을 처리합니다.
client.js: 서버에 간단한 메시지를 보내고, 응답을 처리합니다.
import net from 'net';
import { readHeader, writeHeader } from './utils.js';
import { HANDLER_ID, TOTAL_LENGTH_SIZE } from './constants.js';
const HOST = 'localhost';
const PORT = 5555;
const client = new net.Socket();
client.connect(PORT, HOST, () => {
console.log('서버에 연결 중...');
const message = "Hello";
const buffer = Buffer.from(message);
const header = writeHeader(buffer.length, 11); // 핸들러 ID 11은 문자열을 역순으로 변환
const packet = Buffer.concat([header, buffer]);
client.write(packet);
});
client.on('data', (data) => {
const buffer = Buffer.from(data);
const { length, handlerId } = readHeader(buffer);
console.log(`Handler: ${handlerId}`);
console.log(`Length: ${length}`);
const headerSize = TOTAL_LENGTH_SIZE + HANDLER_ID;
const message = buffer.slice(headerSize);
console.log(`서버에서 받은 메시지: ${message}`);
});
client.on('close', () => {
console.log(`연결이 종료되었습니다.`);
});
client.on('error', (err) => {
console.log(`클라이언트 오류: ${err}`);
});
서버는 연결을 수신하고, 메시지를 핸들러로 처리한 후 결과를 클라이언트에 반환합니다.
server.js: 핸들러 ID에 따라 클라이언트로부터 메시지를 처리하고, 응답을 반환합니다.
import net from 'net';
import { readHeader, writeHeader } from './utils.js';
import { HANDLER_ID, MAX_MESSAGE_LENGTH, TOTAL_LENGTH_SIZE } from './constants.js';
import handlers from './index.js';
const PORT = 5555;
const server = net.createServer((socket) => {
console.log(`클라이언트 연결됨: ${socket.remoteAddress}:${socket.remotePort}`);
socket.on('data', (data) => {
const buffer = Buffer.from(data);
const { length, handlerId } = readHeader(buffer);
console.log(`Handler: ${handlerId}`);
console.log(`Length: ${length}`);
if (length > MAX_MESSAGE_LENGTH) {
console.error(`Error: 메시지 길이 초과`);
socket.write(`Error: 메시지 길이 초과`);
socket.end();
return;
}
const handler = handlers[handlerId];
if (!handler) {
console.error(`Error: 잘못된 핸들러 ID ${handlerId}`);
socket.write(`Error: 잘못된 핸들러 ID`);
socket.end();
return;
}
const headerSize = TOTAL_LENGTH_SIZE + HANDLER_ID;
const message = buffer.slice(headerSize);
console.log(`클라이언트로부터 받은 메시지: ${message}`);
const responseMessage = handler(message);
const responseBuffer = Buffer.from(responseMessage);
const header = writeHeader(responseBuffer.length, handlerId);
const packet = Buffer.concat([header, responseBuffer]);
socket.write(packet);
});
socket.on('end', () => {
console.log(`클라이언트 연결 종료됨: ${socket.remoteAddress}:${socket.remotePort}`);
});
socket.on('error', (err) => {
console.log(`소켓 오류: ${err}`);
});
});
server.listen(PORT, () => {
console.log(`서버가 포트 ${PORT}에서 대기 중입니다.`);
});
utils.js: 메시지의 헤더를 읽고 작성하는 기능을 담당합니다.
import { HANDLER_ID, TOTAL_LENGTH_SIZE } from './constants.js';
export const readHeader = (buffer) => {
return {
length: buffer.readUInt32BE(0),
handlerId: buffer.readUInt16BE(TOTAL_LENGTH_SIZE),
};
};
export const writeHeader = (length, handlerId) => {
const headerSize = TOTAL_LENGTH_SIZE + HANDLER_ID;
const buffer = Buffer.alloc(headerSize);
buffer.writeUInt32BE(length + headerSize, 0);
buffer.writeUInt16BE(handlerId, TOTAL_LENGTH_SIZE);
return buffer;
};
constants.js: 서버와 클라이언트에서 사용되는 상수를 정의합니다.
export const TOTAL_LENGTH_SIZE = 4;
export const HANDLER_ID = 2;
export const MAX_MESSAGE_LENGTH = 1024;
기본적인 TCP 서버-클라이언트 통신을 구축하는 방법이였습니다. 클라이언트가 메시지를 서버로 보내면, 서버는 해당 메시지를 적절한 핸들러를 통해 처리한 뒤, 처리된 메시지를 다시 클라이언트로 전송합니다.
이 프로젝트를 실행하려면 다음 명령어를 사용하세요
의존성 설치
npm install
서버 시작
npm run dev
별도의 터미널에서 클라이언트 실행
npm run client