十二、网络编程

1.TCP编程

  • 服务器:
func main() {
	//指定服务器通信协议、IP地址、端口号
	listener, err := net.Listen("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer listener.Close() //关闭

	//阻塞监听客户端连接请求
	conn, err := listener.Accept()
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close() //关闭

	//读取客户端发送的数据
	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if err != nil {
		fmt.Println(err)
		return
	}

	//处理数据
	fmt.Println("服务器:", string(buf[:n]))
	conn.Write([]byte("reply:" + string(buf[:n])))
}
  • 客户端:
func main() {
	//指定服务器通信协议、IP地址、端口号
	conn, err := net.Dial("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close()  //关闭

	//主动写数据给服务器
	conn.Write([]byte("hello world"))

	//接收服务器回发的数据
	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(buf[:n]))
}
1.1并发版本
  • 服务器:
func HandlerConnect(conn net.Conn) {
	defer conn.Close() //关闭
	// 获取连接的客户端 Addr
	addr := conn.RemoteAddr()
	fmt.Println(addr, ",客户端成功连接!")

	//循环读取客户端发送的数据
	buf := make([]byte, 4096)
	for {
		n, err := conn.Read(buf)
		data := string(buf[:n])
		if strings.HasPrefix(strings.ToLower(data), "exit") {
			fmt.Println("服务器检测到客户端[", addr, "]退出...")
			return
		} else if n == 0 {
			fmt.Println("服务器检测到客户端[", addr, "]已关闭...")
			return
		} else if err != nil {
			fmt.Println("conn.Read err:", err)
			return
		}

		//处理数据
		fmt.Println(addr, ",服务器读到数据:", data)
		conn.Write([]byte("reply:" + data))
	}
}

func main() {
	//创建监听套接字
	listener, err := net.Listen("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Listen err:", err)
		return
	}
	defer listener.Close() //关闭

	//监听客户端连接请求
	for {
		fmt.Println("服务器等待客户端连接...")
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listener.Accept err:", err)
			return
		}

		//具体完成服务器和客户端的数据通信
		go HandlerConnect(conn)
	}
}
  • 客户端:
func main() {
	//主动发起连接请求
	conn, err := net.Dial("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Dial err:", err)
		return
	}
	defer conn.Close() //关闭

	// 获取用户键盘输入( stdin ),将输入数据发送给服务器
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := os.Stdin.Read(buf)
			if err != nil {
				fmt.Println("os.Stdin.Read err:", err)
				continue
			}
			//写给服务器, 读多少,写多少!
			conn.Write(buf[:n])
		}
	}()

	// 回显服务器回发的数据
	buf := make([]byte, 4096)
	for {
		n, err := conn.Read(buf)
		if n == 0 {
			fmt.Println("客户端检测到服务器已关闭...")
			return
		} else if err != nil {
			fmt.Println("conn.Read err:", err)
			return
		}
		fmt.Println(string(buf[:n]))
	}
}

2.UDP编程

  • 服务器
func main() {
	//组织一个udp地址结构
	srvAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.ResolveUDPAddr err:", err)
		return
	}
	fmt.Println("UDP服务器地址结构创建完成。")

	//创建用于通信的socket
	udpConn, err := net.ListenUDP("udp", srvAddr)
	if err != nil {
		fmt.Println("net.ListenUDP err:", err)
		return
	}
	defer udpConn.Close() //关闭
	fmt.Println("UDP服务器通信socket创建完成。")

	//读取客户端发送的数据
	buf := make([]byte, 4096)
	//返回3个值,分别是读取到的字节数、客户端的地址、error
	n, cltAddr, err := udpConn.ReadFromUDP(buf)
	if err != nil {
		fmt.Println("udpConn.ReadFromUDP err:", err)
		return
	}

	//模拟处理数据
	fmt.Printf("服务器读到 %v 的数据:%s\n", cltAddr, string(buf[:n]))
	//回写数据给客户端
	_, err = udpConn.WriteToUDP([]byte("replay:"+string(buf[:n])), cltAddr)
	if err != nil {
		fmt.Println("udpConn.WriteToUDP err:", err)
		return
	}
}
  • 客户端
  • 参考 TCP 客户端。net.Dial(“udp”, “server 的IP+port”)
func main() {
	//指定服务器通信协议、IP地址、端口号
	conn, err := net.Dial("udp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close()  //关闭

	//主动写数据给服务器
	conn.Write([]byte("hello world"))

	//接收服务器回发的数据
	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(buf[:n]))
}
2.1并发版本
  • 服务器
  • UDP默认支持客户端并发访问,服务器处理 ReadFromUDP 和 WriteToUDP操作分开。提高并发效率。
func main() {
	//组织一个udp地址结构
	srvAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.ResolveUDPAddr err:", err)
		return
	}
	fmt.Println("UDP服务器地址结构创建完成。")

	//创建用于通信的socket
	udpConn, err := net.ListenUDP("udp", srvAddr)
	if err != nil {
		fmt.Println("net.ListenUDP err:", err)
		return
	}
	defer udpConn.Close() //关闭
	fmt.Println("UDP服务器通信socket创建完成。")

	//读取客户端发送的数据
	buf := make([]byte, 4096)
	for {
		//返回3个值,分别是读取到的字节数、客户端的地址、error
		n, cltAddr, err := udpConn.ReadFromUDP(buf)
		if err != nil {
			fmt.Println("udpConn.ReadFromUDP err:", err)
			return
		}

		//模拟处理数据
		fmt.Printf("服务器读到 %v 的数据:%s\n", cltAddr, string(buf[:n]))

		go func() {
			//回写数据给客户端
			_, err = udpConn.WriteToUDP([]byte("replay:"+string(buf[:n])), cltAddr)
			if err != nil {
				fmt.Println("udpConn.WriteToUDP err:", err)
				return
			}
		}()
	}
}
  • 客户端(参考TCP客户端)
func main() {
	//指定服务器通信协议、IP地址、端口号
	conn, err := net.Dial("udp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close() //关闭

	//获取用户键盘输入数据
	go func() {
		buf := make([]byte, 4096)
		for {
			n, err := os.Stdin.Read(buf)
			if err != nil {
				fmt.Println("os.Stdin.Read err:", err)
				continue
			}
			//主动写数据给服务器
			conn.Write(buf[:n])
		}
	}()

	buf := make([]byte, 4096)
	for {
		//接收服务器回发的数据
		n, err := conn.Read(buf)
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println(string(buf[:n]))
	}
}

3.文件传输

  • 发送端:
func SendFile(conn net.Conn, filePath string) {
	//只读打开文件
	file, err := os.Open(filePath)
	if err != nil {
		fmt.Println("os.Open err:", err)
		return
	}
	defer file.Close()

	//从本地文件中读数据,写给接收端。读多少写多少
	buf := make([]byte, 4096)
	for {
		n, err := file.Read(buf)
		if err != nil {
			if err == io.EOF {
				fmt.Println("发送文件完毕...")
			} else {
				fmt.Println("file.Read err:", err)
			}
			return
		}
		//写到网络socket中
		_, err = conn.Write(buf[:n])
		if err != nil {
			fmt.Println("conn.Write err:", err)
			return
		}
	}
}

func main() {
	//go run Client.go D:\books\golang\netcat-win32-1.12.zip
	list := os.Args //获取命令行参数
	if len(list) != 2 {
		fmt.Println("格式为:go run xxx.go 文件绝对路径")
		return
	}

	//提取文件的绝对路径
	filePath := list[1]

	//提取文件名
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		fmt.Println("os.Stat err:", err)
		return
	}
	fileName := fileInfo.Name()

	//主动发起连接请求
	conn, err := net.Dial("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Dial err:", err)
		return
	}
	defer conn.Close()

	//发送文件名给接收端
	_, err = conn.Write([]byte(fileName))
	if err != nil {
		fmt.Println("conn.Write err:", err)
		return
	}

	//读取服务器回发的数据
	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if err != nil {
		fmt.Println("conn.Read err:", err)
		return
	}

	//判断是否收到服务器回发的“ok”
	data := string(buf[:n])
	if strings.HasPrefix(strings.ToLower(data), "ok") {
		//写文件内容给服务器
		SendFile(conn, filePath)
	}
}
  • 接收端
func RecvFile(conn net.Conn, fileName string) {
	//按照文件名创建新文件
	file, err := os.Create(fileName)
	if err != nil {
		fmt.Println("os.Create err:", err)
		return
	}
	defer file.Close()

	//从网络中读数据,写入本地文件
	buf := make([]byte, 4096)
	for {
		n, err := conn.Read(buf)
		if n == 0 {
			fmt.Println("接收文件完毕...")
			return
		} else if err != nil {
			fmt.Println("conn.Read err:", err)
			return
		}
		//写入本地文件,读多少写多少
		file.Write(buf[:n])
	}
}

func main() {
	//创建用户监听的socket
	listen, err := net.Listen("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Listen err:", err)
		return
	}
	defer listen.Close()

	//阻塞监听
	conn, err := listen.Accept()
	if err != nil {
		fmt.Println("listen.Accept err:", err)
		return
	}
	defer conn.Close()

	//获取文件名,保存
	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if err != nil {
		fmt.Println("conn.Read err:", err)
		return
	}
	fileName := string(buf[:n])

	//会写ok给发送端
	_, err = conn.Write([]byte("ok"))
	if err != nil {
		fmt.Println("conn.Write err:", err)
		return
	}

	//获取文件内容
	RecvFile(conn, fileName)
}

4.HTTP编程

4.1http请求报文
  • 服务器代码:
func errFunc(info string, err error) {
	if err != nil {
		fmt.Println(info, err)
		os.Exit(1)
	}
}

func main() {
	listener, err := net.Listen("tcp", "127.0.0.1:8000")
	errFunc("net.Listen err:", err)
	defer listener.Close()

	conn, err := listener.Accept()
	errFunc("listener.Accept err:", err)
	defer conn.Close()

	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if n == 0 {
		return
	}
	errFunc("conn.Read err:", err)
	fmt.Printf("|%s|\n", string(buf[:n]))
}
  • 把上面这段代码运行起来,然后在浏览器输入:http://127.0.0.1:8000/
  • 就可以在服务器控制台得到请求报文:
|GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: __guid=96992031.1246173382985738200.1550026754561.2637; monitor_count=2

|
4.2http响应报文
  • 服务代码:
func handler(resp http.ResponseWriter, req *http.Request){
	resp.Write([]byte("hello world"))
}

func main() {
	//注册回调函数,该回调函数会在服务器被访问时,自动调用。
	http.HandleFunc("/test.do", handler)

	//绑定服务器监听地址
	http.ListenAndServe("127.0.0.1:8000", nil)
}
  • 客户端代码:
func errFunc2(info string, err error) {
	if err != nil {
		fmt.Println(info, err)
		os.Exit(1)
	}
}

func main() {
	conn, err := net.Dial("tcp", "127.0.0.1:8000")
	errFunc2("net.Dial err:", err)
	defer conn.Close()

	httpRequest := "GET /test.do HTTP/1.1\r\nHost:127.0.0.1:8000\r\n\r\n"
	conn.Write([]byte(httpRequest))

	buf := make([]byte, 4096)
	n, err := conn.Read(buf)
	if n == 0 {
		return
	}
	errFunc2("conn.Read err:", err)
	fmt.Printf("|%s|\n", string(buf[:n]))
}
|HTTP/1.1 200 OK
Date: Wed, 13 Feb 2019 03:08:51 GMT
Content-Length: 11
Content-Type: text/plain; charset=utf-8

hello world|
4.3HTTP服务端
  • 用TCP来开发服务端,就是做web应用,类似发布到Tomcat上面那些。
  • 用TCP来开发客户端,就是做类似爬虫那些。
func myHandler(resp http.ResponseWriter, req *http.Request) {
	//从客户端读到的内容
	fmt.Println("Header:", req.Header)
	fmt.Println("URL:", req.URL)
	fmt.Println("Method:", req.Method)
	fmt.Println("Host:", req.Host)
	fmt.Println("RemoteAddr:", req.RemoteAddr)
	fmt.Println("Body:", req.Body)

	//写给客户端的数据内容
	url := req.URL.String()
	if "/test" == url {
		resp.Write([]byte("This Is A Web Server"))
	} else if "/hello" == url {
		resp.Write([]byte("Hello World"))
	} else {
		resp.Write([]byte("404 Page Not Found"))
	}
}

func main() {
	//注册回调函数,该回调函数会在服务器被访问时,自动调用。
	//指定"/",则http://127.0.0.1:8000/* 都可以访问服务器
	//指定"/project/",则http://127.0.0.1:8000/project/* 都可以访问服务器
	http.HandleFunc("/", myHandler)

	//绑定服务器监听地址
	http.ListenAndServe("127.0.0.1:8000", nil)
}
4.4HTTP客户端
func main() {
	//使用Get方法获取服务器响应包数据
	//resp, err := http.Get("http://www.baidu.com")
	resp, err := http.Get("http://127.0.0.1:8000/test")
	if err != nil {
		fmt.Println("http.Get err:", err)
		return
	}
	defer resp.Body.Close()

	// 获取服务器端读到的数据
	fmt.Println("Status = ", resp.Status)         // 状态
	fmt.Println("StatusCode = ", resp.StatusCode) // 状态码
	fmt.Println("Header = ", resp.Header)         // 响应头部
	fmt.Println("Body = ", resp.Body)             // 响应包体

	buf := make([]byte, 4096) // 定义切片缓冲区,存读到的内容
	var result string
	// 获取服务器发送的数据包内容
	for {
		n, err := resp.Body.Read(buf) // 读body中的内容。
		if n == 0 {
			fmt.Println("-> Read finish!")
			break
		}
		if err != nil && err != io.EOF {
			fmt.Println("resp.Body.Read err:", err)
			return
		}
		result += string(buf[:n]) // 累加读到的数据内容
	}
	// 打印从body中读到的所有内容
	fmt.Println("result = ", result)
}
  • 输出结果
Status =  200 OK
StatusCode =  200
Header =  map[Date:[Wed, 13 Feb 2019 12:41:06 GMT] Content-Length:[20] Content-Type:[text/plain; charset=utf-8]]
Body =  &{0xc0420f0240 {0 0} false <nil> 0x5cee00 0x5ceda0}
-> Read finish!
result =  This Is A Web Server
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值