Day 26 互联网协议/Socket套接字

拜拜、爱过 提交于 2019-12-01 16:32:35

软件开发架构

C/S架构

Client:客户端

Server:服务端

  • 优点:

    软件的使用稳定,并且可以节省网络资源

  • 缺点

    1. 若用户想在同一设备上使用多个软件,必须下载多个客户端
    2. 软件的一次更新,客户端也必须跟着重新下载更新

B/S架构

Browser:浏览器

Server:服务端

  • 优点:

    以浏览器充当客户端,无需用户下载多个软件,也无需用户下载更新软件版本,直接在浏览器上访问需要使用的软件

  • 缺点

    消耗网络资源较大,当网络不稳定时,软件的使用也不稳定

互联网协议

互联网协议又称为网络七层协议,OSI七层协议,OSI是一个世界标准组织

OSI七层协议:

  • 应用层
  • 表示层
  • 会话层
  • 传输层
  • 网络层
  • 数据链路层
  • 物理连接层

物理层

基于电信号的高低电压发送01二进制数据

数据链路层

数据链路层定义了对电信号的分组方式

以太网协议:

  1. 统一了标准的定义方式

  2. 每一台连接网线的电脑都有一块网卡

    网卡由不同的厂商生产

    每块网卡都有一个唯一的12位编号:mac地址

    ​ 前6位: 厂商号

    ​ 后6位: 流水号

交换机:

​ 可以让多台电脑连接到一起

基于以太网协议发送数据:

​ 特点:广播,单播

​ 弊端:广播风暴,不能跨局域网通信

网络层

如果全世界都光靠数据链路层中的以太网协议,mac地址,广播来通信是不够的,因为一台机器发送信息的话,全世界的机器都将收到,这将会是一场灾难

所以就有了网络层,网络层定义了一个IP协议,IP协议将不同的局域网都分割开来

Mac地址是用来标识你这个教室的一个位置(一台电脑),IP地址是用来标识你在哪个教室(哪个局域网)

局域网中怎么获取对方的Mac地址:

肯定要知道对方的IP地址,这是最基本的,就像你要访问百度,肯定得知道百度的域名,域名就是百度的IP地址。自己的IP可以轻松获得,自己的Mac也轻松获取,目标Mac为12个F,我们叫广播地址,表达的意思是我想要获取这个目标IP地址172.16.10.11的机器的Mac地址。Mac为12个F代表的是一种功能,这个功能就是获取对方的Mac地址,计算机的Mac永远不可能是12个F。假设是在本教室广播,一嗓子吼出去了,所有人开始解包,只有IP地址是172.16.10.11的这个人才会返回他的Mac地址,其他人全部丢弃。发回来源Mac改成飞哥自己的Mac地址,同时把飞哥的Mac地址放在数据部分。

跨网络怎么获取对方的Mac地址:

通过IP地址区分,计算机运算判断出飞哥不在同一个教室,目标IP就变成了网关的IP了。网关的IP在计算机上配死了,可以轻松获取。

IP协议

规定网络地址的协议叫IP协议,它定义的地址称之为IP地址,广泛采用IPv4版本,它规定的网络地址由32位2进制表示,范围0.0.0.0 - 255.255.255.255, 还有一种叫IPv6版本的

本机IP: 回环地址 127.0.0.1 ---> localhost

arp协议

arp协议由来:计算机通信基本靠吼,即广播的方式,所有上层的包到最后都要封装上以太网头,然后通过以太网协议发送,在谈及以太网协议时候,我门了解到:通信是基于Mac的广播方式实现,计算机在发包时,获取自身的Mac是容易的,如何获取目标主机的Mac,就需要通过arp协议

arp协议功能:广播的方式发送数据包,获取目标主机的Mac地址

协议工作方式:每台主机IP都是已知的,例如:主机172.16.10.10/24访问172.16.10.11/24

传输层

IP地址帮助我们找到区分子网,Mac地址帮我们找到主机,但是主机上打开着许多个应用程序

那么我们如何标识一台计算机上的应用程序呢,那就是传输层中的端口,端口即应用程序与网卡关联的编号

传输层的功能就是建立端口到端口的通信

端口的范围是0-65535,其中0-1023为系统占用端口

开发中常用软件的默认端口号:

mysql:3306

mongodb:27017

Django:8000

Tomcat:8080

Flask:5000

Redis:6379

TCP协议

可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割

三次握手

第一次握手:

起初两端都处于CLOSED关闭状态,Client将标志位SYN置为1,随机产生一个值seq=x,并将该数据包发送给Server,Client进入SYN-SENT状态,等待Server确认

第二次握手:

Server收到数据包后由标志位SYN=1得知Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给Client以确认连接请求,Server进入SYN-RCVD状态,此时操作系统为该TCP连接分配TCP缓存和变量

第三次握手:

Client收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并且此时操作系统为该TCP连接分配TCP缓存和变量,并将该数据包发送给Server,Server检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client和Server就可以开始传输数据

(1)为什么A还要发送一次确认呢?可以二次握手吗?

主要为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。如A发出连接请求,但因连接请求报文丢失而未收到确认,于是A再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,A工发出了两个连接请求报文段,其中第一个丢失,第二个到达了B,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达B,此时B误认为A又发出一次新的连接请求,于是就向A发出确认报文段,同意建立连接,不采用三次握手,只要B发出确认,就建立新的连接了,此时A不理睬B的确认且不发送数据,则B一致等待A发送数据,浪费资源

(2) Server端易受到SYN攻击?

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击,SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪

防范SYN攻击措施:降低主机的等待时间使主机尽快的释放半连接的占用,短时间受到某IP的重复SYN则丢弃后续请求

四次挥手

第一次挥手:

A的应用进程先向其TCP发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN-WAIT-1(终止等待1)状态,等待B的确认

第二次挥手:

B收到连接释放报文段后即发出确认报文段,(ACK=1,确认号ack=u+1,序号seq=v),B进入CLOSE-WAIT(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放

A收到B的确认后,进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段

第三次挥手:

B没有要向A发出的数据,B发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),B进入LAST-ACK(最后确认)状态,等待A的确认

第四次挥手:

A收到B的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),A进入TIME-WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,A才进入CLOSED状态

(1)为什么连接的时候是三次握手,关闭的时候却是四次握手?

因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手

(2)为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文

Socket套接字

多个客户端:

# server端
import socket
server = socket.socket()
server.bind(
    ('127.0.0.1', 8888)
)
server.listen(5)  # 半连接池,可以“等待5个用户接受服务”

while True:
    # 等待客户端连接过来
    conn, addr = server.accept()
    print(addr)

    # 循环接收客户端数据
    while True:
        try:
            data = conn.recv(1024).decode('utf-8')
            print(data)

            # mac和linux系统bug: b''
            if len(data) == 0:
                continue

            if data == 'q':
                break

            conn.send(data.encode('utf-8'))

        except Exception as e:
            print(e)
            break

    conn.close()
# client端
import socket

client = socket.socket()

client.connect(
    ('127.0.0.1', 8888)
)

while True:
    send_msg = input('client---》server:')

    client.send(send_msg.encode('utf-8'))

    if send_msg == 'q':
        break

    data = client.recv(1024).decode('utf-8')
    print(data)

client.close()
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!