使用Python开发SQLite代理服务器的方法


Posted in Python onDecember 07, 2018

SQLite数据库使用单个磁盘文件,并且不需要像Oracle、MSSQL、MySQL等数据库管理系统那样启动服务,使用非常灵活方便。但是SQLite也有个很严重的问题,就是没有相应的服务,也没有监听任何端口,因此相应的程序只能访问本地数据库。也就是说,无法分离程序和数据库,只能把程序和数据库放在同一台计算机上。

本文使用Python开发了一个SQLite数据库的服务程序,可以完美地分离程序和数据库。技术要点是Socket编程,在数据库服务器上运行服务程序,该服务程序监听特定端口、执行代理程序发来的SQL语句并返回结果;代理程序负责接收客户端的SQL语句并转发给服务器,然后再把服务器返回的结果转发给客户端。在具体使用时可以在本文代码基础上进行简化和扩展。

服务程序:

##
# 服务器程序,接收代理服务器转发来的SQL指令,并返回结果
#
import sqlite3
import socket
import struct

def getData(sql):
 '''通过给定的SQL SELECT语句返回结果'''
 with sqlite3.connect(r'database.db') as conn:
  cur = conn.cursor()
  cur.execute(sql)
  result = cur.fetchall()
 return result

def doSql(sql):
 '''适用于DELETE/UPDATE/INSERT INTO语句,返回影响的记录条数'''
 with sqlite3.connect(r'database.db') as conn:
  cur = conn.cursor()
  result = cur.execute(sql)
 return result.rowcount

# 创建socket对象,默认使用IPV4+TCP
sockServer = socket.socket()
sockServer.bind(('', 3030))
sockServer.listen(1)
while True:
 # 接收客户端连接
 try:
  conn, addr = sockServer.accept()
 except:
  continue
 
 sql = conn.recv(1024).decode('gbk').lower()
 
 if sql.startswith(('update','delete','insert')):
  try:
   # 首先发送要发送的字节总数量
   # 然后再发送真实数据
   result = str(doSql(sql)).encode('gbk')
   conn.send(struct.pack('i', len(result)))
   conn.send(result)
  except:
   message = b'error'
   conn.send(struct.pack('i', len(message)))
   conn.send(message)
 elif sql.startswith('select'):
  try:
   result = str(getData(sql)).encode('gbk')
   conn.send(struct.pack('i', len(result)))
   conn.send(result)
  except:
   message = b'error'
   conn.send(struct.pack('i', len(message)))
   conn.send(message)

代理程序:

##
# 代理服务器,在SQLite数据库服务器和客户端之间进行指令和数据的转发
# 这样可以把数据库和程序放到两个服务器上进行分离
##
import socket
from threading import Thread
import struct

sockServer = socket.socket()
sockServer.bind(('',5050))
sockServer.listen(50)

def agent(conn):
 # 接收客户端发来的指令,并进行过滤
 sql = conn.recv(1024)
 if not sql.decode('gbk').startswith(('select', 'delete', 'insert','update')):
  message = b'not a sql statement'
  conn.send(struct.pack('i', len(message)))
  conn.send(message)
  return
 else:
  sockClient = socket.socket()
  # 尝试连接服务器
  try:
   sockClient.connect(('10.2.1.3', 3030))
  except:
   message = b'Server not alive'
   conn.send(struct.pack('i', len(message)))
   conn.send(message)
   return
   
  # 向服务程序转发SQL语句
  sockClient.send(sql)
  # 数据量大小,使用sturct序列化一个整数需要4个字节
  size = sockClient.recv(4)
  conn.send(size)
  size = struct.unpack('i', size)[0]
  while True:
   if size == 0:
    break
   elif size > 4096:
    data = sockClient.recv(4096)
    conn.send(data)
    size -= len(data)
   else:
    data = sockClient.recv(size)
    conn.send(data)
    size -= len(data)
  sockClient.close()
 conn.close()

while True:
 conn, _ = sockServer.accept()
 Thread(target=agent, args=(conn,)).start()

模拟客户端程序:

##
# 模拟客户端,向SQLite代理服务器发送指令并接收数据
#
import sqlite3
import socket
import struct

while True:
 sql = input('输入一个要执行的SQL语句:\n')
 # 没有输入,进入下一次循环
 if sql.strip() == '':
  continue
 
 # 输入exit或quit,退出客户端
 if sql in ('exit', 'quit'):
  break
 # 建立socket,尝试连接
 sockClient = socket.socket()
 try:
  sockClient.connect(('10.2.1.3', 5050))  
 except:
  print('服务器异常,请检查')
 else:
  # 发送远程SQL语句
  sockClient.send(sql.encode('gbk'))
  size = sockClient.recv(4)
  size = struct.unpack('i', size)[0]
  
  data = b''
  while True:
   if size == 0:
    break
   elif size > 4096:
    # 注意断包和粘包
    # 虽然设置了4096,但是不一定能够接收4096字节
    #即使缓冲区的数据远多于4096
    t = sockClient.recv(4096)
    data += t
    size -= len(t)
   else:
    t = sockClient.recv(size)
    data += t
    size -= len(t)
  data = data.decode('gbk')
  try:
   data = eval(data)
  except:
   pass
  sockClient.close()
  print(data)

以上这篇使用Python开发SQLite代理服务器的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python网络编程学习笔记(二):socket建立网络客户端
Jun 09 Python
Python解惑之True和False详解
Apr 24 Python
django之session与分页(实例讲解)
Nov 13 Python
Python 实现网页自动截图的示例讲解
May 17 Python
Python利用ORM控制MongoDB(MongoEngine)的步骤全纪录
Sep 13 Python
使用python对多个txt文件中的数据进行筛选的方法
Jul 10 Python
python异常触发及自定义异常类解析
Aug 06 Python
浅谈python元素如何去重,去重后如何保持原来元素的顺序不变
Feb 28 Python
Jmeter HTTPS接口测试证书导入过程图解
Jul 22 Python
python的flask框架难学吗
Jul 31 Python
Django中的JWT身份验证的实现
May 07 Python
刚学完怎么用Python实现定时任务,转头就跑去撩妹!
Jun 05 Python
解决python 未发现数据源名称并且未指定默认驱动程序的问题
Dec 07 #Python
Python中collections模块的基本使用教程
Dec 07 #Python
对python 操作solr索引数据的实例详解
Dec 07 #Python
python用post访问restful服务接口的方法
Dec 07 #Python
python3 实现验证码图片切割的方法
Dec 07 #Python
python 用opencv调用训练好的模型进行识别的方法
Dec 07 #Python
Python cv2 图像自适应灰度直方图均衡化处理方法
Dec 07 #Python
You might like
php的urlencode()URL编码函数浅析
2011/08/09 PHP
探讨多键值cookie(php中cookie存取数组)的详解
2013/06/06 PHP
PHP 5.3新增魔术方法__invoke概述
2014/07/23 PHP
php根据用户语言跳转相应网页
2015/11/04 PHP
Laravel框架使用Seeder实现自动填充数据功能
2018/06/13 PHP
Javascript读取cookie函数代码
2010/10/16 Javascript
ASP.NET jQuery 实例8 (动态添加内容到DropDownList)
2012/02/03 Javascript
js创建子窗口并且回传值示例代码
2013/07/02 Javascript
javascript实现回车键提交表单方法总结
2015/01/10 Javascript
动态加载js的方法汇总
2015/02/13 Javascript
jquery实现表单输入时提示文字滑动向上效果
2015/08/10 Javascript
js省市联动效果完整实例代码
2015/12/09 Javascript
jQuery配合coin-slider插件制作幻灯片效果的流程解析
2016/05/13 Javascript
js制作支付倒计时页面
2016/10/21 Javascript
Vue单页式应用(Hash模式下)实现微信分享的实例
2017/07/21 Javascript
nodejs实现连接mongodb数据库的方法示例
2018/03/15 NodeJs
详解使用VueJS开发项目中的兼容问题
2018/08/02 Javascript
JS 逻辑判断不要只知道用 if-else 和 switch条件判断(小技巧)
2020/05/27 Javascript
基于JavaScript实现轮播图效果
2021/01/02 Javascript
Python遍历zip文件输出名称时出现乱码问题的解决方法
2015/04/08 Python
简单谈谈Python中的几种常见的数据类型
2017/02/10 Python
spark dataframe 将一列展开,把该列所有值都变成新列的方法
2019/01/29 Python
Django 过滤器汇总及自定义过滤器使用详解
2019/07/19 Python
python如何把字符串类型list转换成list
2020/02/18 Python
对Matlab中共轭、转置和共轭装置的区别说明
2020/05/11 Python
详解Flask前后端分离项目案例
2020/07/24 Python
用Python 爬取猫眼电影数据分析《无名之辈》
2020/07/24 Python
Python __slots__的使用方法
2020/11/15 Python
Staples加拿大官方网站:办公用品一站式采购
2016/09/25 全球购物
Scholastic父母商店:儿童书籍
2017/01/01 全球购物
推荐信格式范文
2014/05/09 职场文书
初中学习计划书范文
2014/09/15 职场文书
2015年爱国卫生工作总结
2015/04/22 职场文书
《鲁滨逊漂流记》之六读后感(4篇)
2019/09/29 职场文书
分享CSS盒子模型隐藏的几种方式
2022/02/28 HTML / CSS
Python Matplotlib绘制两个Y轴图像
2022/04/13 Python