TCP는 데이터를 물처럼 연속적으로 보내는 '스트림' 방식
그래서 클라이언트가 { "type": "login" } 이라는 데이터를 한 번에 보내도, 서버는 아래처럼 여러 조각으로 나누어 받을 수 있음
{ "type":"login" }이 상태에서 각 조각을 받을 때마다 JSON.parse()를 시도하면, 문법이 완성되지 않았으므로 에러가 발생할 수 있다.
\n)를 붙일 수 있다.\n을 붙여서 전송'{ "type": "login" }\n'const net = require('net');
const HOST = '127.0.0.1';
const PORT = 9999;
// 새로운 소켓 객체를 생성하여 서버에 연결
const client = net.createConnection({ host: HOST, port: PORT }, () => {
console.log(`✅ 서버(${HOST}:${PORT})에 연결되었습니다.`);
// 보낼 메시지 리스트 (JavaScript 객체 배열)
const messages = [
{ type: "login", user: "neo" },
{ type: "message", text: "Follow the white rabbit." }
];
messages.forEach((msg, index) => {
// 메시지 전송에 약간의 시간차를 두어 스트림 환경을 시뮬레이션
setTimeout(() => {
// 1. JavaScript 객체를 JSON 문자열로 변환
const jsonString = JSON.stringify(msg);
// 2. 메시지 끝에 구분자('\n') 추가
const messageToSend = jsonString + '\n';
console.log(`📤 전송 시도: ${messageToSend.trim()}`);
// 3. 서버로 데이터 전송 (문자열을 보내면 자동으로 UTF-8로 인코딩됨)
client.write(messageToSend);
}, index * 500); // 0ms, 500ms 간격으로 전송
});
// 모든 메시지를 보낸 후 연결 종료 예약
setTimeout(() => {
console.log('🏁 모든 메시지를 전송하여 연결을 종료합니다.');
client.end(); // 'FIN' 패킷을 보내 정상적으로 연결을 종료
}, messages.length * 500);
});
// 서버로부터 데이터를 받을 경우 (이 예제에서는 서버가 응답을 보내지 않음)
client.on('data', (data) => {
console.log('서버로부터 데이터 수신:', data.toString());
});
// 'close' 이벤트: 연결이 완전히 종료되었을 때 발생
client.on('close', () => {
console.log('🔗 서버와의 연결이 종료되었습니다.');
});
// 'error' 이벤트: 오류 발생 시
client.on('error', (err) => {
console.error('TCP 클라이언트 오류:', err.message);
});
\n 문자가 있는지 확인\n이 발견되면, 그 앞까지의 문자열을 잘라내 완전한 JSON 메시지로 간주하고 처리// Node.js의 내장 'net' 모듈을 불러옵니다.
const net = require('net');
const HOST = '127.0.0.1';
const PORT = 9999;
// TCP 서버 생성
const server = net.createServer((socket) => {
// 'socket'은 현재 연결된 클라이언트를 나타내는 객체입니다.
console.log(`🤝 클라이언트(${socket.remoteAddress}:${socket.remotePort})가 연결되었습니다.`);
// 1. 클라이언트별로 독립적인 버퍼를 생성합니다.
let buffer = '';
// 2. 'data' 이벤트 리스너: 클라이언트로부터 데이터 조각(chunk)을 수신할 때마다 실행됩니다.
socket.on('data', (chunk) => {
// 수신한 데이터(Buffer 객체)를 문자열로 변환하여 버퍼에 추가합니다.
buffer += chunk.toString();
// 3. 버퍼에 구분자('\n')가 있는지 확인하고, 있다면 처리합니다.
let delimiterIndex;
while ((delimiterIndex = buffer.indexOf('\n')) !== -1) {
// 4. 구분자 앞부분을 잘라내 완전한 메시지를 만듭니다.
const message = buffer.substring(0, delimiterIndex);
// 4-1. 버퍼에서는 처리한 메시지 부분을 삭제합니다.
buffer = buffer.substring(delimiterIndex + 1);
if (message) {
try {
// 5. JSON 문자열을 JavaScript 객체로 파싱합니다.
const json = JSON.parse(message);
console.log(`📥 수신 및 처리 완료:`, json);
} catch (e) {
console.error('⚠️ JSON 파싱 오류 발생:', message);
}
}
}
});
// 'end' 이벤트 리스너: 클라이언트가 연결을 종료했을 때 실행됩니다.
socket.on('end', () => {
console.log('🔌 클라이언트와의 연결이 종료되었습니다.');
});
// 'error' 이벤트 리스너: 오류 발생 시 실행됩니다.
socket.on('error', (err) => {
console.error('💥 소켓 오류:', err.message);
});
});
// 지정된 포트와 호스트에서 클라이언트 연결을 수신 대기합니다.
server.listen(PORT, HOST, () => {
console.log(`📢 서버가 ${HOST}:${PORT}에서 클라이언트 연결을 기다립니다...`);
});