개발공부일지
NodeJS - net 모듈을 사용해서 TCP 구현 본문
목차
1. net 내장 모듈
2. Server파일, Client파일 만들고 connect하기
3. 세션이 맺어졌을때 client → server hello world라는 텍스트 보내기
4. sever → client hello world라는 텍스트 보내기
5. request(요청메시지) 파일로 보내기
6. request(요청메시지) 파싱하기
7. respones message 보내기
1. net 내장 모듈
https://nodejs.org/dist/latest-v18.x/docs/api/net.html
const net = require("net");
- net은 라이브러리로 socket을 생성해준다.
- 3way-handshake를 경험해보기위해서
- 서버는 언제 어떻게 요청이 들어올지 모르니까 listen 상태가 되어 있어야하므로 먼저 작성한다.
2. Server파일, Client파일 만들고 connect하기
// Server 파일
const net = require("net");
// console.log(net);
const server = net.createServer();
// console.log(server);
server.on("connection", () => {
console.log("연결이 되었습니다!");
});
server.listen(3000, () => {
console.log(`Server Listening on port 3000`);
});
// Client 파일
const net = require("net");
const socket = net.connect({
port: 3000,
host: "127.0.0.1",
});
socket.on("connect", () => {
console.log(`ESTALISHED`);
});
- server를 실행했을때 '연결이 되었습니다'가 뜬다면 client로가서 연결됐을때의 이벤트 작성하기
- client가 SYN을 받았을때의 이벤트로 socket.on() 에 console.log(`ESTALISHED`)를 작성,
server에도 `ESTALISHED` 작성
// Server 파일
server.on("connection", () => {
// console.log("연결이 되었습니다!");
console.log("ESTALISHED");
});
- 이것으로 세션이 맺어진것이다! (서로 데이터를 보낼수 있는 상황이 만들어진것! )
- 하지만 받은 데이터는 바이너리일거라서 16진수로 바꿔줘야한다!
→ 파싱하고 스트링으로 바꿔주어야한다!
3. 세션이 맺어졌을때 client가 server에 hello world라는 텍스트 보내기
// Client 파일
socket.on("connect", () => {
console.log(`ESTALISHED`);
socket.write("hello world!");
});
// Server 파일
server.on("connection", (socket) => {
// console.log(socket);
socket.on("data", (chunk) => {
console.log(chunk);
});
console.log("ESTALISHED");
});
- server에서의 connenciton은 socket을 주입해주어야한다. (인자값에 넣어주기)
- 다시 서버를 실행하면 Buffer로 알려준다.
- 그래서 콘솔에 chunk.toString()을 해주면 hello world를 서버에서 볼수있다.
4. sever에서 client로 hello world라는 텍스트 보내기
// Server 파일
server.on("connection", (socket) => {
console.log("ESTALISHED");
// console.log(socket);
socket.on("data", (chunk) => {
console.log(chunk.toString());
socket.write("server : hello world!");
});
});
// Client 파일
socket.on("data", (chunk) => {
console.log(chunk.toString());
});
5. request(요청메시지) 파일로 보내기
- promise사용해서 요청메시지를 반으로 잘라 2번보내는데 두번째꺼는 3초뒤에 가게끔하기
// Client 파일
const net = require("net");
const fs = require("fs").promises;
const readFileContent = async (filePath) => {
try {
const content = await fs.readFile(filePath);
return content;
} catch (e) {
throw new Error("파일 읽기 실패");
}
};
const socket = net.connect({
port: 3000,
host: "127.0.0.1",
});
// console.log(socket);
socket.on("connect", async () => {
console.log(`ESTALISHED`);
const requestMessage = await readFileContent("./request.txt");
// console.log(requestMessage.length / 2);
const halfLength = Math.floor(requestMessage.length / 2);
const firstHalf = requestMessage.slice(0, halfLength);
const secondHalf = requestMessage.slice(halfLength);
console.log(firstHalf.toString());
console.log("-----------------");
console.log(secondHalf.toString());
socket.write(firstHalf);
setTimeout(() => {
socket.write(secondHalf);
}, 3000);
});
socket.on("data", (chunk) => {
console.log(chunk.toString());
});
6. request(요청메시지) 파싱하기
GET / HTTP/1.1
Host: 127.0.0.1:3000
Connection: keep-alive
Cache-Control: max-age=0
Content-Length: 9
sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
id=boram
① header와 body 영역 완벽하게 나누기
let buffer = Buffer.alloc(0);
socket.on("data", (chunk) => {
buffer = Buffer.concat([buffer, chunk]);
// console.log(chunk.toString());
const headerEndIndex = buffer.indexOf("\r\n\r\n");
}
② buffer 내용 자르고 header start-line, 파싱 작업하기
if (headerEndIndex !== -1) {
const headerBuffer = buffer.slice(0, headerEndIndex);
const bodyBuffer = buffer.slice(headerEndIndex + 4);
// console.log(chunk.toString());
const headerLine = headerBuffer.toString().split("\r\n");
const startLine = headerLine
.shift()
.split(" ")
.map((value, index) => [START_LINE_NAMES[index], value])
.reduce((acc, line) => {
const [key, value] = line;
acc[key] = value;
return acc;
}, {});
const headers = headerLine.reduce((acc, line) => {
const [key, value] = line.split(": ");
// console.log(key, value);
acc[key] = value;
return acc;
}, {});
if (parseInt(headers["Content-Length"]) === bodyBuffer.length) {
buffer = Buffer.alloc(0);
socket.write(message);
socket.end();
console.log(startLine, headers, bodyBuffer.toString());
}
}
7. respones message 보내기
// Server 파일
const message = `HTTP/1.1 200 OK
Vary: Origin
Access-Control-Allow-Credentials: true
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Thu, 31 Aug 2023 02:09:17 GMT
ETag: W/"109-18a495a1d6c"
Date: Thu, 31 Aug 2023 02:10:09 GMT
Connection: keep-alive
Keep-Alive: timeout=5
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
Hello world!
</script>
</body>
</html>
`;
server.on("connection", (socket) => {
console.log("ESTALISHED");
// console.log(socket);
socket.on("data", (chunk) => {
console.log(chunk.toString());
socket.write(message);
});
});
※ 브라우저에서 확인해봤을때
※ socket 세션이 남겨졌을때 데이터 정보를 저장해둔것
※ HTTP는 1요청에 1응답만 가능해서 socket.end()를 해주지않으면 계속 로딩이 돌게되는데 효율적이지않다.!
그래서 끝났다는것을 client가 요청을 보낼때 request 헤더에 content-length로 알려주어야한다!
→ length가 byte를 뜻하니까 length 만큼 받아지면 끝났다고 알수있게된다.
※ URL에 확장자를 적을 필요는 없다!
apache만들때는 /var/www/index.html 이렇게 디렉토리가 정해진거였기때문에 작성했던것!
※ request message에는 헤더와 바디 사이에 엔터(line-breaker)가 있다!
※ socket.end() 는 데이터를 다보내고 종료한다는 의미!
※ request message 형식
'NodeJS' 카테고리의 다른 글
NodeJS - 요청, 응답 (0) | 2023.09.09 |
---|---|
NodeJS - Response Message class문법으로 만들기 (0) | 2023.09.07 |
NodeJS - network, OSI 7계층, host, 프로토콜, port (0) | 2023.08.30 |
NodeJS - 진수 / Buffer(내장모듈) / writeStream (0) | 2023.08.29 |
NodeJS - 내장 모듈, fs, path, readFile, writeFile / 외장 모듈, NPM, dotenv (0) | 2023.08.28 |