네트워크 서버는 어떻게 작동합니까?

네트워크 서버
그것들은 신비한 것들이야..프로젝트를 초기화하기만 하면 나머지는 처리됩니다.그런데 엔진 뚜껑 아래에서 도대체 무슨 일이 일어났을까?
궁금해서 내 친구에게 그걸 아느냐고 물었다. 그 결과 그는 재미를 위해 책을 읽는 사람이었다HTTP paper. 그래서 우리는 나의 흐름을 사용해서 Go에 우리의 웹 서버를 구축하기 시작했다.
서버가 미키마우스를 주제로 한다고 말씀드렸나요?

하이퍼텍스트 전송 프로토콜


HTTP는 '하이퍼텍스트 전송 프로토콜' 을 대표한다. 90년대 초 웹 프로젝트의 일부였다.

Tim Berners-Lee, a British scientist, invented the World Wide Web (WWW) in 1989, while working at CERN. The web was originally conceived and developed to meet the demand for automated information-sharing between scientists in universities and institutes around the world. - CERN


이 프로젝트는 이미 인터넷 작업의 기초가 되었다. 그것은 어떻게 서버 간에 데이터와 정보를 교류할 수 있는지에 대한 기대를 개술했다.유니버설 네트워크 프로젝트의 작업으로 인해, 당신의 컴퓨터는 사이트를 어떻게 해석하는지 안다

우리 뭐 했지?


우리는 '처음부터' 웹 서버를 두 번 교체하기로 결정했습니다. 첫 번째는 본고에서 말한 바와 같이 하드 인코딩 단점에 대한 get 요청을 처리합니다.다른 블로그에서, 나는 더욱 동적으로 실현되는 단점을 처리할 수 있는 웹 서버를 구축하는 방법을 소개할 것이다.p>

서버는 어떻게 작동합니까?


주요했어


듣다



기본 차원에서 웹 서버listens:



port, err := net.Listen("tcp", ":1928")

우리는 Listener 대상port을 만들었습니다. 이 대상은 특정한 데이터 포트/통신 단점을 감청하여 특정network protocol의 전송 요청을 받습니다.이 코드는 기본적으로 서버를 시작합니다. 전송된 요청을 받을 수 있습니다.이 예에서, 우리는 포트 1928에 TCP 스타일의 요청이 나타날 것을 기대한다.(미키마우스는 1928년에 만들어졌기 때문에 1928포트를 듣기로 선택했습니다!)


너무 좋아요!우리 망했어!그렇지


는 완전히



우리는 지금 요청을 받을 수 있지만, 요청을 어떻게 처리하고 읽는지, 응답을 어떻게 보내는지


받아들이다



conn, err := port.Accept()

port에서 실현된 generic functions 함수 중 하나는 Accept() 함수이다.Accept 새로운 전송 요청인 전송 연결이 Conn의 대상으로 되돌아올 때까지 일시 정지/막힘.서버가 실행될 때 이 함수는 계속 나타나야 합니다. 이것은 코드에 무한port에 놓여 있음을 의미합니다


네, 잘 읽었어요. 무한순환을 장려하는 시대예요


처리하다.



go handleConnection(conn)

우리는 새로운 사용자 정의 함수를 만들었습니다. for loop 이 함수는 사실상 전송된 연결에서 데이터를 보고 해석하고 읽기 시작하는 곳입니다.매번 다른 클라이언트로부터 전송된 연결(즉 우리가 서버로서의 어떠한 외부 연결)이 있을 때마다 우리는 그것을 받아들이고 새로운handle Connection을 파생시켜 이 클라이언트를 처리할 것이다


키워드handleConnection를 사용하여 파생goroutines을 사용합니다. 이것은 서버에 사용multithreading asynchronisity됩니다.만약 우리가 goroutines 을 사용하여 병발성을 사용하지 않는다면, 이 라인의 전송 요청을 막으면, 이 라인의 모든 내용을 막기 때문에, 같은 많은 전송 요청을 처리할 수 없습니다.p>

주 기능 코드



func main() {
    fmt.Println("Goofy: hyuck - booting up!")

    port, err := net.Listen("tcp", ":1928")

    if err != nil { //failed to set up server
        fmt.Println("Mickey: Oh no Goofy! It looks like there was an error starting up the server! ")
        fmt.Println(err.Error())
        return
    }

    for {
        conn, err := port.Accept() 

        if err != nil { //client failed to connect with server
            fmt.Println(err.Error())
            return
        }

        go handleConnection(conn)
        fmt.Println("Welcome to the Mickey Mouse Web House!")
    }
}


두 번 인쇄



fmt.Println("Welcome to the Mickey Mouse Web House!")

에서 상기 코드를 실행할 때 이 인쇄는 두 번 나타날 수 있습니다.브라우저가 서버에서 요청을 실행하면 페이지의 go와 페이지의 내용에 대해 별도의 요청을 수행합니다


handleConnection()


HTTP 요청 메시지 분석


HTTP의 규범에 따라 필요한 정보를 특정한 순서로 전달하여 수신자가 정확하게 소화할 수 있도록 해야 합니다



GET / HTTP/1.1\r\n
Content-Type: text/plain; charset=UTF-8\r\n
Content-Length: <length>\r\n
\r\n
Hello World!\n

Note: each one of these lines end with \r\n - when you're reading the incoming request, this allows the buffer to know where each line of the protocol ends. Basically, it helps separate new lines.



GET / HTTP/1.1\r\n

당신은 표면적으로 볼 수 있습니다.이것은 서버가 전송한 요청 유형, 요청 목표 (도달하려는 단점), HTTP 버전


HTTP 메시지에 대한 분석을 더 읽으려면 Mozilla팀에서 제공한 this blog post를 보십시오


👋 I want to point out Content-Type header - up until I started working on this project, it didn't hit home as to why setting and checking Content-Type in HTTP requests was important. But now, I understand that each HTTP header acts almost like a basic if-else check point. Without setting the Content-Type specifically, the interpreting server won't know what to do or how to process the contents of the request. Processing each kind of request is basically hard coded.

Basically, always check your Content-Type header if you're sending a request, and if you're processing/receiving a request, be sure to specify the expected Content-Type in your documentation to reduce frustration on both ends!


HTTP 응답 메시지 분석


HTTP 응답 메시지는 다음과 같습니다:



HTTP/1.1 200 OK\r\n
Content-Type: text/plain; charset=UTF-8\r\n
Content-Length: <length>\r\n
\r\n
Hello World!\n

그것을 분해합시다






HTTP/1.1 200 OK\r\n

이 줄은 HTTP 버전, 상태 코드 및 상태 코드와 관련된 텍스트를 사용하고 있는지 알려 줍니다



Content-Type: text/plain; charset=UTF-8\r\n

이 헤더는 클라이언트가 전송한 내용의 유형을 알려 줍니다. 어떻게 처리하는지 알 수 있도록 합니다



Content-Length: <length> \r\n
\r\n

내용 길이의 값은 수신자가 메시지를 받는 전체 시간을 알 수 있도록 도와주기 때문에 매우 중요하다.더블 favicon 로 끝납니다. 두 번째는 빈 줄입니다. 메시지의 시작을 기록하기 위해 프로세서가 사용하도록 요청합니다.br/>



Hello World!\n

메일 본문입니다


수신 요청 읽기






request, err := bufio.NewReader(connection).ReadString('\n')

우리는 전송 연결을 기반으로 하는 bufio 대상을 사용하여 전송 요청에서 정보를 읽습니다.서버가 가능한 한 간단하게 하기 위해서, 우리는 심지어 어떤 종류를 입력해야 하는지 request 검사하지 않고, 요청한 노드만 검사할 계획이다.이렇게 하면 HTTP 요청의 첫 줄만 읽을 수 있고 요청의 나머지 부분에서 무슨 말을 했는지 신경 쓸 필요가 없습니다



requestParts := strings.Split(request, " ")

우리가 받은 요청을 몇 부분으로 나누어 검사합니다



if requestParts[1] == "/clubhouse" {

에서 요청하는 노드가 있습니다.이 서버에 대해 우리는 단지 하나의 단점만 받아들인다: \r\n



message := "if goofy has a dog, and goofy is a dog....????"

connection.Write([]byte("HTTP/1.1 200 OK\r\n"))
connection.Write([]byte("Content-Type: text/plain; charset=UTF-8\r\n"))
connection.Write([]byte("Content-Length: " + strconv.Itoa(len(message)) + "\r\n\r\n"))
connection.Write([]byte(message + "\n"))
return

응답을 보내려면 connection objectclubhouse 함수를 사용하지만 보내기 전에 필요한 헤더를 보내야 합니다(상기 설명과 하이퍼텍스트 전송 프로토콜에서 설명한 바와 같습니다)


들어오는 요청을 찾지 못하면Write404 오류로 돌아갑니다



connection.Write([]byte("HTTP/1.1 404 Not Found\r\n"))
connection.Write([]byte("Content-Type: text/plain; charset=UTF-8\r\n"))
connection.Write([]byte("Content-Length: 0\r\n\r\n"))

우리가 이 방법을 완성하기 전에 발생한 마지막 일은 연결을 닫아야 한다는 것이다



defer connection.Close() 

실제로 이것은 우리가 /clubhouse 함수에서 하는 첫 번째 일이다. 만약 당신이 초보라면 handleConnection는 주 함수가 완성되었을 때 실행해야 할 함수를 설명하는 키워드이다. 그것이 어디에서 끝나든지


handleConnection() 코드



func handleConnection(connection net.Conn) {
    defer connection.Close()
    request, err := bufio.NewReader(connection).ReadString('\n')

    if err != nil {
        fmt.Println(err.Error())
        return
    }

    requestParts := strings.Split(request, " ")

    if requestParts[1] == "/clubhouse" {
        message := "If Goofy has a dog, and Goofy is a dog....????"

        connection.Write([]byte("HTTP/1.1 200 OK\r\n"))
        connection.Write([]byte("Content-Type: text/plain; charset=UTF-8\r\n"))
        connection.Write([]byte("Content-Length: " + strconv.Itoa(len(message)) + "\r\n\r\n"))
        connection.Write([]byte(message + "\n"))
        return
    }

    connection.Write([]byte("HTTP/1.1 404 Not Found\r\n"))
    connection.Write([]byte("Content-Type: text/plain; charset=UTF-8\r\n"))
    connection.Write([]byte("Content-Length: 0\r\n\r\n"))
}




Final code on Github.


서비스 엔드포인트


나는 서버가 사실상 문자열을 읽고 해석하는 것일 뿐이라는 것을 놀랐다.그들과 여러 해 동안 협력한 후에 나는 정말 서버가 훨씬 복잡하다고 생각했다. 이런 간단한 예는 훨씬 복잡할 것이다.p>

현재 우리 서버는 하드코딩 응답이 있는 단점 defer 하나만 처리할 수 있습니다.그러면 동적 실현의 단점은?서버 코드에 직접 정의되지 않은 것들은 걱정하지 마세요. 저희도 있습니다.블로그 게시물이 곧 게시됩니다


참고로 New Relic이 서버와 함께 작동하는지 궁금하시면...네.Click here to learn more .


저와 함께 이 프로젝트에 참여해 주셔서 감사합니다!한 스승이 나를 도와 이 과정을 완성하게 하였는데, 이것은 그것을 매우 쉽게 소화시켰다.고맙습니다


이 공부를 직접 보셨으면 좋겠어요?나는 거의 매일 Twitch에서 인코딩과 다른 재미있는 과학 기술 내용을 재생한다.Come hang out! 안녕히 계세요


미키마우스


네...비록 이것은 미키마우스로 어떤 비교도 하지 않았지만 나는 줄곧 나의 미키마우스의 인상을 연구해 왔다. 조나슨과 나의 유튜브가 이 프로젝트를 방송하는 과정에서 나는 줄곧 나의 인상을 만들어 왔기 때문에 미키마우스를 사용했다

좋은 웹페이지 즐겨찾기