Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발공부일지

NodeJS - net 모듈을 사용해서 TCP 구현 본문

NodeJS

NodeJS - net 모듈을 사용해서 TCP 구현

보람- 2023. 8. 31. 17:57

목차

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 형식

엔터를 두고 위에는 header, 아래를 body라고 한다.