18.1 基本介绍
Golang的主要设计目标之一,就是面向大规模后端服务,网路通信这块是服务端程序必不可少也是至关重要的一部分。
网络编程有两种
1、TCP socket编程,是网络编程的主流。之所以叫Tcp socket编程,是因为是基于tcp/ip协议的。比如:QQ聊天
2、b/s结构的http编程,我们使用浏览器去访问服务器时,使用的就是http协议,而http底层依旧是用tcp socket实现的。 比如:京东商城 (这属于 go web开发范畴)
18.1.1 协议tcp/ip
TCP/IP 中文名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是Internet最基本的协议,Internet国际互联网络的基础,简单的说,就是由网络层的IP协议和传输层的TCP协议组成的。
18.1.2 OSI与TCP/IP参考模型
追踪一个路由:windows下 tracert www.baidu.com
18.1.3 ip地址
概述:每个internet上的主机和路由器都有一个IP地址,它包括网络号和主机号,地址有ipv4(32位)和ipv6(128位),可以通过ipconfig来查看
18.1.4 端口
这里所指的端口不是物理意义上的端口,而是特指TCP/IP协议中的E端口,是逻辑意义上的端口
如果把IP地址比作一间房子,端口欧式出入这间房子懂的门。真正的房子只有几个们,但是一个IP地址的端口可以有65536(即:255 × 255)个之多!端口是通过端口号来标记的,端口号只有整数,范围是从0到65535(即:255 × 255 - 1)
端口分类:
- 0是保留端口
- 1-1024是固定端口,又叫有名端口,即被某些程序固定使用,一般程序员不使用
- 1025-65535是动态端口,这些端口,程序员可以使用
端口使用注意
- 在计算机(尤其是做服务器)要尽可能的少开端口
- 一个端口只能被一个程序监听
- 如果使用 netstat -an 可以查看本机有哪些端口在监听
- 可以使用 netstat -anb 来查看监听端口的pid,在结合任务管理器关闭不安全的端口
18.2 tcp socket编程的客户端和服务器端
下图为Golang socket编程中客户端和服务器端的网络分布。
18.3 tcp socket编程的入门
18.3.1 服务端的处理流程:
- 监听端口 8888
- 接收客户端的tcp链接,建立客户端和服务器端的链接
- 创建gorotine,处理该链接的请求(通常客户端会通过链接发送请求包)
18.3.2 客户端的处理流程:
- 建立与服务端的链接
- 发送请求数据[终端],接收服务器端返回的结果数据
- 关闭链接
简单的示意图
18.3.3 代码实现
服务端功能:
编写一个程序,在8888端口监听
可以和多个客户端创建链接
链接成功后,客户端可以发送数据,服务器端接收数据,并显示在终端上
先用telnet来测试,然后编写客户端程序来测试
服务端代码: server.go
package main
import (
"fmt"
"net" //做网络开发时,net包含了我们需要的所有的包
)
func process(conn net.Conn) {
// 这里循环接收客户端发送的数据
defer conn.Close() //关闭conn
for {
// 创建一个新的切片
buf := make([]byte, 1024)
// 1、等待客户端通过conn发送信息给我,
// 2、如果客户端没有write[发送],那么协程就阻塞在这里
// fmt.Printf("服务器在等待客户端%s 发送数据
" + conn.RemoteAddr().String())
n, err := conn.Read(buf) //从conn获取
if err != nil {
fmt.Printf("客户端退出 err=%v", err)
return //!!!!!!!!!!!!!!
}
// 3、显示客户端发送的内容到服务器的终端
fmt.Print(string(buf[:n]))
}
}
func main() {
fmt.Println("服务器开始监听....")
// 1、TCP表示使用网络协议是tcp
// 2、0.0.0.0:8888表示在本地监听 8888端口
listen, err := net.Listen("tcp", "0.0.0.0:8888")
if err != nil {
fmt.Println("listen err=", err)
return
}
defer listen.Close() //延时关闭
// 循环等待客户端来链接我
for {
// 等待客户端来链接我
fmt.Println("等待客户端来链接我....")
conn, err := listen.Accept()
if err != nil {
fmt.Printf("Accept() err=%v", err)
} else {
fmt.Printf("Accept() sucess con=%v 客户端IP=%v
", conn, conn.RemoteAddr().String())
}
// 这里准备起一个协程,为客户端服务
go process(conn)
}
}
客户端功能:
编写一客户端程序,能链接到服务器端的8888端口
客户端可以发送单行数据 ,然后就退出
能通过终端输入数据(输入一行,发送一行),并发送给服务器端口
在终端输入exit,表示退出程序
客户端代码:client.go
package main
import (
"fmt"
"net"
"bufio"
"os"
"strings"
)
func main() {
conn, err := net.Dial("tcp", "192.168.1.6:8888")
if err != nil {
fmt.Println("client dial err=", err)
return
}
//功能1:客户端可以发送单行数据,然后就退出
reader := bufio.NewReader(os.Stdin) //os.Stdin 表示标准输入
for {
// 从终端读取一行用户输入,并准备发送给服务器
line, err := reader.ReadString('
')
if err != nil {
fmt.Println("readerString err=", err)
}
// 如果用户输入的是exit就退出
line = strings.Trim(line, "
")
if line == "exit" {
fmt.Println("客户端退出了......")
break
}
// 再将line发送给服务器
_, err = conn.Write([]byte(line + "
"))
if err != nil {
fmt.Println("conn.Write err=", err)
}
}
}
运行结果