端口:
拥有IP地址的主机可以提供许多服务.比如Web服务,FTP服务,SMTP服务这些服务完全通过1个IP地址来实现.那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务关系是一对多的关系.实际上是通过"IP地址+端口号"来区分不同的服务.
端口是为了将同一台电脑上的不同程序进行隔离
IP是找电脑,而端口是找电脑上的程序.
示例:
MySQL是一个软件,软件帮助我们在硬盘上进行文件操作.默认端口:3306
Redis是一个软件,软件帮助我们在内存里进行数据操作.默认端口:6379
网站默认端口:80 ,访问时 :http://www.luffycity.com:80
网站默认端口:443 ,访问时:https://www.luffycity.com:443
端口范围:
1~65535 但是1~1024是内置(默认)不可用
OSI 7层模型
人们按照分工不同把互联网协议从逻辑上划分了层级:
应用层 : 使用软件 打开软件或网站 表示层 : 看到数据,图片,视频 生产数据:szwwd 会话层 : 保持链接状态或登录 应用偷偷携带一点其他数据:令牌19rRNAwf8GVe6xyT9kJPIu5SlQc socket模块: 传输层 : TCP/UDP [TCP][szwwd|19rRNAwf8GVe6xyT9kJPIu5SlQc] 网络层 : IP 【IP】【[TCP][szwwd|19rRNAwf8GVe6xyT9kJPIu5SlQc]】 数据链路层 : MAC [MAC][【IP】【[TCP][szwwd|19rRNAwf8GVe6xyT9kJPIu5SlQc]】] 物理层 : 将数据转换成电信号发送
TCP是因特网中的传输城协议,使用三次握手协议链接.当主动发出SYN请求链接后,等待对方回答SYN+ACK[1],并最终对对方的SYN执行ACK确认.这种建立连接的方法可以防止产生错误的连接. tcp三次握手过程: 客户端发送SYN(SEQ = x)报文给服务端,进入SYN_SEND状态 服务器端收到SYN报文,回应一个SYN(SEQ = y)ACK(ACK = x+1)报文,进入SYN_RECV状态. 客户端收到服务器端的SYN报文,回应一个ACK(ACK = y+1)报文,进入Established状态. 三次握手完成,TCP客户端和服务端成功建立连接,可以开始数据传输.
建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的。 (1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。 (2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。 注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。 (3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。 (4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。[1] 既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。 注意: (1) “通常”是指,某些情况下,步骤1的FIN随数据一起发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。[2] (2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。 (3) 当一个Unix进程无论自愿地(调用exit或从main函数返回)还是非自愿地(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN。 无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。[2]
FTP进度条
#模拟进度条原理 import time def func(size,total_size): val = int(size/total_size*100) time.sleep(0.02) print(" %s%%|%s"%(val,"*"*val),end="") for i in range(101) func(i,100)
#os模块 import os size = os.stat(r'D:sylars15day311.进度条.py').st_size print(size)
文件上传
import os import json import socketserver import shutil CODE = {'1001':'上传文件,从头开始上传'} def upload(cmd_dict,conn,username): """ 服务端完成上传文件(含断点续传) :param cmd_dict: :param conn: :return: """ #2.获取文件信息 file_md5 = cmd_dict['md5'] file_name = cmd_dict['file_name'] file_path_path = os.path.join('home',username,file_md5) file_name_path = os.path.join('home',username,file_name) #判断文件是否存在 exist = os.path.exists(file_md5_path) if not exist: #不续传 #3.1.1 可以开始上传了 response = {'code':1001} conn.sendall(json.dumps(response).encode('utf-8')) #3.1.2接收上传文件内容 f = open(file_md5_path,'wb') recv_size = 0 while recv_size < upload_file_size: data = conn.recv(1024) f.write(data) f.flush() recv_size+=len(data) f.close() #3.1.3改名字 shutil.move(file_md5_path,file_name_path) else: #续传 #3.2 续传+大小 exist_size = os.stat(file_md5_path).st_size response = {'code':1002,'size':exist_size} conn.sendall(json.dumps(response).encode('utf-8')) f = open(file_md5_path,'ab') recv_size = reist_size while recv_size < upload_file_size: data = conn.recv(1024) f.write() f.fiush() recv_size += len(data) f.close() #3.2.1改名字 shutil.move(file_md5_path,file_name_path) class NbServer(sockeretserver.BaseRequestHandler): def handle(self): """ self.request 是客户端的socket对象 :return: """ #1.接收命令 upload_cmd_bytes = self.request.recv(8096) cmd_dict = json.loads(upload_cmd_bytes.decode('utf8')) if cmd_dict['cmd'] == 'upload': upload(cmd_dict,self.request,'wj') elif cmd_dict['cmd'] == 'download': #下载 pass if __name__ =='__main__': server = socketserver,ThreadingTCPServer(('127.0.0.1',8001),NbServer) server.serve_forever()
import os import socket import json import hashlib CODE = {'1001':'上传文件,从头开始上传'} def file_md5(file_path): """ 文件进行md5加密 :param file_path: :return: """ obj = open(file_path,'rb') m = hashlib.md5() for line in obj: m.update(line) obj.close() return m.hexdigest() def jdt(size,total_size): """ 显示进度条 :return: """ val = int(size/total_size*100) print(' %s%%|%s'%(val,"*"*val),end='') def send_file(exist_size,file_total_size): """ 发送文件 :param exist_size:开始读取字节的位置 :param file_total_size: 文件总字节大小 :return: """ f = open(file_path,'rb') f.seek(exist_size) send_size = exist_size while send_size < file_total_size: data = f.read(1024) sk.sendall(data) send_size += len(data) jdt(send_size,file_total_size) f.close() print("上传成功") def upload(file_path): """ 文件上传(含断点) :param file_path: :return: """ file_md5_val = file_md5(file_path) file_name = os.path.basename(filee_path) file_size = os.stst(file_path).st_size cmd_dict = {'cmd':'upload','file_name':file_name,'size':file_size,'md5':file_md5_val} upload_cmd_bytes = json.dumps(cmd_dict).encode('utf-8') sk.sendall(upload_cmd_bytes) #2.等待服务端的响应 response = json.loads(sk.recv(8096).decode('utf-8')) if response['code'] == 1001: send_file(0,file_size) else: #断点续传 exist_size = response['size'] send_file(exist_size,file_size) sk = socket.socket() sk.connect(('127.0.0.1',8001)) while True: #upload|文件路径 user_input = input("请输入要执行的命令:") #1.自定义协议{'cmd':'upload','file_path':'....'} cmd,file_path = user_input.split('|',maxsplit = 1) if cmd == 'upload': upload(file_path) elif cmd == 'download': pass