文章目录
一 基础知识
1.1 网络编程分类
网络编程有两种:
- TCP socket 编程,是网络编程的主流。之所以叫 Tcp socket 编程,是因为底层是基于 Tcp/ip 协议的. 比如: QQ 聊天
- b/s 结构的 http 编程,我们使用浏览器去访问服务器时,使用的就是 http 协议,而 http 底层依旧是用 tcp socket 实现的。比如: 京东商城
1.2 协议
TCP/IP(Transmission Control Protocol/Internet Protocol)的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是 Internet 最基本的协议、Internet 国际互联网络的基础,简单地说,就是由网络层的 IP 协议和传输层的 TCP 协议组成的。
1.3 OSI 与 Tcp/ip 参考模型
QQ间相互通讯案例:
1.4 ip地址
每个 internet 上的主机和路由器都有一个 ip 地址,它包括网络号和主机号,ip 地址有 ipv4(32位)或者 ipv6(128 位). 可以通过 ipconfig 来查看
1.5 端口
我们这里所指的端口不是指物理意义上的端口,而是特指 TCP/IP 协议中的端口,是逻辑意义上的端口。
如果把 IP 地址比作一间房子,端口就是出入这间房子的门。真正的房子只有几个门,但是一个 IP 地址的端口 可以有 65536(即:256×256)个之多!端口是通过端口号来标记的,端口号只有整数,范围是从 0 到 65535(256×256-1)
端口分类:
- 0 号是保留端口.
- 1-1024 是固定端口(程序员不要使用)又叫有名端口,即被某些程序固定使用,一般程序员不使用.
22: SSH 远程登录协议 23: telnet 使用 21: ftp 使用
25: smtp 服务使用 80: iis 使用 7: echo 服务 - 1025-65535 是动态端口。这些端口,程序员可以使用.
注意事项:
- 在计算机(尤其是做服务器)要尽可能的少开端口
- 一个端口只能被一个程序监听
- 如果使用 netstat –an 可以查看本机有哪些端口在监听
- 可以使用 netstat –anb 来查看监听端口的 pid,在结合任务管理器关闭不安全的端口
二 tcp socket 编程流程
2.1 服务端的处理流程
- 监听端口 8888
- 接收客户端的 tcp 链接,建立客户端和服务器端的链接.
- 创建 goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)
2.2 客户端的处理流程
- 建立与服务端的链接
- 发送请求数据[终端],接收服务器端返回的结果数据
- 关闭链接
2.3 程序示意图
- 服务端监听端口8888,客户端链接8888端口后,建立链接
- 服务端主线程§ 接收到客户端链接时,开启一个协程
- 处理客户端的请求,使得我们可以做一些分支来处理请求
三 net包
使用go做socket网络开发使用的是 net 包
import "net"
net包提供了可移植的网络I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket。
虽然本包提供了对网络原语的访问,大部分使用者只需要Dial、Listen和Accept函数提供的基本接口;以及相关的Conn和Listener接口。crypto/tls包提供了相同的接口和类似的Dial和Listen函数。
官方示例:
Dial函数和服务端建立连接:
conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
// handle error
}
fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
status, err := bufio.NewReader(conn).ReadString('\n')
// ...
Listen函数创建的服务端:
ln, err := net.Listen("tcp", ":8080")
if err != nil {
// handle error
}
for {
conn, err := ln.Accept()
if err != nil {
// handle error
continue
}
go handleConnection(conn)
}
四 net包重要类型
4.1 type Dialer
type Dialer struct {
// Timeout是dial操作等待连接建立的最大时长,默认值代表没有超时。
// 如果Deadline字段也被设置了,dial操作也可能更早失败。
// 不管有没有设置超时,操作系统都可能强制执行它的超时设置。
// 例如,TCP(系统)超时一般在3分钟左右。
Timeout time.Duration
// Deadline是一个具体的时间点期限,超过该期限后,dial操作就会失败。
// 如果Timeout字段也被设置了,dial操作也可能更早失败。
// 零值表示没有期限,即遵守操作系统的超时设置。
Deadline time.Time
// LocalAddr是dial一个地址时使用的本地地址。
// 该地址必须是与dial的网络相容的类型。
// 如果为nil,将会自动选择一个本地地址。
LocalAddr Addr
// DualStack允许单次dial操作在网络类型为"tcp",
// 且目的地是一个主机名的DNS记录具有多个地址时,
// 尝试建立多个IPv4和IPv6连接,并返回第一个建立的连接。
DualStack bool
// KeepAlive指定一个活动的网络连接的生命周期;如果为0,会禁止keep-alive。
// 不支持keep-alive的网络连接会忽略本字段。
KeepAlive time.Duration
}
Dialer类型包含与某个地址建立连接时的参数。
每一个字段的零值都等价于没有该字段。因此调用Dialer零值的Dial方法等价于调用Dial函数。
*func (Dialer) Dial
func (d *Dialer) Dial(network, address string) (Conn, error)
Dial在指定的网络上连接指定的地址。参见Dial函数获取网络和地址参数的描述。
4.2 type Listener
type Listener interface {
// Addr返回该接口的网络地址
Addr() Addr
// Accept等待并返回下一个连接到该接口的连接
Accept() (c Conn, err error)
// Close关闭该接口,并使任何阻塞的Accept操作都会不再阻塞并返回错误。
Close() error
}
Listener是一个用于面向流的网络协议的公用的网络监听器接口。多个线程可能会同时调用一个Listener的方法。
func Listen
func Listen(net, laddr string) (Listener, error)
返回在一个本地网络地址laddr上监听的Listener。网络类型参数net必须是面向流的网络:
“tcp”、“tcp4”、“tcp6”、“unix"或"unixpacket”。参见Dial函数获取laddr的语法。
4.3 type Conn
type Conn interface {
// Read从连接中读取数据
// Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
Read(b []byte) (n int, err error)
// Write从连接中写入数据
// Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
Write(b []byte) (n int, err error)
// Close方法关闭该连接
// 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误
Close() error
// 返回本地网络地址
LocalAddr() Addr
// 返回远端网络地址
RemoteAddr() Addr
// 设定该连接的读写deadline,等价于同时调用SetReadDeadline和SetWriteDeadline
// deadline是一个绝对时间,超过该时间后I/O操作就会直接因超时失败返回而不会阻塞
// deadline对之后的所有I/O操作都起效,而不仅仅是下一次的读或写操作
// 参数t为零值表示不设置期限
SetDeadline