使用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任务调度实例分析
May 19 Python
Python实现简单的文件传输与MySQL备份的脚本分享
Jan 03 Python
Python读取和处理文件后缀为.sqlite的数据文件(实例讲解)
Jun 27 Python
Python WSGI的深入理解
Aug 01 Python
python截取两个单词之间的内容方法
Dec 25 Python
python实现转盘效果 python实现轮盘抽奖游戏
Jan 22 Python
Python基础教程(一)——Windows搭建开发Python开发环境
Jul 20 Python
python批量修改文件名的示例
Sep 27 Python
python函数超时自动退出的实操方法
Dec 28 Python
python连接手机自动搜集蚂蚁森林能量的实现代码
Feb 24 Python
OpenCV-Python模板匹配人眼的实例
Jun 08 Python
Selenium浏览器自动化如何上传文件
Apr 06 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
完整删除ecshop中获取店铺信息的API
2014/12/24 PHP
jQuery+PHP实现图片上传并提交功能
2020/07/27 PHP
定义JavaScript二维数组采用定义数组的数组来实现
2012/12/09 Javascript
6款经典实用的jQuery小插件及源码(对话框/提示工具等等)
2013/02/04 Javascript
JQuery中$.each 和$(selector).each()的区别详解
2015/03/13 Javascript
javascript实现控制的多级下拉菜单
2015/07/05 Javascript
jQuery.extend 函数及用法详细
2015/09/06 Javascript
jQuery实现简单的文件上传进度条效果
2020/03/26 Javascript
jQuery实现分页功能(含ajax请求、后台数据、附完整demo)
2017/04/03 jQuery
深入理解nodejs中Express的中间件
2017/05/19 NodeJs
JavaScript使用atan2来绘制箭头和曲线的实例
2017/09/14 Javascript
ES6知识点整理之函数对象参数默认值及其解构应用示例
2019/04/17 Javascript
vue 2.5.1 源码学习 之Vue.extend 和 data的合并策略
2019/06/04 Javascript
Vue $attrs & inheritAttr实现button禁用效果案例
2020/12/07 Vue.js
关于python写入文件自动换行的问题
2018/06/23 Python
Python编程在flask中模拟进行Restful的CRUD操作
2018/12/28 Python
Python实现12306火车票抢票系统
2019/07/04 Python
Django Admin后台添加数据库视图过程解析
2020/04/01 Python
matplotlib源码解析标题实现(窗口标题,标题,子图标题不同之间的差异)
2021/02/22 Python
html5 桌面提醒:Notifycations应用介绍
2012/11/27 HTML / CSS
Peter Alexander新西兰站:澳大利亚领先的睡衣设计师品牌
2016/12/10 全球购物
英国安全产品购物网站:The Safe Shop
2017/03/20 全球购物
Melijoe美国官网:法国奢侈童装购物网站
2017/04/19 全球购物
Easy Spirit官网:美国休闲鞋履中的代表品牌
2019/04/12 全球购物
什么是托管函数?托管函数有什么用?
2014/06/15 面试题
电大本科自我鉴定
2014/02/05 职场文书
《彭德怀和他的大黑骡子》教学反思
2014/04/12 职场文书
学习雷锋活动总结
2014/04/29 职场文书
工商局副局长个人对照检查材料
2014/09/25 职场文书
2014年班组工作总结
2014/11/20 职场文书
2014年体育工作总结
2014/11/24 职场文书
2015年档案管理员工作总结
2015/05/13 职场文书
2015仓库保管员年终工作总结
2015/05/13 职场文书
2015年学校党建工作总结
2015/05/19 职场文书
演讲稿之感恩老师(三篇范文)
2019/09/06 职场文书
详解CocosCreator项目结构机制
2021/04/14 Javascript