Go在网络处理方面是非常强大,提供完整并简单的包,使用者很容易上手使用这些包。
一 HTTP
》HttpClient 使用
1.GET 请求
2.POST请求
3.自定义Request
》HTTP SETVER 使用
type MyHandler struct{
}
//请求处理器 必须实现的方法
func(handler *MyHandler)ServeHTTP(writer http.ResponseWriter,req *http.Request) {
writer.Write([]byte("hello,world"))
}
func handle() {
mux:=http.NewServeMux()
//重定向
rh:=http.RedirectHandler("http://www.baidu.com",307)
//注册响应处理器, 根据指定的路径
mux.Handle("/foo",rh)
//根据地址,建立连接
http.ListenAndServe(":3000",mux)
}
func handle2() {
//注册匿名的处理器
http.HandleFunc("/foo",func(writer http.ResponseWriter,request *http.Request) {
fmt.Println("hello,world")
writer.Write([]byte("hello,world"))
})
//注册自定义的处理器
http.Handle("/boo",&MyHandler{})
//建立连接 、开启监听
http.ListenAndServe(":3000",nil)
}
二 TCP
》客户端使用
func main() {
conn,e:=net.Dial("tcp","127.0.0.1:8888")
if e!=nil {
panic(e)
}
defer conn.Close()
conn.Write([]byte("i am client"))
b:=make([]byte,1024)
conn.Read(b)
fmt.Println(string(b))
}
》服务端使用
packagemain
import(
"net"
"fmt"
"time"
)
/**
conn.Read的行为特点
1.1、Socket中无数据
连接建立后,如果对方未发送数据到socket,接收方(Server)会阻塞在Read操作上,这和前面提到的“模型”原理是一致的。执行该Read操作的goroutine也会被挂起。runtime会监视该socket,直到其有数据才会重新
调度该socket对应的Goroutine完成read。由于篇幅原因,这里就不列代码了,例子对应的代码文件:go-tcpsock/read_write下的client1.go和server1.go。
1.2、Socket中有部分数据
如果socket中有部分数据,且长度小于一次Read操作所期望读出的数据长度,那么Read将会成功读出这部分数据并返回,而不是等待所有期望数据全部读取后再返回。
1.3、Socket中有足够数据
如果socket中有数据,且长度大于等于一次Read操作所期望读出的数据长度,那么Read将会成功读出这部分数据并返回。这个情景是最符合我们对Read的期待的了:Read将用Socket中的数据将我们传入的slice填满后返回:n = 10, err = nil
1.4、Socket关闭
如果client端主动关闭了socket,那么Server的Read将会读到什么呢?
这里分为“有数据关闭”和“无数据关闭”。
有数据关闭是指在client关闭时,socket中还有server端未读取的数据。当client端close socket退出后,server依旧没有开始Read,10s后第一次Read成功读出了所有的数据,当第二次Read时,由于client端 socket关闭,Read返回EOF error
无数据关闭情形下的结果,那就是Read直接返回EOF error
1.5、读取操作超时
有些场合对Read的阻塞时间有严格限制,在这种情况下,Read的行为到底是什么样的呢?在返回超时错误时,是否也同时Read了一部分数据了呢?
不会出现“读出部分数据且返回超时错误”的情况
*/
/**
conn.Write的行为特点
2.1、成功写
前面例子着重于Read,client端在Write时并未判断Write的返回值。所谓“成功写”指的就是Write调用返回的n与预期要写入的数据长度相等,且error = nil。这是我们在调用Write时遇到的最常见的情形,这里不再举例了
2.2、写阻塞
TCP连接通信两端的OS都会为该连接保留数据缓冲,一端调用Write后,实际上数据是写入到OS的协议栈的数据缓冲的。TCP是全双工通信,因此每个方向都有独立的数据缓冲。当发送方将对方的接收缓冲区以及自身的发送缓冲区写满后,Write就会阻塞
2.3、写入部分数据
Write操作存在写入部分数据的情况。没有按照预期的写入所有数据。这时候循环写入便是
*/
func handleConn(conn net.Conn) {
fmt.Println("handler conn....")
deferconn.Close()
b:=make([]byte,1024)
conn.Read(b)
fmt.Println(string(b))
conn.Write([]byte("hello,I have receiver message"))
}
/**
Goroutine safe
基于goroutine的网络架构模型,存在在不同goroutine间共享conn的情况,那么conn的读写是否是goroutine safe的呢?在深入这个问题之前,我们先从应用意义上来看read操作和write操作的goroutine-safe必要性。
对于read操作而言,由于TCP是面向字节流,conn.Read无法正确区分数据的业务边界,因此多个goroutine对同一个conn进行read的意义不大,goroutine读到不完整的业务包反倒是增加了业务处理的难度。对与Write操作而言,倒是有多个goroutine并发写的情况。
每次Write操作都是受lock保护,直到此次数据全部write完。因此在应用层面,要想保证多个goroutine在一个conn上write操作的Safe,需要一次write完整写入一个“业务包”;一旦将业务包的写入拆分为多次write,那就无法保证某个Goroutine的某“业务包”数据在conn发送的连续性。
同时也可以看出即便是Read操作,也是lock保护的。多个Goroutine对同一conn的并发读不会出现读出内容重叠的情况,但内容断点是依
runtime调度来随机确定的。存在一个业务包数据,1/3内容被goroutine-1读走,另外2/3被另外一个goroutine-2读 走的情况。比如一个完整包:world,当goroutine的read slice size < 5时,存在可能:一个goroutine读到 “worl”,另外一个goroutine读出”d”
*/
funcmain() {
l,e:=net.Listen("tcp",":8888")
ife!=nil {
panic(e)
}
deferl.Close()
for{
conn,err:=l.Accept()
iferr !=nil{
fmt.Println("error")
break
}
time.Sleep(time.Second*10)
gohandleConn(conn)
}
}