Day29 网络基础

一、 C/S模型

1. 客户端(Client)/服务器(Server)架构

  • 硬件C/S架构:如打印机
  • 软件C/S架构:如Web

2. OSI七层协议

 网络通信就是两个进程之间在通信

为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,为了实现互联网这个目标,互联网协议簇(Internet Protocol Suite)就是通用协议标准。Internet是由inter和net两个单词组合起来的,原意就是连接“网络”的网络,有了Internet,任何私有网络,只要支持这个协议,就可以联入互联网。

二、Socket

应用程序比如浏览器、电子邮件、文件传输服务器等产生的数据,会通过传输层协议进行传输,而应用程序是不会和传输层直接建立联系的,而是有一个能够连接应用层和传输层之间的套件,这个套件就是SocketSocket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。

在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

 

套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。

套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。

①基于文件类型的套接字家族:AF_UNIX

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

②基于网络类型的套接字家族:AF_INET

python支持很多种地址家族,AF_INET是使用最广泛的一个,而且由于我们只关心网络编程,所以大部分时候我们只使用AF_INET。还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,它们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现。

socket工作流程

  1. 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。
  2. 在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。
  3. 客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

端口号指的是服务器端的端口号,因为作为服务器,提供什么样的服务,端口号就必须固定下来。由于我们想要访问网页,因此提供网页服务的服务器必须把端口号固定在80端口,因为80端口是Web服务的标准端口。其他服务都有对应的标准端口号,例如SMTP服务是25端口,FTP服务是21端口,等等。

端口号小于1024的是Internet标准服务的端口,端口号大于1024的,可以任意使用。

三、基于TCP的套接字

tcp是基于连接的,必须先启动服务端,然后再启动客户端去链接服务端。创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器。

tcp服务端

ss = socket() #创建服务器套接字
ss.bind()      #把地址绑定到套接字
ss.listen()      #监听链接
inf_loop:      #服务器无限循环
    cs = ss.accept() #接受客户端链接
    comm_loop:         #通讯循环
        cs.recv()/cs.send() #对话(接收与发送)
    cs.close()    #关闭客户端套接字
ss.close()        #关闭服务器套接字(可选)

tcp客户端

cs = socket()    # 创建客户套接字
cs.connect()    # 尝试连接服务器
comm_loop:        # 通讯循环
    cs.send()/cs.recv()    # 对话(发送/接收)
cs.close()            # 关闭客户套接字

socket通信流程与打电话流程类似,我们就以打电话为例来实现一个low版的套接字通信

服务器端

import socket

# 监听本机网卡,端口
# 绑定环回地址,客户端必须同时在本机运行才能连接
ip_port = ('127.0.0.1', 9002)
# 收发消息长度
BUFSIZE = 1024

# 获取TCP套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 把地址绑定到套接字
s.bind(ip_port)
# 监听链接
s.listen(5)

# 接收客户端链接, 返回元组类型数据
conn, addr = s.accept()
print('接到client %s 的请求' %addr[0])

# 接收客户端消息
msg = conn.recv(BUFSIZE)
print(msg, type(msg))

replyment = 'OK'
# 发送给客户端消息
conn.send(replyment.encode('utf-8'))

# 关闭客户端套接字
conn.close()

# 关闭服务器套接字
s.close()

socket.listen(backlog)函数用于设置服务器套接字(socket)的监听队列大小。参数backlog表示等待被接受的连接的最大数量(正整数)。当服务器套接字调用socket.listen()后,它会开始监听指定的端口,并将接收到的连接请求排入队列等待处理。如果连接请求超过了backlog指定的数量,多余的连接请求会被服务器忽略或拒绝。

客户端

import socket

# 监听本机网卡,端口
ip_port = ('127.0.0.1', 9002)
# 收发消息长度
BUFSIZE = 1024

# 获取客户套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 尝试链接服务器
s.connect_ex(ip_port)

msg = "say OK"
# 发送消息给服务端
s.send(msg.encode('utf-8'))

feedback = s.recv(BUFSIZE)
# 接收服务端消息
print(feedback.decode('utf-8'))

# 关闭客户端套接字
s.close()


有时候在突然关闭了连接后,又突然进行连接可能会出现一些端口冲突问题,这是因为TCP协议本身的问题。

在关闭(重启)服务的时候,绑定的端口并不是马上释放,而是进入TIME_WAIT状态,并等待一段时间(2MSL)才释放。正常主机用户这样只要换个端口就可以继续使用,但在服务器上就必须快速回收和重用这些TIME_WAIT资源

TCP三次握手

 

在连接过程实际完成客户端到服务端的连接和服务端到客户端的连接

客户端首先发送连接申请

服务端给予确认,同时发送自己的连接申请

客户端给予确认,开始发生数据

第二步中可以实际是两次操作,之所以可以合并在一起,是因为此时并没有数据进行传输

第三步的时候,若客户端不给予确认,并不断重新发起连接申请,则会造成服务端不断超时重发确认和连接报文,造成资源浪费,导致正常服务不能进行,这就是SYN泛洪攻击

TCP四次挥手

 

在释放过程同样需要释放客户端到服务端的连接和服务端到客户端的连接

客户端发起断开申请

服务端给予确认,因为服务端仍有可能需要发送数据,所以并没有一起发生断开申请

在没有数据要发送后,服务端发送断开申请

客户端给予确认,并等待2MSL时间,之后都处于CLOSED状态

为什么必须需要等待2MSL时间

  1. 最后一个ACK确认报文可能会丢失,服务端在等待超时后会重传之前的断开申请,而客户端已经释放了连接则不会再发送一次确认报文,服务端始终不能正常处于CLOSED状态
  2. MSL(Maximum Segment Lifetime)的时间大于等于网络中最长生存时间,即一个报文段在网络中存在的最长时间。在正常情况下,旧的报文段会在2MSL的时间内被丢弃,从而确保不会对新的连接造成混淆。

解决方法:重用ip和端口、降低TIMEOUT时间、快速回收处于TIME-WAIT的socket等

phone=socket(AF_INET,SOCK_STREAM)

# 允许在关闭套接字后立即重新使用相同的地址 
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) 
phone.bind(('127.0.0.1',8080))

为了确保在绑定新的套接字之前可以重用地址,需要在socket.bind()之前设置socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)选项。这样可以避免由于地址被占用而导致绑定失败的情况发生。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值