1. 网络编程基本介绍
Golang的主要设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端,程序必不可少也是至关重要的一部分。
网络编程有两种:
1)TCP socket编程,是网络编程的主流。之所以叫Tcp socket编程,是因为底层是基于Tcp/ip协议的,比如:QQ聊天【示意图】
2)b/s结构的http编程,我们使用浏览器去访问服务器时,使用的就是http协议,而http底层依旧是用tcp socket实现的。【示意图】 比如:京东商城【这属于 go web开发范畴】
2.协议(tcp/ip)
TCP/IP(Transmission Control Protocol/Internet Protocol)的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是Internet最基本的协议、Internet国际互联网络的基础,简单地说,就是由网络层的IP协议和传输层的TCP协议组成的。
3. OSI与Tcp/ip 参考模型(推荐tcp/ip协议3卷)
4. ip地址
概述:每个internet上的主机和路由器都有一个ip地址,它包括网络号和主机号,ip地址有ipv4(32位)或者ipv6(128位).可以通过ipconfig来查看
5. 端口(port)-介绍
我们这里所指的端口不是指物理意义上的端口,而是特指TCP/IP协议中的端口,是逻辑意义上的端口
如果把IP地址比作一间房子,端口就是出入这间房子的门。真正的房子只有几个门,但是一个IP地址的端口可以有65536个(即256 * 256)个之多。端口是通过端口号来标记的,端口号只有整数,范围是从0到65535(256 * 256 -1)
6. 端口(port)-分类
0号是保留端口
1-1024是固定端口(程序员不要使用),又叫有名端口,即被某些程序固定使用,一般程序员不使用
22:SSH远程登录协议
23:telnet使用
21:ftp使用
25:smtp服务使用
80:iis使用
7:echo服务
1025——65535是动态端口,这些端口程序员可以使用
7.端口(port)-使用注意
1)在计算机(尤其是做服务器)要尽可能的少开端口
2)一个端口只能被一个程序监听
3)如果使用netstat - an 可以查看本机有哪些端口在监听
4)可以使用netstat - anb 来查看监听端口的pid,在结合任务管理器关闭不安全的端口
8.tcp socket编程的客户端和服务器端
为了授课方便,我们将tcp socket编程,简称socket编程,下图为Golang socket编程中客户端和服务器的网络分布图:
9. tcp socket 编程的快速入门——服务器的处理流程
1)监听端口8888
2)接收客户端的tcp链接,建立客户端和服务器端的链接
3)创建goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)
10.tcp socket 编程的快速入门——客户端的处理流程
1)建立与服务端的链接
2)发送请求数据【终端】,接收服务器端返回的结果数据
3)关闭链接
11. tcp socket 编程的快速入门——简单的程序示意图
12. 代码的实现
程序框架图示意图
服务器端功能:
编写一个服务器端程序,在8888端口监听
可以和多个客户端创建链接
链接成功后,客户端可以发送数据,服务器端接收数据,并显示在终端上
先使用telnet来测试(可能电脑没有telnet服务器选项,暂没找到解决办法,建议直接使用客户端测试),然编写客户端程序来测试
服务器端代码:
package main
import (
"fmt"
"net" // 做网络socket开发时,net包含有我们需要所有的方法和函数
)
// 这里必须要有一个conn,否则啥也干不了
func process(conn net.Conn) {
fmt.Println("执行了process")
// 这里我们循环的接收客户端发送的数据
defer conn.Close()
for {
// 每次创建一个新的切片
// Read从连接中读取数据
// Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
buf := make([]byte, 1024)
// 1. 等待客户端通过conn发送信息
// 2. 如果客户端没有write[发送],那么协程就一直阻塞在这里,也有可能会超时
// n代表读取的字节数
// fmt.Printf("服务器在等待客户端%s 发送信息\n",conn.RemoteAddr().String())
n, err := conn.Read(buf) // 从conn读取
if err != nil {
// 如果 err = io.EOF,则代表客户端退出
// 如果等待出了问题,如:超时,客户端退出[链接断了]
fmt.Println("服务器的Read err=",err)
return
}
// 3. 显示客户端发送的内容到服务器的终端
fmt.Println(string(buf[:n]))
}
}
func main() {
fmt.Println("服务器开始监听了...")
// 127.0.0.1只能监听ipv4,0.0.0.0可以监听ipv4和ipv6
// 1. tcp表示使用的网络协议是tcp
// 2. 0.0.0.0:8888 表示在本地监听 8888端口
listen, err := net.Listen("tcp", "127.0.0.1:8888")
if err != nil {
fmt.Printf("listen err=%v\n",err)
return
}
// Close关闭该接口,并使任何阻塞的Accept操作都会不再阻塞并返回错误
defer listen.Close()
// 循环等待客户端来链接我
for {
// 等待客户端来链接我
fmt.Println("等待客户端来链接")
conn, err := listen.Accept()
if err != nil {
// 这个地方错了,不需要return
// 因为A客户端错了,B客户端不一定错了
fmt.Printf("Accept() err=%v\n",err)
} else {
fmt.Printf("Accept() suc conn=%v\n",conn)
// RemoteAddr() Addr
// Conn.RemoteAddr返回远端网络地址
// Addr.String() 字符串格式的地址
fmt.Printf("客户端的ip=%v\n",conn.RemoteAddr().String())
}
// 这里准备起一个协程,为客户端服务,这里不能写在主线程,使用协程,避免阻塞
go process(conn)
}
}
客户端功能:
1. 编写一个客户端程序,能链接到服务器端的8888端口
2. 客户端可以发送单行数据,然后就退出
3. 能通过终端输入数据(输入一行发送一行),并发送给服务器端[]
4. 在终端输入exit,表示退出程序
客户端代码:
package main
import (
"fmt"
"net"
"bufio"
"os"
"strings"
)
func main() {
// 1. 编写一个客户端程序,能链接到 服务器端的8888端口
conn, err := net.Dial("tcp", "127.0.0.1:8888")
if err != nil {
fmt.Println("client dial err=",err)
return
}
fmt.Println("connect success",conn)
// 功能1: 客户端可以发送单行数据,然后就退出
// Stdin、Stdout和Stderr是指向标准输入、标准输出、标准错误输出的文件描述符。
// 所有的socket都会落在一个文件上
reader := bufio.NewReader(os.Stdin) // os.Stdin 代表标准输入【终端】
for {
// 从终端读取一行用户输入,并准备发送给服务器
line, err := reader.ReadString('\n')
// 如果用户输入的是exit就退出
line = strings.Trim(line," \r\n") // 去除空格换行
if line == "exit" {
fmt.Println("客户端退出...")
break
}
// 再将line 发送给 服务器
// n代表返回的字节
_, err = conn.Write([]byte(line))
if err != nil {
fmt.Println("conn.Write err=",err)
}
}
}
声明:文章是个人做笔记用的,不喜勿喷....