1.网络编程
1.1 网络协议
从应用的角度出发,协议可理解为“规则”,是数据传输和数据的规则,是通信双方都要遵守的规则。
协议存在的意义是为了让 双方为了更好的沟通
在双方之间被遵守的协议称为原始协议
当此协议被更多的人采用后,并不断的增加、改进、维护、完善。最终形成一个稳定的、完整的文件传输协议,被广泛应用与各种文件传输过程中。该协议就成为了一个标准协议
。 btw,最早的ftp协议就是由此衍生而来的。
1.2 分层模型
1.2.1 网络分层架构
网络模型采用分层的方式的是 为了减少协议设计的复杂性
每一层利用下一层提供的服务来为上一层提供服务,本层服务的实现细节对上层屏蔽。
TCP/IP
链路层:主要用于处理mac地址
网络层:主要处理IP相关的信息
传输层:主要处理端口相关的信息
应用层:主要处理应用层协议
1.2.2 层与协议
网络的每一层,都定义了很多协议。这些协议发总称,叫做“TCP/IP协议”。TCP/IP协议是一个大家族,不仅仅只有TCP和IP协议,它还包括其他协议,如下图:
TCP/IP 协议族:
ARP: Address Resolution Protocol, 正向地址解析协议。通过已知的IP,寻找对应的主机MAC地址。
RARP:是反响的地址转换协议,通过MAC地址确定IP地址。
IP:Internet Protocol,是因特网互联协议
ICMP:Internet Control Message Protocol,是Internet控制报文协议,它是TCP/IP协议的一个子协议,用于在IP主机、路由器之间传递控制消息。
eg: ping 127.0.0.1 就是用到了ICMP协议IGMP:Internet Group Management Protocol,是Internet组管理协议,它是因特网协议家族中的一个
组播
协议,该协议运行在主机和组播路由器之间。TCP:Transmission Control Protocol,传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。主要用于 文件传输
UDP:User Datagram Protocol,是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。主要用于 聊天
FTP:
Telnet
TFTP
NFS
1.2.3 每层的功能
- 链路层
以太网规定,连入网络的所有设备,都必须具有“网卡”接口。
- 数据包必须是从一块网卡,传送到另一块网卡的。通过网卡能够使不同的计算机之间连接,从而完成数据通信等功能。
- 网卡的地址----MAC地址,就是数据包的物理发送地址和物理接受地址。
- 网卡 就是有标识的 ==》》网卡地址 《《==》》 物理地址 MAC地址
- 网络层
链路层主要处理 mac 地址,网络层主要处理 ip 地址。
mac:物理地址 (理论上是唯一的) ip:逻辑地址 (网段地址,可以改变的)
- 如果只有一层链路层的话,通信仅通过mac地址的话,最终会导致广播风暴。
- 因此,会加上网络层,引入 ip 地址,将 mac 地址分为不同的组,每一组就是一个网段,也就是 ip 地址,避免了广播风暴问题。
*ARP:通过 ip 找 mac (ARP是在链路层)
- 传输层
传输层:端口号,目的端口号
端口号, 是网络程序的一个编号
传输层是进程到进程的
- 应用层
网络通信条件:
1)网卡,mac地址(不需要用户处理) ARP ===》 通过ip找mac
2)逻辑地址,ip地址(需要用户指定) ====》为了确定哪个电脑接受
3) 端口 ===》为了确定哪个程序接受
同一个系统,一个程序只能绑定一个端口。
不同系统,同意端口对应的程序可能不一样。
通信过程的组包和拆包
2.Socket编程
- Socket 也是一种文件描述符。
- Socket 起源于 Unix,而 Unix 基本哲学之一就是“一切皆文件”,都可以用“打开open-> 读写 write/read -> 关闭 close” 模式来操作,Socket 就是该模式的一个实现,网络的Socket数据传输是一种特殊的 I/O。
- Socket 也只有一个类似于打开文件的函数调用:Socket( ), 该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过 Socket 实现的。
2.1 Socket类型
常见的 Socket 类型有两种:流式 Socket ( SOCK_STREAM )
和 数据报式 Socket ( SOCK_DGRAM )
。 流式是一种面向连接的 Socket, 针对于面向连接的 TCP 服务应用;数据报式 Socket 是一种无连接的 Socket, 对应于无连接的 UDP服务应用。
2.2 TCP的C/S架构
cs模型:客户端和服务器*
客户端 ===》 客户
1)主动请求服务
服务器 ===》 客服
1)被动提供服务
bs模型:浏览器和服务器
browser/server
2.3 示例程序代码
TCP服务器示例代码:
package main
import (
"fmt"
"net"
)
func main( ) {
//监听
listen, err := net.Listen("tcp", "127.0.0.1 : 8000" ) // 如果是本地的ip地址 则可以不用写 直接省略掉 即 " : 8000"
if err != nil {
fmt.Println("err = ", err)
return
}
defer listen.Close( ) //最后程序结束后才执行listen.Close( )进行关闭
//阻塞等待用户链接
conn , err := listen.Accept( )
if err != nil {
fmt.Println("err = ", err)
return
}
//接受用户的请求
buf := make( [ ]byte, 1024) //创建切片缓冲区,其大小为1024
n, err1 := conn.Read(buf )
if err1 != nil {
fmt.Println("err = ", err)
return
}
fmt.Println("buf = " , string(buf[:n])) //指定打印的切片,即读了多少就打印多少
defer conn.Close( ) //关闭当前用户链接
}
TCP客户端的示例代码:
package main
import (
"fmt"
"net"
)
func main( ) {
//主动连接服务器
conn, err := net.Dial ("tcp", "127.0.0.1 : 8000" ) //服务器的ip地址和端口
if err != nil {
fmt.Println ( " err = " , err)
return
}
defer conn.Close( )
//发送数据
conn.Write([ ]byte ("are you ok")
}
执行结果截图:
简单版并发服务器
TCP服务器示例代码:
package main
import (
"fmt"
"net"
"strings" //大写转小写函数用到的包
)
//处理用户请求
func HandleConn(conn net.Conn) { //conn 是 net.Conn 类型
//函数调用完毕,自动关闭conn
defer conn.Close( )
//获取客户端的网络地址信息
addr := conn.RemoteAddr( ).String( ) // .String( ) 转换成字符串
fmt.Println(addr, " connect sucessful" )
buf := make([ ]byte, 2048)
for{
//读取用户数据
n, err := conn.Read(buf)
if err != nil {
fmt.Println("err = ", err)
return
}
fmt.Printf("read buf = %s\n", string(buf[:n])) //把缓冲区的内容转换成string类型后打印
// fmt.Println("len = " , len(string(buf[:n])))
if "exit" == string(buf[:n - 1] { //如果输入 exit 则退出连接
fmt.Println(addr, "exit")
return
}
//把数据转换为大写,再给用户发送
conn.Write([ ]byte( strings.ToUpper( string(buf[:n]) ) ))
}
}
func main( ) {
//监听
listen, err := net.Listen("tcp", "127.0.0.1 : 8000" ) // 如果是本地的ip地址 则可以不用写 直接省略掉 即 " : 8000"
if err != nil {
fmt.Println("err = ", err)
return
}
defer listen.Close( ) //最后程序结束后才执行listen.Close( )进行关闭
//接受多个用户
for {
conn, err := listener.Accept( )
if err != nil {
fmt.Println("err = " , err)
return
}
//处理用户请求。每来一个用户就新建一个 go 协程
go HandleConn(conn)
执行结果截图:
客户端即可输入也可接受服务器回复的示例代码
package main
import (
"fmt"
"net"
"os"
)
func main( ) {
//主动连接服务器
conn, err := net.Dial("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("net.Dial err = ", err)
return
}
//main调用完毕,关闭连接
defer conn.Close( )
//接受服务器回复的数据,新任务
go func( ) {
//从键盘输入内容,给服务器发送内容
str := make([ ] byte, 1024)
for {
n, err := os.Stdin.Read(str) //从键盘上读取内容,并存在str中
if err != nil {
fmt.Println("conn.Read err = ", err)
return
}
//把输入的内容给服务器发送
conn.Write(str[:n])
}
} ( )
//切片缓冲
buf := make([ ]byte, 1024)
for {
n, err := conn.Read(buf) //接受服务器的请求
if err != nil {
fmt.Println("conn.Read err = ", err)
return
}
fmt.Println(string(buf[:n])) //打印接收到的内容,转换为字符串再打印
}
}
TCP服务器示例代码见上
// if "exit" == string(buf[:n - 1] { //如果输入 exit 则退出连接 !!!nc测试
if "exit" == string(buf[:n - 2] { //自己写的客户端测试。发送时,多了2个字符。"\r\n"
fmt.Println(addr, "exit")
return
}
执行结果截图:
3.HTTP编程
3.1 概述
3.1.1 Web工作方式
平时浏览网页的时候,会打开浏览器。输入网址后按下回车键,然后就会显示出你想要浏览的内容。其背后的过程可以简单概述为:
对于普通的上网过程,系统其实是这样做的:浏览器本身就是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP,然后通过IP地址找到IP对应的服务器后,要求建立TCP连接,等浏览器发送完HTTP Request(请求)包后,服务器接受到请求包之后才开始处理请求包,服务器调用自身服务,返回HTTP Response(响应)包:客户端收到来自服务器的响应后开始渲染这个Response包里的主体(body),等收到全部的内容随后断开与该服务器之间的TCP连接。
一个Web服务器也称为HTTP服务器,它通过HTTP协议与客户端通信。
这个客户端通常指的是Web浏览器(其实手机端客户端内部也是浏览器实现的)。
3.1.2 HTTP协议
超文本传输协议(HTTP,HyperText Transfer Protocol)
它详细规定了浏览器和万维网服务器之间互相通信的规则
,通过因特网传送万维网文档的数据传送协议。
HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。如下图所示:
3.1.3 地址(URL)
- URL全程为Unique Resource Location,用来表示网络资源,可以理解为网络文件路径。
- URL的长度有限制,不同的服务器的限制值不太相同,但是不能无限长。
URL的格式如下:
https://www.baidu.com/
3.2 HTTP报文浅析
执行结果截图:
- 请求报文格式说明
HTTP请求报文由请求行、请求头部、空行、请求包体 4个部分组成,如下图所示。
(1)请求行
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。
常用的HTTP协议的请求方法有GET、POST。
GET:
- 当客户端从服务器中读取某个资源时,使用GET方法。GET方法要求服务器将URL定位的资源放在
响应报文的数据部分
,回送给客户端,即向服务器请求某个资源。- 使用GET方法时,请求参数和对应的值附加在URL后面,利用一个问号("?")代表URL的结尾与请求参数的开始,传递参数长度受限制,因此GET方法不适合用于上传数据。
- 通过GET方法来获取网页时,参数会显示在浏览器地址栏上,因此保密性很差。
POST:- 当客户端给服务端提供信息较多时可以用POST方法,POST方法向服务器提交数据,比如完成表单数据的提交,将数据提交给服务器处理。
- GET一般用于获取/查询资源信息,POST会附带用户数据,一般用于更新资源信息。POST方法将请求参数封装在HTTP请求数据中,而且长度没有限制,因为POST携带的数据,在HTTP的请求正文中,以名称/值的形式出现,可以传输大量数据。
(2)请求头
请求头部为请求报文添加了一些附加信息,由”名/值“( 即 ”key/value" )对组成,每一行一对,名和值之间使用冒号分隔。
3.3 响应报文测试方法
响应报文格式分析
3.4 HTTP服务器编程
http服务器获取客户端的一些信息