Python异常处理、模块和包、网络编程
一、异常处理
# try excpet try: choice = int(input(">>>")) print(choice) except ValueError: print("您输入的不是数字") # 万能异常 # 所有的异常处理都用万能异常好不好? # 具体的异常处理+万能异常: # 能够提前预料到的异常都应该用具体的异常去处理,剩下其他的异常用万能异常控制 # 万能异常应该写在最后 try: choice = int(input(">>>")) print(choice) except Exception: # 不写Exception也是万能异常 print("您输入的不是数字") # try excpet as # 可以显示具体的错误 try: choice = int(input(">>>")) print(choice) except ValueError as e: print(e) except Exception as e: # 如果使用万能异常,一定要用as显示错误 print(e) # try excpet else try: choice = int(input(">>>")) print(choice) except ValueError: print('请输入一个数字') except Exception as e: # 处理未知异常的所有异常 print(e) else: print('执行else了') # 如果try语句中的代码都顺利的执行了,没有报错,那么执行else中的代码 finally: print('执行finally了') # 无论如何都会执行 # try标准写法 try: pass # 可能有问题的代码 except ValueError: # 能预料到的错误 pass except Exception as e:print(e) # 能处理所有的异常 else:pass # try中的代码没有错误的时候执行 finally:pass # 无论如何都会执行的 # 断言 # 如果断言失败则不执行断言下面的代码 assert 1==2
二、模块和包
# 模块总结 # 能不能导入模块 : sys.path # 导入模块的顺序 : 内置模块 扩展模块 自定义模块 # 导入模块 : 相当于执行了这个模块,文件中的名字会被存储在一块独立的内存空间中 # 一个模块如果执行多次import,只会执行一次 # 模块之间不能发生循环引用。 # import # 在全局创建了一个模块名,指向属于这个模块的命名空间 # 空间里存储了所有文件中的名字 # 起别名 import ... as .. # 不推荐一行导入多个模块 # from import # import后面的名字会出现在全局 ,相当于对独立命名空间中的一个引用 # from import 也支持 as语句 也支持 导入多个名字 # from import * 相当于导入所有名字,*和被导入模块__all__是互相影响的 # 包 # 定义:一组py文件组成的文件夹,在这个文件夹里有一个__init__.py 叫做包 # 导入一个模块相当于执行了模块中的代码 # 导入一个包相当于执行了__init__.py的代码 # 相对导入:以glance作为起始 # from ..cmd import manage # manage.main() # 绝对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内) # from glance.cmd import manage # manage.main()
三、网络编程
# 为什么要有网络编程? # 两个文件之间如何实现通信? # 基于文件通信--> 两个程序都在同一台机器上 # 基于网络--> 两个不同机器上的程序 # 交换机是用于在一个局域网内的机器之间的通信 # 路由器是用于不同局域网之间的机器的通信 # arp协议:一台机器通过IP地址和交换机找到另一台机器的mac地址的过程 # ip地址能够找到网络中的唯一一台机器,临时的 # 如何在一台机器上精准的找到要和我通信的服务??? # 操作系统端口范围: 0-65535 # 在一台机器上 每一个服务所使用的端口不能相同 # 通过什么能够找到网络世界里的一个服务? ip+端口 # 两种通信协议 # TCP:(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。 # UDP:(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)
# 基于TCP协议的socket的server # tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 import socket sk = socket.socket() sk.bind(("127.0.0.1", 9000)) # 把地址绑定到套接字 sk.listen() # 监听链接 con, address = sk.accept() # 接受客户端链接 msg = con.recv(1024).decode("utf-8") # 接收客户端信息 print(msg) # 打印客户端信息 con.send("client说:你好".encode("utf-8")) # 向客户端发送信息 con.close() # 关闭客户端套接字 sk.close() # 关闭服务器套接字(可选) # 基于TCP协议的socket的client import socket sk = socket.socket() # 创建客户套接字 sk.connect(("127.0.0.1", 9000)) # 尝试连接服务器 sk.send("server说:你好".encode("utf-8")) # 给server发送消息 msg = sk.recv(1024).decode("utf-8") # 接受server发来的消息 print(msg) sk.close() # 关闭客户套接字
# OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。 import socket sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加。端口重用 sk.bind(("127.0.0.1", 9000)) # 把地址绑定到套接字 sk.listen() # 监听链接 con, address = sk.accept() # 接受客户端链接 msg = con.recv(1024).decode("utf-8") # 接收客户端信息 print(msg) # 打印客户端信息 con.send("client说:你好".encode("utf-8")) # 向客户端发送信息 con.close() # 关闭客户端套接字 sk.close() # 关闭服务器套接字(可选)
# 基于UDP协议的socket的server # udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接 import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(("127.0.0.1", 9000)) msg, address = sk.recvfrom(1024) print(address, msg.decode("utf-8")) sk.sendto("你好".encode("utf-8"), address) sk.close() # 基于UDP协议的socket的client import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.sendto("你好".encode("utf-8"), ("127.0.0.1", 9000)) msg, address = sk.recvfrom(1024) print(address, msg.decode("utf-8")) sk.close()
# QQ聊天 # server import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(("127.0.0.1", 9000)) while True: msg, address = sk.recvfrom(1024) print("%s[%s]:%s" % (address[0], address[1], msg.decode("utf-8"))) to_msg = input(">>>:").strip() sk.sendto(to_msg.encode("utf-8"), address) # client import socket sk = socket.socket(type=socket.SOCK_DGRAM) while True: msg = input(">>>:") sk.sendto(msg.encode("utf-8"), ("127.0.0.1", 9000)) msg, address = sk.recvfrom(1024) print("%s[%s]:%s" % (address[0], address[1], msg.decode("utf-8")))
# server import socket import os sk = socket.socket() sk.bind(("127.0.0.1", 9000)) sk.listen() while True: con, address = sk.accept() while True: msg = con.recv(1024).decode("utf-8") if msg.upper() == "Q": con.close() break ret = os.popen(msg).read() con.send(ret.encode("utf-8")) # client import socket sk = socket.socket() sk.connect(("127.0.0.1", 9000)) while True: cmd = input(">>>:") if not cmd:continue sk.send(cmd.encode("utf-8")) if cmd.upper() == "Q":break ret = sk.recv(1024).decode("utf-8") print(ret) print("1") sk.close()
from socket import * ip_port = ('127.0.0.1', 8080) tcp_socket_server = socket() tcp_socket_server.bind(ip_port) tcp_socket_server.listen() conn, address = tcp_socket_server.accept() data1 = conn.recv(10) data2 = conn.recv(10) print('----->', data1.decode('utf-8')) print('----->', data2.decode('utf-8')) conn.close() # 粘包 接收方的缓存机制 # 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) # # 黏包现象只发生在tcp协议中: # 1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。 # 2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
# 粘包 发送方的缓存机制 # 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包) import socket BUFSIZE = 1024 ip_port = ('127.0.0.1', 8080) s = socket.socket() s.connect(ip_port) s.send('hello'.encode('utf-8')) s.send('egg'.encode('utf-8')) s.close()
# 解决一些小数据在连续发送时候的粘包问题 import socket import struct sk = socket.socket() sk.bind(("127.0.0.1", 9000)) sk.listen() con, addr = sk.accept() header_len_bytes = con.recv(4) # 接受客户端发来的struct类型长度为4的数据 header_len = struct.unpack("i", header_len_bytes)[0] # unpack获取真实数据长度 print(con.recv(header_len)) # 接受真实数据 header_len_bytes = con.recv(4) header_len = struct.unpack("i", header_len_bytes)[0] print(con.recv(header_len)) con.close() sk.close()
# 解决一些小数据在连续发送时候的粘包问题 import socket import struct sk = socket.socket() sk.connect(("127.0.0.1", 9000)) data1 = "aaaaaaaa" header1 = struct.pack("i", len(data1)) # 使用struct把真实数据长度固定4的bytes类型 sk.send(header1) # 发送长度为4的bytes类型 sk.send(data1.encode("utf-8")) # 发送真实的数据 data2 = "bbbbbbbb" header2 = struct.pack("i", len(data2)) sk.send(header2) sk.send(data2.encode("utf-8")) sk.close()
# 解决粘包,高级版(主要解决大文件的传输) import socket import struct import json sk = socket.socket() sk.bind(("127.0.0.1", 9000)) sk.listen() con,addr = sk.accept() header_struct = con.recv(4) # 接受报头长度struct类型 header_len = struct.unpack("i", header_struct)[0] # 获取unpack后的报头长度 header_str = con.recv(header_len).decode("utf-8") # 获取字符串格式报头 header = json.loads(header_str) # 反序列化字符串,获取字典格式的报头 recv_size = 0 with open(header["name"], "wb") as f: # 循环接受真实数据 while recv_size < header["size"]: line = con.recv(1024) f.write(line) recv_size += len(line) con.close() sk.close()
# 解决粘包,高级版(主要解决大文件的传输) import socket import json import struct import os sk = socket.socket() sk.connect(("127.0.0.1", 9000)) file_path = r"C:\Users\lanpa\Desktop\Python自动化21期\Python21\day8\课件\4.包.py" header = {"name": os.path.basename(file_path), "size": os.path.getsize(file_path)} header_json = json.dumps(header) # 把字典转换成字符串格式 header_struck = struct.pack("i", len(header_json)) # 把字符串长度转换成struck类型固定长度为4的bytes类型 sk.send(header_struck) # 发送header长度 sk.send(header_json.encode("utf-8")) # 发送报头信息 with open(file_path, "rb") as f: # 循环发送真实的数据 for line in f: sk.send(line)