<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
完成一個http請求的處理和響應,主要有以下幾個步驟:
完成上面幾個步驟,便能夠實現一個簡單的http伺服器,完成對基本的http請求的處理
go中net包下有提供Listen和Accept兩個方法,可以完成連線的建立,可以簡單看下範例:
func main() { // 對8080埠進行監聽 l, _ := net.Listen("tcp", ":8080") // 獲取和埠8080完成三次握手的tcp連線 conn, _ := l.Accept() // 此時便能夠使用該連線和使用者端進行通訊 data := make([]byte, 4096) // 可以從conn讀取資料緩衝區當中 conn.Read(data) // 將緩衝區的資料列印處理 print(string(data)) }
可以執行這段程式碼,然後在瀏覽器對本地8080埠傳送請求,該程式能夠讀取到瀏覽器傳送過來的http請求體資料。
當通過Accept方法獲取到連線後,能夠使用該連線和使用者端進行通訊,該連線實現了net.Conn介面,具體介面的定義如下:
type Conn interface { // Read reads data from the connection. // Read can be made to time out and return an error after a fixed // time limit; see SetDeadline and SetReadDeadline. Read(b []byte) (n int, err error) // Write writes data to the connection. // Write can be made to time out and return an error after a fixed // time limit; see SetDeadline and SetWriteDeadline. Write(b []byte) (n int, err error) // Close closes the connection. // Any blocked Read or Write operations will be unblocked and return errors. Close() error }
能夠通過呼叫Read方法從使用者端讀取資料,使用Write方法往使用者端返回資料。
當和使用者端建立連線後,同時也能夠讀取到使用者端傳送過來的請求,此時要處理http請求的話,此時是需要解析出http請求體的,然後才能對http請求進行處理。接下來我們看一下一個http請求例子:
GET /ping HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.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.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
rn(空行)
hello world
接下來對HTTP請求體來進行分析,第一行是請求行,包含請求的方法,請求URI,以及HTTP版本。下面這個例子中,請求方法是GET,請求URI是/ping,HTTP版本是1.1。
GET /ping HTTP/1.1
請求行到空行之間的內容便是請求頭部,每一個頭部欄位都有其對應的作用,比如Connection首部欄位,這裡值為keep-alive,這裡的作用是告訴伺服器,這個連線要處理多個http請求,不要處理完一個http請求就把連線斷開了。
而且一個http請求首部欄位,是可以有多個對應的值的,多個值之間用逗號隔開。
Cache-Control: public, max-age=31536000
第三部分的內容為請求體,也就是空行之後直到整個http請求的結束。可以看下面例子,請求體的內容是hello world。實際上GET請求是不應該有請求體的內容的,此處是我手動加進去的,只是為了方便展示使用。
GET /ping HTTP/1.1
....(省略http請求體部分首部欄位)
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
rn(空行)
hello world
當我們瞭解完http請求體的結構,接下來可以編寫程式碼來解析http請求體。
我們定義一個Conn結構體,由Conn完成資料的讀取和HTTP請求體的解析,Conn定義如下:
type Conn struct { rwc net.Conn // bufio.Reader 提供了快取的功能,以及一些函數,方便我們解析HTTP請求體 br *bufio.Reader bw *bufio.Writer // 是否已經寫入響應頭 wroteHaeder bool } func NewConn(rwc net.Conn) *Conn { return &Conn{ rwc: rwc, br: bufio.NewReader(rwc), bw: bufio.NewWriter(rwc), } }
同時解析出來的HTTP請求,也需要有個結構體來儲存這部分資料,Request定義如下,這裡暫時只支援GET請求,所以並沒有儲存請求體的內容
type Request struct { // 儲存請求方法,上面例子便是GET method string // 用於儲存請求的uri uri string // 用於儲存http版本 proto string // http首部欄位 Header map[string]string }
接下來由Conn完成HTTP請求體的解析,然後將解析的結果儲存到Request物件當中。只需要根據HTTP請求體結構來進行解析即可,具體邏輯如下:
func (c *Conn) readRequest() (*Request, error) { req := NewRequest() // 現在只支援Get請求,讀取第一行內容 // GET /ping HTTP1.1 line, err := c.br.ReadBytes('n') if err != nil { return req, err } var f []string // 按空格來進行分割,將請求行分割為三部分 if f = strings.Split(string(line), " "); len(f) != 3 { return req, errors.New("http Header error") } // 獲取到GET, /ping, HTTP/1.1 req.method, req.url, req.proto = f[0], f[1], f[2] // 解析請求體首部欄位 for { line, err = c.br.ReadBytes('n') if err != nil { return nil, err } // 當讀取到空行時,說明已經首部欄位已經讀取完了 if len(strings.TrimSpace(string(line))) == 0 { break } //舉例,讀取connection: keep-alive,獲取第一個空格的下標 i := bytes.IndexByte(line, ' ') if i == -1 { return nil, errors.New("header is error") } // 此時獲取到請求首部key,為connection key := string(line[:i-1]) // 讀取到對應的值,這裡讀取到keep-alive value := strings.TrimSpace(string(line[i:])) // 簡單讀取頭部欄位即可 req.Header[key] = value } }
此時已經獲取到HTTP請求了,之後需要對HTTP請求來進行處理,這裡可以先簡單進行處理,根據不同的請求執行不同的處理邏輯:
func main() { // 對8080埠進行監聽 l, _ := net.Listen("tcp", ":8080") // 獲取和埠8080完成三次握手的tcp連線 conn, _ := l.Accept() // 獲取到conn連線 c := NewConn(conn, handler) // 讀取到請求體 req, _ := c.readRequest() if request.uri == "/hello" { .... }else{ .... } }
當http請求處理完成之後,需要將返回一個處理結果返回給使用者端,有時候還需要返回一些資料給使用者端,這裡返回的資料需要符合HTTP響應體的結構,接下來我們看看HTTP響應體的結構
HTTP/1.1 200 OK
Server: CloudWAF
Date: Sun, 04 Dec 2022 02:29:27 GMT
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Content-Language: zh-CN
Strict-Transport-Security: max-age= 31536000
Content-Encoding: gzip
rn(空行)
xxxx響應資料
可以看到,HTTP響應體和請求體結構類似,當需要返回資料給使用者端時,需要按照HTTP協定定義好的響應體結構來進行返回,這樣使用者端才能夠正確解析。
為了方便使用,構造HTTP響應體結構這部分邏輯應該由Conn物件來承載,由Conn物件提供一個Write方法,當需要返回資料時,只需要呼叫Write方法寫入要返回的資料即可,不需要去操心去構造HTTP響應體的內容,Writer方法具體邏輯如下:
const ( StatusOK = 200 ) var statusText = map[int]string{ StatusOK: "OK", } // 返回響應行 // 構造響應行 HTTP/1.1 200 OK func (c *Conn) writeHeader(status int) error { if c.wroteHeader { return errors.New("code has been set") } c.wroteHeader = true var proto string //GET /hello HTTP/1.1 proto = "HTTP/1.1" // 獲取文字描述,這裡為OK text, ok := statusText[status] if !ok { text = "status code " + strconv.Itoa(status) } // 寫入資料 HTTP1.1 200 OKrn c.bw.WriteString(proto + " " + strconv.Itoa(status) + " " + text + "rn") // 寫入空行 c.bw.Write("rn") return nil } // 寫入響應資料 func (c *Conn) WriteData(data []byte) error { // 還沒寫入請求頭 if !c.wroteHeader { //預設狀態碼是200 OK c.writeHeader(StatusOK) } c.bw.Write(data) return nil }
func main() { // 對8080埠進行監聽 l, _ := net.Listen("tcp", ":8080") // 獲取和埠8080完成三次握手的tcp連線 conn, _ := l.Accept() // 獲取到conn連線 c := NewConn(conn) // 讀取到請求體 req, _ := c.readRequest() if request.uri == "hello" { c.WriteData("hello") }else{ c.WriteData("hello world") } // 在最後,需要將緩衝區的資料進行清空 c.bw.Flush() // 因為響應沒有設定content-length,所以只有連線斷開後, // 瀏覽器才知道資料讀取完成了,此處需要斷開連線 c.rwc.Close() }
到此為止,一個簡單的HTTP伺服器已經實現了,能夠實現簡單的HTTP請求的解析和響應。
以上就是Golang實現簡單http伺服器的範例詳解的詳細內容,更多關於Golang http伺服器的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45