网络协议 osi七层协议
1 物理层:(光纤 双绞线)
主要基于电气特性发送高低电压(电信号) 高电压对应1 低电压对应0
2 数据链路层
定义电信号的分组方式
以太网协议:
一组电信号组成一个数据包 叫做'帧'
每一个数据帧由 报头head和数据data两部分组成
head(固定18个字节)
发送者源地址 6个字节
接收者源地址 6个字节
数据类型 6个字节
data(最短46个字节 最长1500个字节)
数据包的具体内容
head+data [64,1518] 字节 如果超过最大限制就分片发送
mac协议:
head中发送者源地址以及接收者源地址就是mac地址
mac地址 长度为48位2进制 通常由12位16进制数表示 (前六位是厂商编号 后六位是流水线号)
广播:
有了mac地址 同一网络下的两台主机就可以通信(一台主机通过arp协议获取另一台主机的mac地址)
以太网通过最原始的方式 广播进行通信
3 网络层
功能: 引入一套新的地址来区分不同的广播域/子网 也就是网络地址
IP协议:
固定网络地址的协议叫做ip协议 定义的地址叫做 ip地址 v4版本 ipv4 规定网络地址由32位2进制表示
范围 0.0.0.0-255.255.255.255
一个ip地址通常写成四段十进制数 192.168.1.1
IP地址:
网络部分: 标识子网
主机部分: 标识主机
子网掩码:
表示子网络特征的一个参数 形式上等同于ip地址 也是一个32位的2进制数字 网络部分全是1 主机部分全是0
例如 172.16.10.1 网络部分是前24位 主机部分是后8位
子网掩码是 11111111.11111111.11111111.00000000 十进制就是 255.255.255.0
判断两个ip地址是否处在同一个子网络:
方法是将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,否则为0),然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否则就不是。
IP协议的作用:
1 为每台计算机分配IP地址
2 确定那些地址在同一个子网络
IP数据包 直接放入以太网协议的 data部分 [[以太网头][IP头][IP数据]]
1 head 长度20到60字节
2 data 最长为 65515字节
ARP协议
功能: 广播的形式发送数据包 获取目标主机的mac地址
协议工作模式: 每一台主机的ip都是已知的
4 传输层
功能: 建立端口到端口的通信
端口范围: 0-65535 0-1023是系统占用端口
1 TCP协议
可靠传输 tcp数据包没有长度限制 理论可以无限长 但是为了保证网络传输的效率 通常TCP数据包的长度不会超过IP数据包的长度 确保单个TCP不需要再分割
三次握手四次挥手
2 UDP协议
不可靠传输 报头部分只有8个字节 总长度不超过 65535字节 刚好放进一个IP数据包
5 应用层(包括应用层 表示层 会话层)
功能: 规定应用程序的数据格式
网络编程
- 初始版本
client
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send('alex'.encode('utf-8'))
from_server_data = phone.recv(1024)
print(from_server_data)
phone.close()
server
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn,client_addr = phone.accept()
print(conn,client_addr,sep='\n')
from_client_data = conn.recv(1024)
print(from_client_data)
conn.send(from_client_data.upper())
conn.close()
phone.close()
- 第二版 通信循环
client
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
while 1:
try:
client_data = input('>>>')
phone.send(client_data.encode('utf-8'))
from_server_data = phone.recv(1024)
print(from_server_data.decode('utf-8'))
except ConnectionError:
break
phone.close()
server
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn,client_addr = phone.accept()
print(
conn,
client_addr,
sep='\n'
)
while 1:
try:
from_client_data = conn.recv(1024)
print(from_client_data.decode('utf-8'))
conn.send(from_client_data+b'sb')
except ConnectionError:
break
conn.close()
phone.close()
- 第三版 通信链接循环
client
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
while 1 :
client_data = input('>>>')
phone.send(client_data.encode('utf-8'))
from_server_data = phone.recv(1024)
print(from_server_data.decode('utf-8'))
phone.close()
server
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
while 1:
conn,client_addr = phone.accept()
print(client_addr)
while 1:
try:
from_client_data = conn.recv(1024)
print(from_client_data.decode('utf-8'))
conn.send(from_client_data+b'alex')
except ConnectionError:
break
conn.close()
phone.close()
- 第四版 远程执行命令
client
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8081))
while 1 :
to_server_msg = input("...").strip()
phone.send(to_server_msg.encode('utf-8'))
from_server_msg = phone.recv(1024)
print(from_server_msg.decode('utf-8'))
phone.close()
server
import socket
import subprocess
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5)
while 1:
conn,client_addr = phone.accept()
print(client_addr)
while 1:
try:
cmd = conn.recv(1024)
print(cmd.decode('utf-8'))
ret = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
correct_msg = ret.stdout.read()
error_msg = ret.stderr.read()
conn.send(correct_msg+error_msg)
except ConnectionError:
break
coon.close()
phone.close()
- udp协议
client
import socket
ip_port = ('127.0.0.1',8087)
udp_sk = socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr = udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)
server
import socket
udp_sk =socket.socket(type=socket.SOCK_DGRAM)
udp_sk.bind(('127.0.0.1',8087))
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr)
udp_sk.close()
- 黏包-情况一
client
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
while 1 :
cmd = input('...').strip()
phone.send(cmd.encode('utf-8'))
from_server_data = phone.recv(1024)
print(from_server_data.decode('utf-8'))
phone.close()
server
'''
接收方没有及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
'''
import socket
import subprocess
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
while 1 :
conn,client_addr = phone.accept()
print(client_addr)
while 1 :
try:
cmd = conn.recv(1024)
ret = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
correct_msg = ret.stdout.read()
error_msg = ret.stderr.read()
conn.send(correct_msg+error_msg)
except ConnectionError:
break
conn.close()
phone.close()
- 黏包-情况二
client
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
phone.send(b'alex')
phone.send(b'zxcv')
phone.close()
server
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
conn,client_addr = phone.accept()
first_data = conn.recv(1024)
print('1:',first_data.decode('utf-8'))
second_data = conn.recv(1024)
print('2:',second_data.decode('utf-8'))
conn.close()
phone.close()
- 黏包-解决方案
client
import socket
import struct
import json
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080))
while 1:
cmd = input('...').strip()
if not cmd:continue
phone.send(cmd.encode('utf-8'))
header_size = struct.unpack('i',phone.recv(4))[0]
header_bytes = phone.recv(header_size)
header_dict = json.loads(header_bytes.decode('utf-8'))
total_size = header_dict['total_size']
recv_size = 0
res = b''
while recv_size < total_size:
recv_data = phone.recv(1024)
res += recv_data
recv_size += len(recv_data)
print(res.decode('utf-8'))
phone.close()
server
import socket
import subprocess
import struct
import json
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
while 1 :
conn, client_addr = phone.accept()
print(client_addr)
while 1 :
try:
cmd = conn.recv(1024)
ret = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
correct_msg = ret.stdout.read()
error_msg = ret.stderr.read()
total_size = len(correct_msg) + len(error_msg)
head_dict = {
'md5':'fdsaf2143254f',
'file_name':'f1.txt',
'total_size':total_size
}
header_dict_json = json.dumps(head_dict)
bytes_headers = header_dict_json.encode('utf-8')
header_size = len(bytes_headers)
header = struct.pack('i',header_size)
conn.send(header)
conn.send(bytes_headers)
conn.send(correct_msg)
conn.send(error_msg)
except ConnectionError:
break
conn.close()
phone.close()
原理
'''
整个流程的大致解释:
我们可以把报头做成字典,字典里包含将要发送的真实数据的描述信息(大小啊之类的),然后json序列化,然后用struck将序列化后的数据长度打包成4个字节。
我们在网络上传输的所有数据 都叫做数据包,数据包里的所有数据都叫做报文,报文里面不止有你的数据,还有ip地址、mac地址、端口号等等,其实所有的报文都有报头,这个报头是协议规定的,看一下
发送时:
先发报头长度
再编码报头内容然后发送
最后发真实内容
接收时:
先手报头长度,用struct取出来
根据取出的长度收取报头内容,然后解码,反序列化
从反序列化的结果中取出待取数据的描述信息,然后去取真实的数据内容
'''
- FTP文件
client
import socket
import struct
import json
import os
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8001))
file_info = {
'file_path': r'D:\lnh.python\pyproject\PythonReview\网络编程\08 文件的上传下载\low版\aaa.mp4',
'file_name': 'aaa.mp4',
'file_size': None,
}
file_info['file_size'] = os.path.getsize(file_info['file_path'])
head_dic_json = json.dumps(file_info)
head_dic_bytes = head_dic_json.encode('utf-8')
ret = struct.pack('i', len(head_dic_bytes))
phone.send(ret)
phone.send(head_dic_bytes)
with open(file_info['file_path'], mode='rb') as f1:
data_size = 0
while data_size < file_info['file_size']:
every_data = f1.read(1024)
data_size += len(every_data)
phone.send(every_data)
phone.close()
server
import socket
import subprocess
import json
import struct
import os
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(('127.0.0.1', 8001))
phone.listen(5)
file_positon = r'd:\上传下载'
conn, client_addr = phone.accept()
ret = conn.recv(4)
head_dic_bytes_size = struct.unpack('i', ret)[0]
head_dic_bytes = conn.recv(head_dic_bytes_size)
head_dic_json = head_dic_bytes.decode('utf-8')
head_dic = json.loads(head_dic_json)
file_path = os.path.join(file_positon, head_dic['file_name'])
with open(file_path, mode='wb') as f1:
data_size = 0
while data_size < head_dic['file_size']:
data = conn.recv(1024)
f1.write(data)
data_size += len(data)
conn.close()
phone.close()
- FTP文件plus
client
import socket
import struct
import json
import os
class MYTCPClient:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
allow_reuse_address = False
max_packet_size = 8192
coding='utf-8'
request_queue_size = 5
def __init__(self, server_address, connect=True):
self.server_address=server_address
self.socket = socket.socket(self.address_family,
self.socket_type)
if connect:
try:
self.client_connect()
except:
self.client_close()
raise
def client_connect(self):
self.socket.connect(self.server_address)
def client_close(self):
self.socket.close()
def run(self):
while True:
inp=input(">>: ").strip()
if not inp:continue
l=inp.split()
cmd=l[0]
if hasattr(self,cmd):
func=getattr(self,cmd)
func(l)
def put(self,args):
cmd=args[0]
filename=args[1]
if not os.path.isfile(filename):
print('file:%s is not exists' %filename)
return
else:
filesize=os.path.getsize(filename)
head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}
print(head_dic)
head_json=json.dumps(head_dic)
head_json_bytes=bytes(head_json,encoding=self.coding)
head_struct=struct.pack('i',len(head_json_bytes))
self.socket.send(head_struct)
self.socket.send(head_json_bytes)
send_size=0
with open(filename,'rb') as f:
for line in f:
self.socket.send(line)
send_size+=len(line)
print(send_size)
else:
print('upload successful')
client=MYTCPClient(('127.0.0.1',8080))
client.run()
server
import socket
import struct
import json
import subprocess
import os
class MYTCPServer:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
allow_reuse_address = False
max_packet_size = 8192
coding='utf-8'
request_queue_size = 5
server_dir='file_upload'
def __init__(self, server_address, bind_and_activate=True):
"""Constructor. May be extended, do not override."""
self.server_address=server_address
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
try:
self.server_bind()
self.server_activate()
except:
self.server_close()
raise
def server_bind(self):
"""Called by constructor to bind the socket.
"""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
def server_activate(self):
"""Called by constructor to activate the server.
"""
self.socket.listen(self.request_queue_size)
def server_close(self):
"""Called to clean-up the server.
"""
self.socket.close()
def get_request(self):
"""Get the request and client address from the socket.
"""
return self.socket.accept()
def close_request(self, request):
"""Called to clean up an individual request."""
request.close()
def run(self):
while True:
self.conn,self.client_addr=self.get_request()
print('from client ',self.client_addr)
while True:
try:
head_struct = self.conn.recv(4)
if not head_struct:break
head_len = struct.unpack('i', head_struct)[0]
head_json = self.conn.recv(head_len).decode(self.coding)
head_dic = json.loads(head_json)
print(head_dic)
cmd=head_dic['cmd']
if hasattr(self,cmd):
func=getattr(self,cmd)
func(head_dic)
except Exception:
break
def put(self,args):
file_path=os.path.normpath(os.path.join(
self.server_dir,
args['filename']
))
filesize=args['filesize']
recv_size=0
print('----->',file_path)
with open(file_path,'wb') as f:
while recv_size < filesize:
recv_data=self.conn.recv(self.max_packet_size)
f.write(recv_data)
recv_size+=len(recv_data)
print('recvsize:%s filesize:%s' %(recv_size,filesize))
tcpserver1=MYTCPServer(('127.0.0.1',8080))
tcpserver1.run()