到了新单位总算有点闲了,接着倒腾HTTP。之前的文章可以参考:《基于TCP套接字,通过Golang模拟HTTP请求》和《基于TCP套接字,通过Golang模拟HTTP请求(续)》。
之前都是研究的客户端,现在来研究一下服务端。《HTTP权威指南》上面有一个非常简单的用perl
开发的一个服务器,我就用大Golang
照着写一个。HTTP协议是基于传输层的TCP协议,监听80端口。简单来写(复杂的我也不会),就是最简单的TCP监听,接收消息,处理并返回。我是参考的《Go socket编程实践: TCP服务器和客户端实现》。主逻辑就是这样:
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "", *portFlag))
defer ln.Close()
if err != nil {
panic(err)
}
log.Printf("<<<Server Accepting on Port %d>>>\n\n", *portFlag)
for {
conn, err := ln.Accept()
if err != nil {
log.Panicln(err)
}
go handleConnection(conn)
}
我做东西喜欢从最简单开始,一个是因为我会的不多,一个是简单的来自己能理明白,以后维护其实更省事。
回归正题,这块其实从念书的时候就老写,分分钟搞定。然后就是HTTP协议的东西。当然,还是按最简单的来。HTTP分为三部分,起始行(start line)、首部(header)和主体(body)。
起始行只有一行,就是协议版本和状态码这些,以CRLF
结尾,也就是\r\n
;接着就是头,头里面的内容就是键值对,每组键值对以CRLF
结尾;头和主体中间,还需要多一个CRLF
,我猜是为了解析内容方便。主体之后不需要CRLF
。
下图就是详细的请求格式。其中SP
是空格。
一些必要的头(因为我发现如果没有,请求会失败),就是Content-Type
和Content-length
。Content-length就是Body的长度。
func handleConnection(conn net.Conn) {
defer conn.Close()
log.Printf("[%s]<<<Request From %s>>>\n", time.Now().String(), conn.RemoteAddr())
serve_time := time.Now()
buffers := bytes.Buffer{}
buffers.WriteString("HTTP/1.1 200 OK\r\n")
buffers.WriteString("Server: Cyeam\r\n")
buffers.WriteString("Date: " + serve_time.Format(time.RFC1123) + "\r\n")
buffers.WriteString("Content-Type: text/html; charset=utf-8\r\n")
buffers.WriteString("Content-length:" + fmt.Sprintf("%d", len(DEFAULT_HTML)) + "\r\n")
buffers.WriteString("\r\n")
buffers.WriteString(DEFAULT_HTML)
conn.Write(buffers.Bytes())
}
最后补充一点,连接记得关闭
参考文献
- 【1】《HTTP权威指南》
- 【2】James F.Kurose, Keith W.Ross.COMPUTER NETWORKING. A Top-Down Approach Featuring the Internet(Third Edition).
- 【3】NETWORKING INFO BLOG. HTTP Message Format.
原文链接:最简单的HTTP SERVER,转载请注明来源!
有疑问加站长微信联系(非本文作者)