对于普通的上网过程,系统其实是这样做的:浏览器本身是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP,然后通过IP地址找到IP对应的服务器后,要求建立TCP连接,等浏览器发送完HTTP Request(请求)包后,服务器接收到请求包之后才开始处理请求包,服务器调用自身服务,返回HTTP Response(响应)包;客户端收到来自服务器的响应后开始渲染这个Response包里的主体(body),等收到全部的内容随后断开与该服务器之间的TCP连接。
URL解析格式:
scheme://host[:port#]/path/…/[?query-string][#anchor]
scheme 指定底层使用的协议(例如:http, https, ftp)
host HTTP服务器的IP地址或者域名
port# HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/
path 访问资源的路径
query-string 发送给http服务器的数据
anchor 锚
DNS解析过程
通过上面的步骤,我们最后获取的是IP地址,也就是浏览器最后发起请求的时候是基于IP来和服务器做信息交互的。
Request包分为3部分,第一部分叫Request line(请求行), 第二部分叫Request header(请求头),第三部分是body(主体)。header和body之间有个空行,请求包的例子所示:
GET /domains/example/ HTTP/1.1 //请求行: 请求方法 请求URI HTTP协议/协议版本
Host:www.iana.org //服务端的主机名
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //浏览器信息
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 //客户端能接收的MIME
Accept-Encoding:gzip,deflate,sdch //是否支持流压缩
Accept-Charset:UTF-8,*;q=0.5 //客户端字符编码集
//空行,用于分割请求头和消息体
//消息体,请求资源参数,例如POST传递的参数
HTTP协议定义了很多与服务器交互的请求方法,最基本的有4种,分别是GET,POST,PUT,DELETE。一个URL地址用于描述一个网络上的资源,而HTTP中的GET, POST, PUT, DELETE就对应着对这个资源的查,增,改,删4个操作。我们最常见的就是GET和POST了。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。
HTTP/1.1 200 OK //状态行
Server: nginx/1.0.8 //服务器使用的WEB软件名及版本
Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //发送时间
Content-Type: text/html //服务器发送信息的类型
Transfer-Encoding: chunked //表示发送HTTP包是分段发的
Connection: keep-alive //保持连接状态
Content-Length: 90 //主体内容长度
//空行 用来分割消息头和主体
!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”… //消息体
package main
import (
"fmt"
"net/http"
"strings"
"log"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
}
func main() {
http.HandleFunc("/", sayhelloName) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
http.HandleFunc函数
使用HandleFunc函数是http封装好的一个函数,可以直接使用,第一个参数是web请求路径,第二个参数是的func(writer http.ResponseWriter, request *http.Request)函数。
再使用http.ListenAndServe(":9090",nil)语句,监听9090端口,运行程序后。
使用http://localhost:9090,便会输出Hello astaxie!
其中http.ResponseWriter代表对客户端的响应体,而http.Request代表客户端发送服务器的请求数据。
http.Handle函数
跟HandleFunc一样,Handle也是http封装好的函数,第一个参数跟HandleFunc一样,而第二个参数则是必须是实现了http.Handler接口的类型,http.Handler在http包的定义如下:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
http.ServeMux
无论是使用http.Handle还是http.HandleFunc函数,其实底层代码都是使用http.DefaultServeMux,DefaultServeMux的定义如下代码所示:
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
type Controller struct {}
func (c Controller)ServeHTTP(writer http.ResponseWriter, request *http.Request){
writer.Write([]byte("hello,1"));
}
func hello(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("hello,2"));
}
func main(){
mux := &http.ServeMux{}
mux.HandleFunc("/hello1",hello)
mux.Handle("/hello2",http.HandlerFunc(hello))
mux.Handle("/hello3",&Controller{})
log.Fatal(http.ListenAndServe(":8080",mux))
}
http.Server
http.Server是http包中对web更加底层的支持,我们前面使用的方法,都是对http.Server的封装而已,如果直接使用http.Server,则可以自定义更多的参数,如果连接超时等参数,因此我们下面直接使用http.Server开发Web服务。