传输模型
基本模型
层次划分
网络协议
网络协议是不同计算机间为了进行数据交换而建立的规则,是通信计算机双方必须共同遵从的一组约定。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流。
基本模型
注意点
- 我们在程序里面能操作最低的层次就是传输层
- 互联网数据传输主要用两个协议:TCP和UDP
- HTTP协议不涉及数据包的传输,仅仅规定了客户端和服务器之间的通信格式
- HTTP协议是应用层的协议,是基于传输层的TCP协议来工作的
TCP连接
建立连接(三次握手)
传输数据
断开连接(四次挥手)
IP地址和端口
IP地址
所谓的IP地址就是给每个连接互联网上的主机分配一串数字当作地址,按照TCP/IP规定,每个IP地址长32bit,分为4段,用十进制数字表示,段与段之间用点号隔开。
IP地址 = 网络号+主机号, 通过网络号的不同分为了五大类IP地址,
这是一个历史的概念,在实际中已经没有意义了,我们了解一下就好
私有(内网)IP地址
- IPv4地址协议中预留了3个IP地址段,作为私有地址,供内部组网使用;
- 私有地址可以自己组网时用,但不能在外网上用;
-
这些地址的计算机要上网必须转换成为合法的IP地址,也就是公网地址;
端口
- 端口用来唯一标识应用程序
- 每个应用程序都占了一个端口,一个端口同时只能被一个程序使用
-
共有65535个端口
套接字socket
socket是计算机网络通信的基本的技术之一。如今大多数基于网络的软件,如浏览器,即时通讯工具甚至是P2P下载都是基于Socket实现的。�
网络上两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket,应用程序通过socket向网络中发出消息,或者接收消息。
socket是应用层与TCP/IP协议族的中间抽象层,它是一组接口,把复杂的TCP/IP协议族封装为几个简单的接口提供给应用层调用,实现程序在网络中的通信
socket仅仅是一个调用接口,为了方便程序员针对TCP或者UDP编程的接口。
socket
socket七个方法
注意:
- 服务端是通过accept生成的对等连接套接字来和客户端通信的
- accept和recv是会阻塞的,当没有数据来的时候阻塞等数据过来。
服务端通过accept接受客户端连接请求,没有请求则处于阻塞状态,知道客户端通过connect来连接。recv也是阻塞直到客户端send数据过来。
代码示例
- 写一个不断接受(while True)用户连接的服务端,它的作用是把客户端发来的数据原封不动的返回。写一个客户端来测试上面的服务端。
服务端:
import socket
server_socket = socket.socket() # 创建一个套接字,默认使用IPV4和TCP
server_socket.bind(('0.0.0.0', 7001)) # 绑定ip和端口(注意这里是元组)到套接字,4个0表示任何地址可以访问
server_socket.listen(5) # 服务器监听客户端请求,将服务器套接字变为被动套接字监听模式。不能再发送数据了,由后面产生新的套接字进行数据交互。
print('等待客户端的到来.....')
new_client_socket, address= server.accept() # 接受客户端连接,程序会默认进入阻塞状态(等待客户端连接),如果客户端连接后,自动解除阻塞,会自动向下继续执行。
# 会返回形如(new_client_socket, address)的元组,new_client_socket新的套接字对象,用于与相应客户端端进行数据交互,只服务当前客户端,address客户端地址((hostname,port)的元组),
print('来自{}的请求'.format(address))
while True:
data = new_client_socket.recv(1024) # 接受数据,返回的是字节码,1024个字符,recv会让程序再次阻塞,收到消息后再解除阻塞。
print('接受的数据:{}'.format(data.decode())) # 进行解码
if not data: #如果客户端断开了,则会不断的循环,所以需要判断处理
print('data is lose')
new_client_socket.close() # 关闭和当前客户端的连接
break
new_client_socket.send(data) # 发送数据,要求以字节码格式,可以用b'string'或者encode()对字符串进行编码
server_socket.close() # 服务器不再接受新的客户端连接,老客户端可以继续服务。
客户端:
import socket
client = socket.socket()
client.connect(('127.0.0.1', 7001))
while True:
message = input('请输入内容>>').strip() # 去掉前后空格
if len(message) == 0:
continue
client.send(message.encode()) # 需要发送字节
data = client.recv(1024) # 1024个字符
print(data.decode()) # 接受的是字节,需要解码成字符串
client.close()
总结:这是一个能一直保持连接的程序,因为上面的步骤只是最基本的用法,所以想要能一直连接的程序,就需要使用while True保持循环,还有一个问题就是,此程序只能连接一个客户端,并不能支持多并发,所以当出现第二个客户端时,就会出现挂起不动,所以想要完成多个连接,下篇涉及到。
通过TCP实现文件下载
服务器
# 1. 导入模块
import socket
# 2. 创建socket
tcp_server_socket = socket.socket()
# 3. 绑定端口
tcp_server_socket.bind(("0.0.0.0", 8004))
# 4. 设置监听,设置套接字由主动变为被动
tcp_server_socket.listen(128)
print('开始监听....')
# 5. 接受客户端连接
new_client_socket, address = tcp_server_socket.accept()
print('欢迎新客户端:', address)
# 6. 接收客户端发送的文件名
file_name = new_client_socket.recv(1024).decode()
print('文件名:{}'.format(file_name))
# 7. 根据文件名读取文件内容
try:
with open(file_name, 'rb') as f: # 发送也是需要按字节码形式,所以这里用rb
# 8. 把读取的文件内容发送给客户端(循环)
while True:
file_data = f.read(1024)
# 判断是否读取到文件末尾
if file_data:
new_client_socket.send(file_data)
else:
break
except Exception as e:
print('文件%s下载失败'%file_name)
else:
print('文件%s下载成功' %file_name)
# 9. 关闭和当前客户的连接
new_client_socket.close()
# 10. 关闭服务器
tcp_server_socket.close()
客户端
# 导入模块
import socket
# 2. 创建套接字
tcp_client_socket = socket.socket()
# 3. 建立连接
tcp_client_socket.connect(('127.0.0.1', 8004))
# 4.接受用户输入的文件名
file_name = input('请输入要下载的文件名:\n')
# 5.发送文件名到服务器
tcp_client_socket.send(file_name.encode())
# 6. 创建文件,并且准备保存
with open('download/'+ file_name, 'wb') as f:
# 7. 接受服务端发送的数据,保存到本地(循环)
while True:
file_data = tcp_client_socket.recv(1024)
# 判断数据是否传送完毕
if file_data:
f.write(file_data)
else:
break
# 8. 关闭套接字
tcp_client_socket.close()