记录一些常用简单的网络知识,长时间不用会忘记的( •́ .̫ •̀ )

具体可以阅读 TCP/IP详解 卷1

主要学习 Socket、Socket(Go) 示例以及心跳包。

TCP/IP 模型

四层模型是 TCP/IP 技术的实际模型,七层模型是标准化组织制订的理论规范,两者有如上图的对应关系。人们很少用到七层模型,一般常见的地方在负载均衡时:四层负载均衡和七层负载均衡,分别指的是在 TCP 和 HTTP 层面进行负载均衡。

TCP 和 UDP 是两种最为著名的运输层协议,二者都使用 IP 作为网络层协议。虽然 TCP 使用不可靠的 IP 服务,但它却提供一种可靠的运输层服务。但是与 TCP 不同的是,UDP 是不可靠的,它不能保证数据报能安全无误地到达最终目的。IP 是网络层上的主要协议,同时被 TCP 和 UDP 使用。TCP 和 UDP 的每组数据都通过端系统和每个中间路由器中的 IP 层在互联网中进行传输。

UDP 协议

UDP 是一个简单的面向数据报的运输层协议。不提供可靠性:它把应用程序传给 IP 层的数据发送出去,但是并不保证它们能到达 目的地。 应用程序必须关心 IP 数据报的长度。如果它超过网络的 MTU(最大传输单元),那么就要对 IP 数据报进行分片。如果需要,源端到目的端之间的每个网络都要进行分片,并不只是发送端主机连接第一个网络才这样做。

TCP 协议

TCP 提供一种面向连接的、可靠的字节流服务,面向连接意味着两个使用 TCP 的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个 TCP 连接。这一过程与打电话很相似,先拨号振铃,等待对方摘机说“喂”,然后才说明是谁。

以太网数据帧和 IP 数据包都只是简单地规定了头部应该如何携带信息,而以太网帧并不保证能够送达,也不能保证按照顺序送达,出现了可靠性问题。

TCP 将在两个应用程序之间建立一个全双工 (full-duplex) 的通信。 这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。

TCP 非常复杂,只记录常用的知识点

三次握手

  • 第一次握手:客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
  • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去

四次挥手

  • 第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可以接受数据。
  • 第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
  • 第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
  • 第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

TCP的四次挥手过程(简言之):主动关闭方向被动关闭方发送不会再给你发数据了的信息;被动关闭方对收到的主动关闭方的报文段进行确认;被动关闭方向主动关闭方发送我也不会再给你发数据了的信息;主动关闭方再次对被动关闭方的确认进行确认。

TLS

传输层安全性协议(Transport Layer Security,缩写作 TLS)及其前身安全套接层(Secure Sockets Layer,缩写作 SSL)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。

SSL包含记录层(Record Layer)和传输层,记录层协议确定传输层数据的封装格式。传输层安全协议使用X.509认证,之后利用非对称加密演算来对通信方做身份认证,之后交换对称密钥作为会谈密钥(Session key)。这个会谈密钥是用来将通信两方交换的数据做加密,保证两个应用间通信的保密性和可靠性,使客户与服务器应用之间的通信不被攻击者窃听。

TLS协议的优势是与高层的应用层协议(如HTTP、FTP、Telnet等)无耦合。应用层协议能透明地运行在TLS协议之上,由TLS协议进行创建加密通道需要的协商和认证。应用层协议传送的数据在通过TLS协议时都会被加密,从而保证通信的私密性。

  • 当客户端连接到支持TLS协议的服务器要求创建安全连接并列出了受支持的密码组合(加密密码算法和加密哈希函数),握手开始。
  • 服务器从该列表中决定加密和散列函数,并通知客户端。
  • 服务器发回其数字证书,此证书通常包含服务器的名称、受信任的证书颁发机构(CA)和服务器的公钥。
  • 客户端确认其颁发的证书的有效性。
  • 为了生成会话密钥用于安全连接,客户端使用服务器的公钥加密随机生成的密钥,并将其发送到服务器,只有服务器才能使用自己的私钥解密。
  • 利用随机数,双方生成用于加密和解密的对称密钥。

这就是TLS协议的握手,握手完毕后的连接是安全的,直到连接(被)关闭。如果上述任何一个步骤失败,TLS握手过程就会失败,并且断开所有的连接。

通过非对称加密算法实现握手,交换随机数,通过随机数生成对称加密算法的对称秘钥,通过对称秘钥加密传输的数据。

时间事件
1994年NetScape公司设计了SSL协议(Secure Sockets Layer)的1.0版,但是未发布
1995年NetScape公司发布SSL 2.0版,很快发现有严重漏洞
1996年SSL 3.0版问世,得到大规模应用
1999年互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版
2006年和2008年TLS进行了两次升级,分别为TLS 1.1版和TLS 1.2版。最新的变动是2011年TLS 1.2的修订版
2018年8月TLS 1.3在 RFC 8446 中定义

X.509

X.509 是密码学里公钥证书的格式标准。X.509证书里含有公钥、身份信息(比如网络主机名,组织的名称或个体名称等)和签名信息(可以是证书签发机构CA的签名,也可以是自签名。

另外除了证书本身功能,X.509还附带了证书吊销列表和用于从最终对证书进行签名的证书签发机构直到最终可信点为止的证书合法性验证算法。

X.509是ITU-T标准化部门基于他们之前的ASN.1定义的一套证书标准。

X.509有多种常用的扩展名。不过其中的一些还用于其它用途,就是说具有这个扩展名的文件可能并不是证书,比如说可能只是保存了私钥。

  • .pem – (隐私增强型电子邮件) DER编码的证书再进行Base64编码的数据存放在”—–BEGIN CERTIFICATE—–“和”—–END CERTIFICATE—–“之中
  • .cer, .crt, .der – 通常是DER二进制格式的,但Base64编码后也很常见。
  • .p7b, .p7c – PKCS#7 SignedData structure without data, just certificate(s) or CRL(s)
  • .p12 – PKCS#12格式,包含证书的同时可能还有带密码保护的私钥
  • .pfx – PFX,PKCS#12之前的格式(通常用PKCS#12格式,比如那些由IIS产生的PFX文件)

PKCS#7 是签名或加密数据的格式标准,官方称之为容器。由于证书是可验真的签名数据,所以可以用SignedData结构表述。 .P7C文件是退化的SignedData结构,没有包括签名的数据。

PKCS#12 由PFX进化而来的用于交换公共的和私有的对象的标准格式。

Socket

本质是编程接口(API),对 TCP/IP 的封装,TCP/IP 也要提供可供程序员做网络开发所用的接口,这就是 Socket 编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket 是发动机,提供了网络通信的能力。

golang 代码示例

关键代码

// server.go
netListen, err := net.Listen("tcp", "localhost:1024") 
conn, err := netListen.Accept() 
n, err := conn.Read(buffer)  
// client.go
tcpAddr, err := net.ResolveTCPAddr("tcp4", server) 
conn, err := net.DialTCP("tcp", nil, tcpAddr)  
conn.Write([]byte(words))

Server

package main  
import (  
    "fmt"  
    "net"  
    "log"  
    "os"  
)  
  
func main() {  
//建立socket,监听端口  
    netListen, err := net.Listen("tcp", "localhost:1024")  
    CheckError(err)  
    defer netListen.Close()  
    Log("Waiting for clients")  
    for {  
        conn, err := netListen.Accept()  
        if err != nil {  
            continue  
        }  
        Log(conn.RemoteAddr().String(), " tcp connect success")  
        handleConnection(conn)  
    }  
}  
//处理连接  
func handleConnection(conn net.Conn) {  
    buffer := make([]byte, 2048)  
    for {  
        n, err := conn.Read(buffer)  
        if err != nil {  
            Log(conn.RemoteAddr().String(), " connection error: ", err)  
            return  
        }  
        Log(conn.RemoteAddr().String(), "receive data string:\n", string(buffer[:n]))  
    }  
}  
func Log(v ...interface{}) {  
    log.Println(v...)  
}  
func CheckError(err error) {  
    if err != nil {  
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())  
        os.Exit(1)  
    }  
}  

Client

package main  
import (  
    "fmt"  
    "net"  
    "os"  
)  
  
func sender(conn net.Conn) {  
        words := "hello world!"  
        conn.Write([]byte(words))  
    fmt.Println("send over")  
}  
func main() {  
    server := "127.0.0.1:1024"  
    tcpAddr, err := net.ResolveTCPAddr("tcp4", server)  
    if err != nil {  
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())  
        os.Exit(1)  
    }  
  
    conn, err := net.DialTCP("tcp", nil, tcpAddr)  
    if err != nil {  
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())  
        os.Exit(1)  
    }  
    fmt.Println("connect success")  
    sender(conn)  
} 

心跳包

在 TCP 的机制里面,本身是存在有心跳包的机制的,也就是 TCP 的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。

所以需要写一个应用层上的心跳包,通过每间隔一定时间发送心跳数据,来检测对方是否连接。不停的 conn.Write()conn.Read()

参考链接