1.IO 多路复用
1.监听多个socket变化
2.socket服务端
IO多路复用+socket 来实现web服务器:
- a.服务端优先运行
- b.浏览器:http://.......com
浏览器连接服务器就是socket + send("http协议") - c.服务端获取客户端发来的url,根据url不同响应数据
- d.断开连接
产出:
- a.浏览器发送数据,需要按照指定规则
- b.监听多个socket对象
- c.web框架开发者,业务开发者
- d.模块独立化
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2017/3/27
import select
import socket
s1 = socket.socket()
s1.setblocking(0)
s1.bind(("127.0.0.1", 8888,))
s1.listen(5)
inputs = [s1, ]
while True:
r_list, w_list, e_list = select.select(inputs, [], [], 0.5)
for client in r_list:
if client == s1:
conn, addr = client.accept()
conn.setblocking(0)
inputs.append(conn)
else:
data = bytes()
while True:
try:
chunk = client.recv(1024)
except Exception as e:
chunk = None
if not chunk:
break
data += chunk
data_str = data.decode()
'''print(data_str)
客户端请求过来的数据原型
GET /login.html HTTP/1.1
Host: 127.0.0.1:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8
'''
header, body = data_str.split("
", 1) # 找到第一个
将请求进行分割;上半部为请求头,下半部为请求体
head_list = header.split("
") # 将“请求头”进行分割为一行行的请求行数据;
'''print(head_list)
通过“
”切分过后的请求头数据
['GET /login.html HTTP/1.1', 'Host: 127.0.0.1:8888', 'Connection: keep-alive', 'Cache-Control: max-age=0',
'Upgrade-Insecure-Requests: 1',
'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Encoding: gzip,
deflate, sdch, br', 'Accept-Language: zh-CN,zh;q=0.8']
'''
head_dict = {} # 将请求头数据 切分为字典格式head_dict
for line in head_list:
value = line.split(":", 1) # 将请求头,按照第一个“:”进行分割
if len(value) == 2: # 如果分割为两份
k, v = value # 第一份设置为 k ,第二份为 v
head_dict[k] = v # 将第一段的k,设置值为 v
else: # 处理请求头的第一行 "GET /login.html HTTP/1.1" 按照空格切分;
head_dict["get"], head_dict["url"], head_dict["HTTP"] = line.split(" ") # 将切分的3部分分别赋值;
'''print(head_dict)
切分后的请求头,字典格式 head_dict
{'Accept-Encoding': ' gzip, deflate, sdch, br',
'Accept': ' text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Upgrade-Insecure-Requests': ' 1',
'url': '/login.html',
'Host': ' 127.0.0.1:8888',
'Cache-Control': ' max-age=0',
'Connection': ' keep-alive',
'get': 'GET',
'User-Agent': ' Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
'HTTP': 'HTTP/1.1',
'Accept-Language': ' zh-CN,zh;q=0.8'}
'''
3.socket 客户端(爬虫)
利用一个线程,同时发送n个请求实现(异步非堵塞模块)
a.循环列表,为每一个URL生成一个Socket对象
b.每一个socket对象,向远程发送链接请求
- connect: 会堵塞
c.如果连接上: - 发送数据时,需要遵循HTTP的格式;
d.获取相应内容
e.关闭
注意:可读可写的状态
产出:
a.setblocking(0) # 以前夯住的地方都会报错
b.select 监听其他对象,需要有个 def fileno(self): 函数
c.gevent, twisted, asycio ---> 单线程并发发送http请求;
import socket
import select
class Foo:
def __init__(self, s1, callback, url ,host):
self.s1 = s1
self.callback = callback
self.url = url
self.host = host
def fileno(self):
return self.s1.fileno()
class NbIO:
def __init__(self):
self.fds = []
self.connections = []
def connect(self, url_list):
for item in url_list:
conn = socket.socket()
conn.setblocking(0) # 表示连接不堵塞,不等待服务端返回数据
try:
conn.connect((item["host"], 80)) # 客户端连接 服务端 connect
except BlockingIOError as e:
pass
obj = Foo(conn, item["callback"], item["url"], item["host"])
self.fds.append(obj)
self.connections.append(obj)
def send(self):
while True:
try:
if len(self.fds) == 0:
return
r_list, w_list, e_list = select.select(self.fds, self.connections, [], 0.5)
if len(self.fds) == 0:
break
for obj in r_list:
# 有数据响应回来
conn = obj.s1
data = bytes()
while True:
try:
d = conn.recv(1024)
data += d
except BlockingIOError as e:
d = None
if not d:
break
# print(data.decode())
obj.callback(data)
self.fds.remove(obj)
for obj in w_list:
# 已经连接到远程
conn = obj.s1
template = "GET %s HTTP/1.1
Host: %s
" % (obj.url, obj.host, )
conn.sendall(template.encode())
# print(conn)
self.connections.remove(obj)
except OSError as e:
pass
def f1(data):
print(data.decode())
def f2(data):
print(data)
url_list = [
{'host':"www.baidu.com",'url': '/','callback':f1 }, # socket
{'host':"www.bing.com",'url': '/','callback':f2 },
{'host':"www.cnblogs.com",'url': '/wupeiqi','callback':f1 },
]
obj = NbIO()
obj.connect(url_list)
obj.send()
2.Paramiko模块,按照SSH协议连接数据以及发送数据;
1.可以通过python代码,实现对远程服务器操作;
2.功能:
- a.使用用户名密码:命令,文件
- b.使用用户名秘钥:命令,文件
- c.执行创建session
2.1使用基于用户名密码的连接:
1.通过SSH Client方式连接:
import paramiko
ssh = paramiko.SSHClient() # 创建SSh对象
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 允许连接不在know_hosts文件中的主机
ssh.connect(hostname="192.168.56.11", port=22, username="root", password="123456") # 创建连接
stdin, stdout, stderr = ssh.exec_command("df -h") # 执行命令
result = stdout.read() # 获取命令结果
print(result.decode()) # 打印获取的命令结果
ssh.close() # 关闭连接
2.通过SSHClient 封装 Transport方式连接
import paramiko
transport = paramiko.Transport(('192.168.56.11', 22))
transport.connect(username='root', password='123456')
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command('df -h')
result = stdout.read()
print(result.decode())
ssh.close()
以上输出:
文件系统 容量 已用 可用 已用% 挂载点
/dev/sda3 48G 13G 36G 26% /
devtmpfs 903M 0 903M 0% /dev
tmpfs 912M 0 912M 0% /dev/shm
tmpfs 912M 33M 880M 4% /run
tmpfs 912M 0 912M 0% /sys/fs/cgroup
/dev/sda1 197M 182M 15M 93% /boot
tmpfs 183M 0 183M 0% /run/user/0
3.基于私钥字符串进行连接
import paramiko
private_key = paramiko.RSAKey.from_private_key_file(r'C:Usersadmin.sshid_rsa')
transport = paramiko.Transport(('192.8.21.100',22))
transport.connect(username='root', pkey=private_key)
ssh = paramiko.SSHClient()
ssh._transport = transport
while True:
cmd = input(">>:")
if not cmd:continue
if cmd == "exit":break
stdin, stdout, stderr = ssh.exec_command(cmd)
stderrad = stderr.read()
result=stdout.read()
print(result.decode('utf-8'),stderrad.decode("utf-8"))
transport.close()
2.2SFTPClient基于用户名密码上传下载
1.用于连接远程服务器并执行上传下载
import paramiko
transport = paramiko.Transport(('192.168.56.11', 22))
transport.connect(username='root', password='123456')
sftp = paramiko.SFTPClient.from_transport(transport) # 创建sftp连接
# sftp.put('/tmp/location.py', '/tmp/test.py') # 将location.py 上传至服务器 /tmp/test.py
sftp.get('a.py', 'a.py') # 将远程家目录下的a.py 下载到本地当前目录 a.py
transport.close()
2.通过paramiko封装 Transport方式进行模块化封装
import paramiko
class SshHelp:
def __init__(self, host, port, username, pwd):
self.host = host
self.port = port
self.username = username
self.pwd = pwd
self.transport = None
def connect(self):
transport = paramiko.Transport((self.host, self.port, ))
transport.connect(username=self.username, password=self.pwd)
self.transport = transport
def upload(self, yuan, local):
sftp = paramiko.SFTPClient.from_transport(self.transport)
sftp.get(yuan, local)
def cmd(self, shell):
ssh = paramiko.SSHClient()
ssh._transport = self.transport
stdin, stdout, stderr = ssh.exec_command(shell)
result = stdout.read()
print(result.decode())
def close(self):
self.transport.close()
if __name__ == '__main__':
obj = SshHelp("192.168.56.11", 22, "root", "123456")
obj.connect()
obj.cmd("df -h")
obj.upload("a.py", "a.py")
obj.close()
3.MySQL的操作连接
1.什么是MySQL:
服务端:
a.socket服务端运行,监听,IP和端口
b.获取客户端发送的数据:select inset...
c.解析
d.去文件中操作
客户端:
a.socket客户端:基于各种语言的客户端
b.验证
c.发送命令(SQL语句)
3.1表的操作:
1.创建一个表tb1:
not null: 表示不能为空;
auto_increment: 表示为自增(一个表只能有一个自增的列)
primary key: 主键(是一种索引,查询速度快,有约束的功能表示这一列不能为空、不能重复)
default: 表示为默认值;在为空时设置一个默认值;
create table tb1(
-> id int not null auto_increment primary key,
-> name char(20) null,
-> age int not null)engine=innodb default charset utf8;
2.单外键,一个字符串+约束foreign key;
constraint: 约束、关键字
fk_cc: 名字
foreign key: 关键字
(deparment_id): 本(自)表中的字段deparment_id
references: 关键字
deparment(id): 来自于deparment表里的id列
create table deparment(
id int not null auto_increment primary key,
title char(32) null
)
create table person(
id int not null auto_increment primary key,
username char(32) null ,
age int not null,
deparment_id int not null,
constraint fk_cc foreign key (deparment_id) references deparment(id)
)
3.多项的外键,一个表对多个表的外键
create table deparment(
id int not null auto_increment primary key,
title char(32) null
)
create table host(
id int not null auto_increment primary key,
ip char(32) null,
port char(32) null
)
create table de_2_host(
id int not null auto_increment primary key,
did int not null,
hid int not null,
constraint fk_did_deparment foreign key (did) references deparment(id),
constraint fk_hid_host foreign key (hid) references host(id)
)
4.连表操作,查询a表关联b表,其中a表里的deparment_id和b表里的id相等:
SELECT * FROM a表 LEFT JOIN b表 ON a表.deparment_id = b表.id;
3.2使用pymysql实现连接数据库:
import pymysql
conn = pymysql.connect(host="192.168.56.11", port=3306, user="baolin", passwd="123456", db="python16") # 创建连接
# cursor = conn.cursor() # 创建游标(默认为元组形式)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 创建游标(会依照字典的格式展现)
# 执行sql时不要使用字符串拼接(以免SQL注入)
effect_row = cursor.execute("SELECT id,name,age FROM tb1 WHERE id > %s", (2,)) # 执行SQL,并返回受影响行数;
# effect_row = cursor.executemany("insert into tb1(name,age)VALUES(%s,%s)", [("xiaoliu", 17), ("tianpeng", 23),])
'''不可用:
username = inpus(>>>:)
pwd = inpus(>>>:)
sql = "select * from tb1 username = %s and pwd = %s" %(username,username)
effect_row = cursor.execute(sql)
如果用户输入的用户:root or 1 == 1 --
得到的语句为:(mysql中--为注释)一下语句就会永远成立
select * from tb1 username = root or 1 == 1 -- and pwd = %s"
'''
# ret = cursor.fetchone() # 获取第一条数据
# ret = cursor.fetchmany(3) # 获取内容的前三行
ret = cursor.fetchall() # 获取所有数据
conn.commit() # 提交操作
print(ret)
cursor.close() # 关闭游标
conn.close() # 关闭连接
new_id = cursor.lastrowid # 涉及到插入时,求出最后插入那条数据的自增ID
print(new_id)