IO操作
涉及到在内存中存在数据叫喊的操作。
程序分类
** Io密集型程序**
Io密集型程序指的是在程序执行过程中,有大量的Io操作,而cpu运算较少。耗时cpu较少,但花费时间;
计算密集型程序
计算密集型程序,指的是程序中的计算量较大,IO操作相对较少,cpu消耗大,执行熟读快,几乎没有阻塞。
文件
文件是保存在持久化储存设备上的一段数据。它从格式上可以分为:文本文件,二进制文件等。在python中见文件看作是一种类型的对象,类似其他类型。
字节串
字节串的概念是在python3中才引入的。与字符串不同的是:字节串使用直接序列值表示数据。是常见的二进制数据展现形式。
文件的读写
对文件的操作有:打开文件,读写文件和关闭文件。
打开文件:
#buffering 1 表示有行缓冲,成功返回文件操作对象
file_object = open(filename,access_bode='打开类型:r,w,a等',buffering=-1)
r 以读方式打开 文件必须存在
w 以写方式打开,文件不存在则创建,存在清空原有内容。
a 以追加模式打开
r+ 以读写模式打开 文件必须存在
w+ 以读写模式打开文件,不存在则创建,存在清空原有内容。
a+ 以读写模式打开 追加模式
rb 以二进制读模式打开 同r
wb 以二进制写模式打开 同w
ab 以二进制追加模式打开 同a
rb+ 以二进制读写模式打开 同r+
wb+ 以二进制读写模式打开 同w+
ab+ 以二进制读写模式打开 同a+
读取文件
读取文件的方式有:
read([size]):
data=f.read(16)
# [size]:当给定时,表示读取到给定数目的字符或者字节;当不给定,或者为负数的时候,文件将被读取到文件结尾。
readline([size]):
用来读取文件中一行,
data=f.readlin(28)
#[size]:如果没有给定size参数(默认值为-1)或者size值为负,表示读取一行;给定size,它和read()没有区别。
readlines([sizeint]):
data = f.readlines([])
#读取文件中的每一行作为列表中的一项;当不给sizeint时,和read()一样;当给定sizeint,读取到size字符所在的行为止。
参考代码
# 打开文件
f = open('1.jpg','rb')
# 读操作
while True:
# 到文件结尾时会读出空字串
data = f.read(16)
# 到文件结尾跳出循环
if not data:
break
print("读取到的数据:",data)
# 每次读取一行内容
data = f.readline(6)
print("一行内容:",data)
data = f.readline()
print("一行内容:",data)
print("**************************************")
# 将内容读取到一个列表
# 参数表达的是读取到该字符数所在的行
data = f.readlines(28)
print(data)
# 文件对象可迭代,每次一行
for line in f:
print(line)
# 关闭
f.close()
文件的写入
写入文件的方式有:
write(string):
功能: 把文本数据或二进制数据块的字符串写入到文件中去
参数:要写入的内容
返回值:写入的字符个数
writelines(str_list):
功能:接受一个字符串列表作为参数,将它们写入文件。
参数: 要写入的内容列表
f = open('text','w')
f.write(b"hello,diegui\n") # 如果希望换行则自己添加
m = open('text','ab')
m.write("哎呀,干啥".encode())
# 写入列表内容
l = ['hello world\n','哈哈哈\n']
f.writelines(l)
with操作
保证不管处理过程中是否发生错误或者异常都会执
行规定的“清理”操作,释放被访问的资源,比如有文件读写后自动关闭、线程中锁的自动获取和释放
等。
举例:
with open('4.txt') as f: # 以只读方式生成f对象
data = f.read()
print(data)
通过with方法可以不用close(),因为with生成的对象在语句块结束后会自动处理,所以也就不需要close
了,但是这个文件对象只能在with语句块内使用。
刷新缓冲区
刷新缓冲区条件:
-
缓冲区被写满
-
程序执行结束或者文件对象被关闭
-
行缓冲遇到换行
-
程序中调用flush()函数
flush()
该函数调用后会进行一次磁盘交互,将缓冲区中的内容写入到磁盘。
举例:
f = open('test','w')
while True:
data = input(">>")
if not data:
break
f.write(data + '\n')
f.flush() # 主动刷新缓冲
f.close()
文件偏移量
功能:移动文件偏移量位置
参数:offset 代表相对于某个位置移动的字节数。负数表示向前移动,正数表示向后移动。
whence是基准位置的默认值为 0,代表从文件开头算起,1代表从当前位置算起,2 代表从文件末
尾算起。
举例:
f = open("test",'wb+')
f.write(b"Hello world")
print("偏移量:",f.tell()) # 获取文件偏移量
f.seek(-5,2) # 将文件偏移量定位到开头
data = f.read()
print(data)
f.close()
系统中每一个IO操作都会分配一个整数作为编号,该整数即这个IO操作的文件描述符。
可以通过:fileno()
通过IO对象获取对应的文件描述符。
文件管理函数
- 获取文件大小
os.path.getsize(file) - 查看文件列表
os.listdir(dir) - 查看文件是否存在
os.path.exists(file) - 判断文件类型
os.path.isfile(file) - 删除文件
os.remove(file)
网络编程基础
计算机网络功能主要实现资源共享,数据信息的传递。
OSI七层模型:(是网络通络通信工作流程标准化)
应用层:
提供用户服务,具体功能应用程序。
表示层:
数据的压缩优化加密。
会话层:
建立用户级的连接,选择适当的传输服务。
传输层:
提供传输服务。
网络层:
路由选择,网络互联。
数据链路层:
进行数据的交互,控制具体数据发送。
物理层:
提供数据传输的硬件保证,网卡接口,传输介质。
四层模型:(TCP、IP模型)
应用层:
( 应用层[TFTP,FTP,NFS,WAIS,HTTP],表示层[Telnet,Rlogin,SNMP,Gopher],会话层[SMTP,DNS])。
传输层:
(传输层:[tcp,udp])。
网际层:
(网络层:[IP,ICMP,ARP,RARP,AKP,UUCP])。
网络接口:
(数据链路层[FDDI,Ethernet,Arpanet,PDN,SLTP,PPP],物理层[硬件设备等])。
![全双工的面向连接的可靠的按序递交的无重复的字节流通信]
传输层服务:
面向链接的传输层服务(基于TCP协议的数据传输)
传输特征:提供可靠的数据传输,在传输过程中,无丢失,无失序,无差错,无重复。
实现手段:在通信前建立数据链接,通信结束要正常断开连接。
三次握手(建立连接):
客户端向服务端发送消息保温的请求连接(SYN=1,Seq=X);
服务器收到请求后,回复保温确定可以连接(SYN=1,ACK=X+1,Sep=Y);
客户端收到回复,发送最终报文连接(ACK=Y+1,Seq=Z)。
四次挥手(断开连接):
主动方发送报文请求断开连接(Fin=1,AcK=Z,Seq=X);
被动方收到请求,立即回复,表示准备断开(ACK=X+1,Seq=Z);
被动方准备就绪,再次发送报文表示可以断开连接(Fin=1,AcK=X,Seq=Y);
主动方收到确定,发送最终报文完成断开(ACK=Y Seq=X)。
创建过程,及代码实现:
服务端代码:
import socket
# 创建tcp套接字对象
sockfd = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
# 绑定地址
sockfd.bind(('0.0.0.0',9999))
# 设置监听
sockfd.listen(5)
# 等待处理客户端连接请求
print("Waiting for connect...")
connfd,addr = sockfd.accept()
print("Connect from",addr)
# 消息收发
data = connfd.recv(1024)
print("Receive:",data.decode())
n = connfd.send(b"Thanks")
print('Send %d bytes'%n)
# 关闭套接字
connfd.close()
sockfd.close()
客户端代码(配合服务端使用):
from socket import *
# 创建tcp套接字
sockfd = socket() # 默认值
# 连接服务器
server_addr = ('127.0.0.1',9999) # 服务器地址
sockfd.connect(server_addr)
# 先发后收
msg = input("Msg:")
sockfd.send(msg.encode()) #字节串
data = sockfd.recv(1024)
print("From server:",data.decode())
sockfd.close()
面向无连接的传输层服务(基于UDP协议的数据传输)
- 传输特点 : 传输过程没有连接和断开,数据收发自由随意。
- 适用情况 : 网络较差,对传输可靠性要求不高。比如:网络视频,群聊,广播
udp服务端代码:
from socket import *
# 创建udp套接字
sockfd = socket(AF_INET,SOCK_DGRAM)
# 绑定地址
server_addr = ('127.0.0.1',8888)
sockfd.bind(server_addr)
# 循环收发消息
while True:
data,addr = sockfd.recvfrom(1024)
print("Msg from %s: %s"%(addr,data.decode()))
sockfd.sendto(b'Thanks',addr)
# 关闭套接字
sockfd.close()
udp客户端实现代码:
from socket import *
# 服务器地址
ADDR = ("127.0.0.1",8888)
# 创建套接字
sockfd = socket(AF_INET,SOCK_DGRAM)
# 循环收发消息
while True:
data = input("Msg>>")
if not data: # 退出
break
sockfd.sendto(data.encode(),ADDR)
msg,addr = sockfd.recvfrom(1024)
print("From server:",msg.decode())
sockfd.close()
socket:
由上面的代码,你也已经对套接字**(socket)**有了一定的感性认识:
那么下面,我将详细阐述有关的问题。
服务端:
1.创建套接字:
# 创建tcp套接字对象
sockfd = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
2.绑定地址:
# 绑定地址
sockfd.bind(('0.0.0.0',9999))
3.设置监听:
# 设置监听
sockfd.listen(5)
4.等待处理客户端链接请求:
我们在这里用一个举例进行说明
# 等待处理客户端连接请求
while True:
print("Waiting for connect...")
try:
connfd,addr = sockfd.accept()
print("Connect from",addr)
except KeyboardInterrupt:
# ctrl-c 退出程序
print("Server exit")
break
except Exception as e:
print(e)
continue
其中使用到了:
connfd,addr = sockfd.accept()
#功能: 阻塞等待处理客户端请求返回值: connfd 客户端连接套接字addr 连接的客户端地址
5.收发消息:
# 消息收发 (先收后发)
while True:
data = connfd.recv(1024) #消息接收
# 如果data为空意味着客户端断开
if not data:
break
print("Receive:",data.decode())
# if data == b'Q':
# break
n = connfd.send(b"Thanks") #消息发送
print('Send %d bytes'%n)
connfd.close()
6.关闭套接字:
sockfd.close()
#功能:关闭套接字
客户端
1.创建套接字:
from socket import *
# 创建tcp套接字
sockfd = socket() # 默认值
2.链接服务器:
# 连接服务器
server_addr = ('127.0.0.1',9999) # 服务器地址
sockfd.connect(server_addr)
3.收发消息:
# 先发后收
while True:
msg = input("Msg:")
if not msg:
break
sockfd.send(msg.encode()) #字节串
# if msg == 'Q':
# break
data = sockfd.recv(1024)
print("From server:",data.decode())
这里要注意一点:服务端和客户端的收发消息的次序刚好是反过来的,一端先发后收,另一端必须是先收后发。
4.关闭套接字:
sockfd.close()
tcp套接字数据传输的特点:
当一端退出,另一端会阻塞在recv,此时recv会立即返回一个空字符串;
tcp链接中如果一端已经不存在,仍然进行send 会出现BrokenPipError;
一个监听套接字可以同时连接多个客户端,也能够重复被连接.
tcp粘包:
tcp以字节流方式传输,没有消息边界。多次发送的消息被一次接收,此时就会形成粘包;
可以通过设置发送速度进行控制,或者人为设置消息边界。
tcp套接字和udp套接字编程区别:
- 流式套接字是以字节流方式传输数据,数据报套接字以数据报形式传输;
- tcp套接字会有粘包,udp套接字有消息边界不会粘包;
- tcp套接字保证消息的完整性,udp套接字则不能;
- tcp套接字依赖listen accept建立连接才能收发消息,udp套接字则不需要;
- tcp套接字使用send,recv收发消息,udp套接字使用sendto,recvfrom;
socket套接字的属性:
【1】 sockfd.type 套接字类型;
【2】 sockfd.family 套接字地址类型;
【3】 sockfd.getsockname() 获取套接字绑定地址;
【4】 sockfd.fileno() 获取套接字的文件描述符;
【5】 sockfd.getpeername() 获取连接套接字客户端地址;
【6】 sockfd.setsockopt(level,option,value) 设置套接字选项;
level:选项类别;SOL_SOCKET
option: 具体选项内容;
value: 选项值。
Struct模块进行数据打包:
服务端:
from socket import *
import struct
# 与客户端格式一直
st = struct.Struct("i16sif")
#udp套接字
s = socket(AF_INET,SOCK_DGRAM)
s.bind(('127.0.0.1',8888))
# 打开一个保存信息的文件
f = open('student.txt','a')
while True:
data,addr = s.recvfrom(1024)
# (1,b'Lily',14,94.5)
data = st.unpack(data)
# 写入文件
info = "%d %-10s %d %.1f\n"%data
f.write(info)
f.flush()
f.close()
s.close()
客户端:
import struct
from socket import *
# 设置数据结构
st = struct.Struct("i16sif")
# 创建udp套接字
s = socket(AF_INET,SOCK_DGRAM)
ADDR = ('127.0.0.1',8888)
while True:
print("===========================")
id = int(input("学号:"))
name = input("姓名:").encode()
age = int(input("年龄:"))
score = float(input("得分:"))
# 将数据打包
data = st.pack(id,name,age,score)
s.sendto(data,ADDR) # 发送给服务端
s.close()
可以使用struct模块直接调用pack unpack。此时这两函数第一个参数传入fmt。其他用法功能相同。
HTTP传输:
- 用途 : 网页获取,数据的传输;
- 特点:
应用层协议,传输层使用tcp传输;
简单,灵活,很多语言都有HTTP专门接口;
无状态,协议不记录传输内容;
http1.1 支持持久连接,丰富了请求类型; - 网页请求过程:
1.客户端(浏览器)通过tcp传输,发送http请求给服务端;
2.服务端接收到http请求后进行解析;
3.服务端处理请求内容,组织响应内容;
4.服务端将响应内容以http响应格式发送给浏览器;
5.浏览器接收到响应内容,解析展示;
http请求:
请求行:具体ed请求类别和请求内容;
请求类别: 每个请求类别表示要做不同的事情;
请求头:对请求的进一步解释和描述。
http响应:
响应格式: 响应行,响应头,空行,响应体。
响应行:反馈基本的响应情况;
响应码:
1xx 提示信息,表示请求被接收;
2xx 响应成功;
3xx 响应需要进一步操作,重定向;
4xx 客户端错误;
5xx 服务器错误;
响应头:对响应内容的描述;
响应体:响应的主体内容信息;
代码实现:
"""
http请求响应演示
"""
from socket import *
# tcp服务端
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 端口立即重用
s.bind(('127.0.0.1',8000))
s.listen(5)
c,addr = s.accept()
print("Connect from",addr)
data = c.recv(4096).decode()
print(data) # http请求
html = """HTTP/1.1 200 OK
Content-Type: text/html
<h1>Hello World</h1>
"""
c.send(html.encode())
c.close()
s.close()
"""
编写一个http服务端程序
如果浏览器的请求内容 /
响应码为 200 OK,将index.html内容作为响应内容
如果浏览器的请求是其他的
响应码为 404 Not Found 内容为 "Sorry.."
"""
from socket import *
# 与客户端交互
def handle(connfd):
# 获取http请求
data = connfd.recv(4096).decode()
request_line = data.split('\n')[0] # 请求行
info = request_line.split(' ')[1] # 请求内容
# 看一下请求内容是不是/
if info == '/':
with open('index.html') as f:
# 组织http响应
response = "HTTP/1.1 200 OK\r\n"
response += "Content-Type:text/html\r\n"
response += '\r\n'
response += f.read()
else:
response = "HTTP/1.1 404 Not Found\r\n"
response += "Content-Type:text/html\r\n"
response += '\r\n'
response += "<h1>Sorry...</h1>"
# 发送给浏览器
connfd.send(response.encode())
# 搭建网络
def main():
sockfd = socket()
sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
sockfd.bind(('0.0.0.0',8000))
sockfd.listen(3)
while True:
connfd,addr = sockfd.accept()
print("Connect from",addr)
# 处理客户端请求
handle(connfd)
main()
下一篇讲述:并发编程