개발 노트

http tcp 미완.2 본문

nodejs

http tcp 미완.2

Meter216 2022. 12. 9. 19:17

 

HTTP 동작방식

3way handshake 이후 브라우저와 서버의 통신


브라우저에서 URI를 입력시 동작 형태 > 브라우저에서 uri를 입력하면  > 3wayhandshake가 한 번 일어나고 
http 프로토콜이 일어남.

 

브라우저에서 request message를 보내면  서버에서 알맞는 response message를 던져줘야 한다. 그것이 서버의 역할이다.

(client > server : request 요청을 함 // 이 때 서버는 무조건 response를 준다. 데이터가 없으면 없다라고 쥐어짜서 준다.)

 

요청 응답은 크게 2가지 형태로 읽어야 한다. 빈칸을 기준으로 
head부분 body부분으로 나눠서읽는다.
request header + request body

 

request message


Start line


GET /user?name=hellow HTTP/1.1 //POST를 쓰면 그냥 POST로 입력이 되는것
[요청메서드] [요청 URI] [HTTP 프로토콜 버전]

요청 메서드(Request Method)
    GET : 데이터 요청의 의미로 사용합니다.
    POST : 데이터 입력의 의미로 사용.
    PUT : 데이터 수정의 의미
    DELETE : 데이터 삭제하기 위해서 사용
    OPTIONS : 웹 서버가 지원하는 메서드의 종류 요청

요청 URI(PATH)

    HOST를 제외한 나머지 URI를 적음.
    
HTTP 프로토콜 버전
    HTTP버전이 1.0 1.1 2.0이 있는데 우리는 1.1만 쓴다.

content-Type은 알아야한다.(x-www-form-urlencoded, json)

 

body
요청 데이터를 넣는 공간

 

res message

HTTP/1.1 200 Ok
Data : ....
Content-Length: 6821
Content-Type: text/html

<html>
...내용
</html>

start line


http 프로토콜 버전
    요청메세지와 같음


네트워크 상태코드
    1xx 
    2xx : 대부분 성공의 의미 get > 200 post > 300
    3xx :
    4xx : 페이지가 없음, 오류
    5xx : 서버터짐


ok 상태 메세지


server.js

 

const net = require("net")
const resFn = require("./lib/res")
const reqFn = require("./lib/req")
const PORT = process.env.SERVER_POST || 3000
const HOST = process.env.SERVER_HOST || "127.0.0.1"

const server = net.createServer((client)=>{
    client.setEncoding("utf8")

    client.on("data", (chunk)=>{
        console.log(chunk)
        const res = resFn(client)
        const req = reqFn(chunk)

        if(req.method === "GET" && req.path === '/'){
            res.send('<h1>응답~~~</h1>')
        } else if (req.method === "GET" && req.path === "/list"){
            res.sendFile('list.html')
        } else if (req.method === "GET" && req.path === "/write"){
            res.sendFile('write.html')
        } else if (req.method === "GET" && req.path === "/update"){
            res.sendFile('update.html')
        } else if (req.method === "GET" && req.path === "/view") {
            res.sendFile('view.html')
        }
    })
})

server.on("connection", ()=>{
    console.log("connected")
})

server.listen(PORT,HOST,()=>{
    console.log("server start")
})

브라우저의 경우 url을 쳤을 때 데이터를 서버에 준다 / > 이후 서버에서 브라우저로 응답을 해 준 후

브라우저가 더 이상 받을 데이터가 없으면 엔드를 해버리고 연결을 끊는다.

 

서버가 브라우저에게 던지는 것 > reponse

브라우저가 서버에게 던지는 것 > request

 

 


res.js

 

const fileRead = require('./template')

const message = (content)=>{
    const body = Buffer.from(content)

    return `HTTP/1.1 200 OK
Connection:Close
Content-type:text/html; charset=UTF-8
Contetn-Length:${body.length}

${body.toString()}`
}

module.exports = (client) => {
    return {
        send:(body)=>{ 
            const response = message(body)
            client.write(response)
        },
        sendFile:(filename)=>{
            const body = fileRead(filename) // string html에 있는 모든 내용
            const response = message(body)
            client.write(response)
        }
    }
}

Buffer > 2진수 데이터이다 -> 16진수로 담아서 보여준다

이 16진수 데이터를 보여주기 위해선 다시 변경을 해줘야한다.

문자집합(아스키코드, 유니코드(한글일경우)) utf8(유니코드)

 

buffer로 변환되어있는 파일이기 때문에 인코딩을 해서 다시 변환을 시켜줘야 한다. 

data.toString() > 데이터라는 버퍼 안에서 인코딩을 하는 것 . ()안에 인자값을 넣어주어야한다. 

hex = 스트링으로 다 붙여서 붙여준다.

urf8 = 원래 글자로 바꿔준다.

setEncoding("") 인자값을 넣어주면 기본적으로 인코딩을 해서 받는다. 

 

fireRead를 통해 template에 작성해놓은 경로를 지정 할 수 있는 함수를 불러온다.

filename 이 속한곳 까지의 경로를 붙이고 fileRead를 통해 body에 넣을 수 있는 상태로 만들어 준 후 client.write을 통해 보내준다.

 


 

req.js

 

    // string --> object
    // TODO : startline
    // TODO : header, body 분리하기
    // TODO : header, body 객체 만들기

const getQuery = (queryString)=>{
    if(queryString === undefined) return null
    return queryString.split("&").map(v=>v.split("=")).reduce((acc, value)=>{
        const [key, val] = value
        acc[key] = val
        return acc
    },{})
}

const bodyparser = (body, type)=>{
    if (type === undefined) return null
    if (type.indexOf('application/json') !== -1) return JSON.parse(body)
    if (type.indexOf("application/x-www-form-urlencoded") !== -1) return getQuery(body)
    return body
}

const getMessage = (line) => {
    let flag = false
    let body = ''
    for (const key in line){
        if (flag) body = line.splice(key).map(v=>v.trim()).join("")
        if(line[key] === "") flag = true
    }
    line.pop()

    const headers = line.map(v=>v.split(":")).reduce((acc,value)=>{
        const [key, val] = value
        acc[key] = val
        return acc
    },{})

    body = bodyparser(body, headers["Content-Type"]) // {}
    return [headers, body]
}

const parser = (message)=>{
    const header = message.split("\r\n") // 한줄한줄 배열로 만들기
    const [method, url, version] = header.shift().split(' ') // 맨 위에 줄 한줄 빼는것
    const [path, queryString] = url.split("?") 
    const query = getQuery(queryString)

    const [headers, body] = getMessage(header)

    return {method, url, version, path, queryString, query, headers, body}
}

console.log(parser(msg))

module.exports = parser

parser 부분이 메인이다. parser함수를 넘겨줌으로 인해 req메세지를 해석 할 수 있고, 객체로 저장하여

method path 값 등을 뽑아 올 수 있다.


template.js

const fs = require("fs")
const path = require("path")
const DEFAULT_DIR = "../views"

module.exports = (filename) => {
    const target = path.join(__dirname, DEFAULT_DIR, filename)
    const readline = fs.readFileSync(target, "utf8")
    return readline
}

 

 

 

'nodejs' 카테고리의 다른 글

nodejs 게시판  (0) 2022.12.20
Express  (1) 2022.12.14
nodejs [3way handshake]  (0) 2022.12.07
nodejs [내장객체,케싱,내장모듈,osi7계층]  (0) 2022.12.06
nodejs  (0) 2022.12.05