1. 回顾
我们之前已经实现了一个简单的HTTP服务器,能够对简单的HTTP请求进行处理,具体逻辑如下:
func main() {
// 对8080端口进行监听
l, _ := net.Listen("tcp", ":8080")
// 获取和端口8080完成三次握手的tcp连接
for {
conn, _ := l.Accept()
// 获取到conn连接
c := NewConn(conn)
// 读取到请求体
req, _ := c.readRequest()
if req.uri == "/hello" {
//这里应该没有设置content-length头部字段,所以浏览器需要连接断开后,才知道数据已经读取完成了
c.WriteData([]byte("hello"))
} else {
c.WriteData([]byte("hello world"))
}
c.bw.Flush()
c.rwc.Close()
}
}
那如果我们想要重新实现一个HTTP服务器,是不是要重复实现上面的代码,完成HTTP请求的解析,处理和对请求的响应,这样是不是太麻烦了。
2. 实现可复用HTTP服务器
通过观察上面代码,我们可以发现,除了不同的请求有不同的处理逻辑外,其他所有的逻辑都是完全相同的,不同的代码逻辑如下:
if req.uri == "/hello" {
//这里应该没有设置content-length头部字段,所以浏览器需要连接断开后,才知道数据已经读取完成了
c.WriteData([]byte("hello"))
} else {
c.WriteData([]byte("hello world"))
}
那么针对此处不同,我们可以抽取出一个接口,让不同的服务器传入该接口不同的实现,从而方便实现不同的HTTP服务器。
我们观察上面具体的处理函数,需要根据Request中不同的uri来执行不同的处理逻辑,以及需要Conn来写入响应数据。根据该依据,接口定义如下:
type Handler interface {
ServeHttp(conn *Conn, request *Request)
}
然后我们再提供一个构造HTTP服务器的函数,当需要创建一个新的HTTP服务器,只需要调用该函数,传入对应的处理函数即可。函数定义如下:
func ListenAndServe(addr string, handler Handler){
// addr: 指定监听的端口
// handler: HTTP服务器具体的处理函数
}
该函数的具体实现如下,完成了连接的建立,HTTP请求的解析以及响应体的构造,具体的业务处理逻辑则调用外部Handler接口的实现
func ListenAndServe(addr string, handler Handler){
// 对8080端口进行监听
l, _ := net.Listen("tcp", addr)
// 获取和端口8080完成三次握手的tcp连接
for {
conn, _ := l.Accept()
// 获取到conn连接
c := NewConn(conn)
// 读取到请求体
req, _ := c.readRequest()
// 执行具体的处理逻辑
handler.ServeHttp(conn, req)
// 当请求处理完成之后,需要将数据全部返回给客户端
c.bw.Flush()
c.rwc.Close()
}
}
当我们要实现一个简单HTTP服务器时,我们只需要实现Handler接口,然后调用ListenAndServe函数,就能够启动一个HTTP服务了。
3. 具体示例
- 实现Handler接口,这里实现了http请求具体的处理函数
type SimpleWeb struct {
}
func (s *SimpleWeb) ServeHttp(c *Conn, req *Request) {
if req.uri == "/hello" {
//这里应该没有设置content-length头部字段,所以浏览器需要连接断开后,才知道数据已经读取完成了
c.WriteData([]byte("hello"))
} else {
c.WriteData([]byte("hello world"))
}
}
- 启动HTTP服务器
func main() {
ListenAndServe(":8080", &SimpleWeb{})
}
- 这样一个简单的HTTP服务器就已经完成了,并且成功启动