1.1.1. 一、协议之间的关系。
1、socket 封装了 TCP/IP协议。
2、HTTP 协议(HyperText Transfer Protocol,超文本传输协议) 封装了 socket。
3、如果我们要自定义一个协议,是和HTTP是一层的。相当于把HTTP替换为自己的协议。比如:使用protobuf定义了自己的协议。
1.1.2. 二、TCP/IP 协议。
1、TCP/IP 协议。
TCP/IP(Transmission Control Protocol/Internet Protocol 的简写),中文为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是 Internet 最基本的协议、Internet 国际互联网络的基础,简单地说,就是由网络层的 IP 协议和传输层的 TCP 协议组成的。
2、IP地址。
每个 internet 上的主机和路由器都有一个 ip 地址,它包括网络号和主机号,ip 地址有 ipv4(4字节,32 位)或者 ipv6(16字节,128 位),可以通过 ipconfig 来查看。
3、端口。
电脑的端口有2字节,16位。范围是从 0 到 65535(2的16次方-1)。
可以使用 netstat –an 可以查看本机有哪些端口在监听。使用 netstat –anb 来查看监听端口的 pid。
1.1.3. 三、tcp服务器、客户端通信的实现。
go语言网络编程是在net包下面。
1、服务器 server.go 代码:
实现效果:服务器监听8888端口,等待客户端连接。客户端连接之后,开启一个协程处理客户端连接。这时候服务器和客户端实际上是一个tcp长连接。客户端和服务器一直可以互相发信息通信。它们是双工的。
package main
import (
"fmt"
"net"
)
// 处理请求
func processTest(conn net.Conn) {
//关闭连接
defer conn.Close()
//循环接收客户端发送的数据
for {
buf := make([]byte,1024) //创建切片接收数据
//fmt.Printf("等待客户端 %s 发送数据 \n",conn.RemoteAddr())
n, err := conn.Read(buf) //阻塞等待接收客户端信息。
if err != nil {
fmt.Println("服务端Read 错误 err=",err)
return
}
//显示服务端发送的内容,n表示实际读到的字节数
fmt.Print("客户端的数据=",string(buf[:n]))
}
}
func main() {
fmt.Println("服务器开始监听。。。")
//本地监听 8888 端口
listener, err := net.Listen("tcp", "0.0.0.0:8888")
if err != nil {
fmt.Println("监听失败。。。",err)
return
}
defer listener.Close() // 延时关闭
for {
fmt.Println("等待客户端连接。。。")
conn, err := listener.Accept() //等待连接。
if err != nil {
fmt.Println("Accept() error=",err)
}else {
fmt.Printf("连接成功 conn=%v ,客户端的ip=%v \n",conn,conn.RemoteAddr())
}
//开一个协程,处理一个请求。
go processTest(conn)
}
}
关键代码说明:
1、监听tcp端口:
listener, err := net.Listen("tcp", "0.0.0.0:8888")
2、关闭监听:
defer listener.Close()
3、阻塞等待连接:
conn, err := listener.Accept()
4、开一个协程,处理一个连接:
go processTest(conn)
5、关闭连接:
defer conn.Close()
6、阻塞等待接收客户端信息:
n, err := conn.Read(buf)
2、客户端 client.go 代码。
实现效果:客户端循环读取控制台输入,向服务器发送读取的内容。如果控制台输入 exit,客户端就退出。
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
//连接127.0.0.1:8888,返回连接Conn
conn, err := net.Dial("tcp", "127.0.0.1:8888")
if err != nil {
fmt.Printf("连接错误 err=%v", err)
return
}
//os.Stdin 是标准输入,就是终端控制台。从控制台读取数据。
reader := bufio.NewReader(os.Stdin)
// 循环读取控制台输入,向服务器发送
for {
str, err := reader.ReadString('\n') //阻塞等待输入
if err != nil {
fmt.Printf("从控制台读取数据错误 err=%v", err)
return
}
//输入exit退出。
if strings.Trim(str,"\r\n") == "exit" {
fmt.Printf("客户端退出")
return
}
// 把数据发送给服务器
n, err := conn.Write([]byte(str))
if err != nil {
fmt.Printf("发送数据错误 err=%v", err)
return
}
fmt.Printf("发送了 %d 字节数据。\n", n)
}
}
关键代码说明:
1、连接127.0.0.1:8888,返回连接Conn。
conn, err := net.Dial("tcp", "127.0.0.1:8888")
2、建立控制台读取数据的Reader:
reader := bufio.NewReader(os.Stdin)
3、阻塞等待控制台输入:
str, err := reader.ReadString('\n')
4、把数据发送给服务器:
n, err := conn.Write([]byte(str))
3、运行效果。
- 服务器控制台:
服务器开始监听。。。
等待客户端连接。。。
连接成功 conn=&{ {0xc00008c2c0} } ,客户端的ip=127.0.0.1:59028
等待客户端连接。。。
客户端的数据=你好
客户端的数据=abc
服务端Read 错误 err= read tcp 127.0.0.1:8888->127.0.0.1:59028: wsarecv: An existing connection was forcibly closed by the remote host.
- 客户端控制台:
你好
发送了 7 字节数据。
abc
发送了 4 字节数据。
exit
客户端退出