1. Как работает сеть
Для обычного процесса доступа в Интернет система фактически делает следующее: браузер сам является клиентом.При вводе URL-адреса для запроса веб-страницы браузер сначала запрашивает у DNS-сервера получение IP-адреса, соответствующего доменному имени, а затем найти соответствующий IP-адрес через IP-адрес.После отправки сервера на сервер требуется установить TCP-соединение.После отправки браузером пакета HTTP Request (запроса) сервер начинает обрабатывать пакет-запрос после получения пакет запроса.Сервер вызывает собственный сервис и возвращает пакет HTTP Response (ответ);После получения ответа от сервера начать рендеринг тела(тела) в пакете Response, а затем разорвать соединение TCP с сервером после получение всего контента.
Вышеупомянутые методы работы в Интернете можно просто резюмировать следующим образом:
- Клиент получает сетевой IP-адрес сервера через DNS
- Клиент устанавливает TCP-соединение с сервером по протоколу TCP/IP.
- Клиент отправляет пакет запроса протокола HTTP на сервер, чтобы запросить ресурсный документ на сервере.
- Сервер отправляет клиенту ответный пакет HTTP-протокола. Если запрошенный ресурс содержит содержимое на динамическом языке, сервер вызовет механизм интерпретации динамического языка для обработки «динамического содержимого» и возврата обработанных данных клиенту.
- Клиент отключен от сервера. HTML-документ интерпретируется клиентом, и графический результат отображается на экране клиента.
Несколько концепций на стороне сервера:
- Запрос: информация, запрошенная пользователем, используемая для анализа информации о запросе пользователя, включая сообщение, получение, файл cookie, URL-адрес и другую информацию.
- Ответ: информация, которую сервер должен передать клиенту.
- Conn: ссылка пользователя на запрос
- Обработчик: логика обработки для обработки запросов и генерации возвращаемой информации.
Ниже приведен поток выполнения пакета http:
- Сервер запускает сокет, создает сокет прослушивания, прослушивает указанный порт и ожидает поступления запроса клиента.
- клиентский сервер ссылок
- Клиент отправляет запрос (http)
- Сервер получает запрос клиента и определяет, является ли он HTTP/HTTPS-запросом, и если да, то считывает заголовок HTTP/HTTPS-запроса и данные тела.
- Создание возвращаемой информации в формате HTTP/HTTPS (заголовки ответа HTTP/HTTPS, данные ответа)
- Эта информация возвращается клиенту через сокет, завершая процесс ответа на запрос.
Во-вторых, используйте язык Go для написания HTTP-сервера.
Стандартная библиотека net/http языка Go предоставляет интерфейсы, связанные с http-программированием, которые инкапсулируют сложные и тривиальные детали внутреннего соединения TCP и анализа сообщений.Пользователям нужно только взаимодействовать с двумя объектами http.request и http.ResponseWriter. То есть, пока вы пишете обработчик, запрос будет передаваться через параметры, и все, что ему нужно сделать, это обработать запрошенные данные и записать результат в Response.
WebServer.go
package main
import (
"fmt"
"log"
"net/http"
)
// HelloServer 函数实现了处理器的签名,所以这是一个处理器函数
func HelloServer(w http.ResponseWriter,r *http.Request) {
fmt.Println("path:", r.URL.Path)
fmt.Fprintf(w, "Hello Go Web")
}
func main() {
// 注册路由和路由函数,将url规则与处理器函数绑定做一个map映射存起来,并且会实现ServeHTTP方法,使处理器函数变成Handler函数
http.HandleFunc("/",HelloServer)
fmt.Println("服务器已经启动,请在浏览器地址栏中输入 http://localhost:8900")
// 启动 HTTP 服务,并监听端口号,开始监听,处理请求,返回响应
err := http.ListenAndServe(":8900", nil)
fmt.Println("监听之后")
if err != nil {
log.Fatal("ListenAndServe",err)
}
}
После выполнения описанной выше процедуры введите в браузереhttp://localhost:8900, в выводе консоли:
服务器已经启动,请在浏览器地址栏中输入 http://localhost:8900
path: /
Веб-страница, к которой обращается браузер, показывает: Hello Go Web
1. Укажите несколько маршрутов для HTTP-сервера
В реальной разработке интерфейс HTTP будет иметь много URL-адресов и соответствующих обработчиков. Здесь используется ServeMux сети/http. Mux — это аббревиатура от мультиплексора, что означает мультиплексирование (запросы отправляются поверх, и по некоторым суждениям они распределяются по разным местам в бэкенде). ServeMux может регистрировать больше соответствий между URL-адресами и обработчиками и автоматически перенаправлять запросы соответствующим обработчикам для обработки.
Язык Go реализует веб-маршрутизацию в основном для выполнения трех задач:
- порт прослушивания
- Получать запросы клиентов
- Назначьте соответствующий обработчик для каждого запроса
Вот пример реализации простого http-маршрута:
WebServerRoute.go
package main
import (
"fmt"
"log"
"net/http"
)
// 首页处理器
func HomeHandler (w http.ResponseWriter, r *http.Request) {
fmt.Println("path:", r.URL.Path)
fmt.Fprintf(w, "Welcome to home")
}
// 注册页处理器
func registerHandler (w http.ResponseWriter, r *http.Request) {
fmt.Println("path:", r.URL.Path)
fmt.Fprintf(w, "Welcome to register")
}
// 登录页处理器
func loginHandler (w http.ResponseWriter, r *http.Request) {
fmt.Println("path:", r.URL.Path)
fmt.Fprintf(w, "Welcome to login")
}
func main() {
// 路由:/home -- 首页
http.HandleFunc("/home",loginHandler)
// 路由:/register -- 注册页
http.HandleFunc("/register",registerHandler)
// 路由:/login -- 登录页
http.HandleFunc("/login",loginHandler)
fmt.Println("服务器已经启动,访问:\n首页地址:http://localhost:8900/home\n注册页地址:http://localhost:8900/register\n登录页地址:http://localhost:8900/login")
err := http.ListenAndServe(":8900", nil)
fmt.Println("监听之后")
if err != nil {
log.Fatal("ListenAndServe",err)
}
}
После выполнения описанной выше процедуры введите до и после браузера
http://localhost:8900/home
http://localhost:8900/register
http://localhost:8900/login
Вывод в консоли:
服务器已经启动,访问:
首页地址:http://localhost:8900/home
注册页地址:http://localhost:8900/register
登录页地址:http://localhost:8900/login
path: /home
path: /register
path: /login
Веб-страницы, к которым обращается браузер, отображаются до и после:
Welcome to home
Welcome to register
Welcome to login
2. Получить информацию заголовка HTTP-запроса
В следующем примере получаются заголовки HTTP-запроса: Path, Host, Method(Get, post), Proto, UserAgent и т. д.
RequestInfo.go
package main
import (
"fmt"
"log"
"net/http"
)
// HelloServer2 函数实现了处理器的签名,所以这是一个处理器函数
func HelloServer2(w http.ResponseWriter,r *http.Request) {
fmt.Println("path:", r.URL.Path)
fmt.Println("Url:",r.URL)
fmt.Println("Host:",r.Host)
fmt.Println("Header:",r.Header)
fmt.Println("Method:",r.Method)
fmt.Println("Proto:",r.Proto)
fmt.Println("UserAgent:",r.UserAgent())
fmt.Fprintf(w, "Hello Go Web")
}
func main() {
// 注册路由和路由函数,将url规则与处理器函数绑定做一个map映射存起来,并且会实现ServeHTTP方法,使处理器函数变成Handler函数
http.HandleFunc("/",HelloServer2)
fmt.Println("服务器已经启动,请在浏览器地址栏中输入 http://localhost:8900/a/b")
// 启动 HTTP 服务,并监听端口号,开始监听,处理请求,返回响应
err := http.ListenAndServe(":8900", nil)
fmt.Println("监听之后")
if err != nil {
log.Fatal("ListenAndServe",err)
}
}
После выполнения описанной выше процедуры введите в браузереhttp://localhost:8900/a/b, выводим в консоль:
服务器已经启动,请在浏览器地址栏中输入 http://localhost:8900/a/b
path: /a/b
Url: /a/b
Host: localhost:8900
Header: map[Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3] Accept-Encoding:[gzip, deflate, br] Cookie:[UM_distinctid=1682d397d27566-07ba4cbda8d037-2d604637-4a640-1682d397d2a745; _ga=GA1.1.301532435.1546946971; Hm_lvt_ac60c3773958d997a64b55feababb4a1=1547204629] Upgrade-Insecure-Requests:[1] User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36] Accept-Language:[en,zh-CN;q=0.9,zh;q=0.8] Connection:[keep-alive] Cache-Control:[max-age=0]]
Method: GET
Proto: HTTP/1.1
UserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Веб-страница, к которой обращается браузер, показывает: Hello Go Web
3. Получите полный путь запроса
Полный путь запроса состоит из схемы (http/https) + доменного имени или IP (localhost) + номера порта + пути, например:
FullRequestPath.go
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
/*
获取完整的请求路径
http://localhost:8900/a/b/x.html
1. scheme: http/https
2. 域名或IP: localhost
3. 端口号: 8900
4. Path: /a/b/x.html
*/
// HelloServer 函数实现了处理器的签名,所以这是一个处理器函数
func HelloServer3(w http.ResponseWriter,r *http.Request) {
scheme := "http://"
if r.TLS != nil {
scheme = "https://"
}
fmt.Println("scheme:", scheme)
fmt.Println("域名(IP)和端口号:", r.Host)
fmt.Println("Path:", r.RequestURI)
fmt.Println("完整的请求路径:", strings.Join([]string{scheme,r.Host,r.RequestURI},""))
fmt.Fprintf(w, "Hello Go Web")
}
func main() {
// 注册路由和路由函数,将url规则与处理器函数绑定做一个map映射存起来,并且会实现ServeHTTP方法,使处理器函数变成Handler函数
http.HandleFunc("/",HelloServer3)
fmt.Println("服务器已经启动,请在浏览器地址栏中输入 http://localhost:8900/a/b/x.html")
// 启动 HTTP 服务,并监听端口号,开始监听,处理请求,返回响应
err := http.ListenAndServe(":8900", nil)
fmt.Println("监听之后")
if err != nil {
log.Fatal("ListenAndServe",err)
}
}
После выполнения описанной выше процедуры введите в браузереhttp://localhost:8900/a/b/x.html, выводим в консоль:
服务器已经启动,请在浏览器地址栏中输入 http://localhost:8900/a/b/x.html
scheme: http://
域名(IP)和端口号: localhost:8900
Path: /a/b/x.html
完整的请求路径: http://localhost:8900/a/b/x.html
Веб-страница, к которой обращается браузер, показывает: Hello Go Web
4. Напишите HTTPS-сервер
HTTP-сервер отличается от HTTPS-сервера, HTTP-протокол — это открытый текст, HTTPS-протокол (HTTP через SSL или HTTP через TLS) — это зашифрованный текст.
Создайте сертификат SSL вручную, используя метод openssl:
Команда создания ключевого файла:
openssl genrsa -out server.key 2048
Объедините сгенерированный файл ключа с командой сгенерированного файла csr сертификата:
openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
В процессе создания файла csr вам будет предложено ввести информацию в поле, требуемую сертификатом, включая страну (CN в Китае), провинцию, город, название подразделения, название отдела подразделения (вы можете оставить это поле пустым и нажать Войти). Обратите внимание: за исключением национальной аббревиатуры, которая должна быть заполнена на CN, остальные могут быть на английском или китайском языке.
Код файла HttpsServer.go выглядит следующим образом:
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
/*
编写 HTTPS 服务器
HTTPS = HTTP + Secure(安全)
RSA 进行加密
SHA 进行验证
密钥和证书
生成密钥文件
openssl genrsa -out server.key 2048
生成证书文件
openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
*/
func httpsServer(w http.ResponseWriter, r *http.Request) {
fmt.Println("path:", r.URL.Path)
fmt.Println("Url:",r.URL)
fmt.Println("Host:",r.Host)
fmt.Println("Header:",r.Header)
fmt.Println("Method:",r.Method)
fmt.Println("Proto:",r.Proto)
fmt.Println("UserAgent:",r.UserAgent())
scheme := "http://"
if r.TLS != nil {
scheme = "https://"
}
fmt.Println("完整的请求路径:", strings.Join([]string{scheme,r.Host,r.RequestURI},""))
fmt.Fprintf(w, "Hello Go Web")
}
func main() {
http.HandleFunc("/",httpsServer)
fmt.Println("HTTPS 服务器已经启动,请在浏览器地址栏中输入 https://localhost:4321/")
err := http.ListenAndServeTLS(":4321","/Users/play/goweb/src/basic/server.crt","/Users/play/goweb/src/basic/server.key",nil)
if err != nil {
log.Fatal("ListenAndServe",err)
}
}
После выполнения описанной выше процедуры введите в браузереhttps://localhost:4321/, выводим в консоль:
HTTPS 服务器已经启动,请在浏览器地址栏中输入 https://localhost:4321/
2019/07/06 19:35:55 http: TLS handshake error from [::1]:58945: remote error: tls: unknown certificate
path: /
Url: /
Host: localhost:4321
Header: map[Cache-Control:[max-age=0] Upgrade-Insecure-Requests:[1] User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36] Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3] Accept-Encoding:[gzip, deflate, br] Accept-Language:[en,zh-CN;q=0.9,zh;q=0.8] Cookie:[UM_distinctid=1682d397d27566-07ba4cbda8d037-2d604637-4a640-1682d397d2a745; _ga=GA1.1.301532435.1546946971; Hm_lvt_ac60c3773958d997a64b55feababb4a1=1547204629]]
Method: GET
Proto: HTTP/2.0
UserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
完整的请求路径: https://localhost:4321/
Веб-страница, к которой обращается браузер, отображает: Hello Go Web.
Поскольку примененный выше SSL-сертификат не является сертифицированным, он предложит «неизвестный сертификат», а несертифицированный сертификат можно использовать только для разработки и тестирования.