使用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发送email的3种方法
Apr 28 Python
利用python求相邻数的方法示例
Aug 18 Python
python2 与 pyhton3的输入语句写法小结
Sep 10 Python
Python3转换html到pdf的不同解决方案
Mar 11 Python
python仿抖音表白神器
Apr 08 Python
10款最好的Python开发编辑器
Jul 03 Python
python如何给字典的键对应的值为字典项的字典赋值
Jul 05 Python
Python中logging日志库实例详解
Feb 19 Python
python使用Geany编辑器配置方法
Feb 21 Python
Python实现分数序列求和
Feb 25 Python
django queryset 去重 .distinct()说明
May 19 Python
用python给csv里的数据排序的具体代码
Jul 17 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
JavaScript入门之对象与JSON详解
2011/10/21 Javascript
jQuery自带的一些常用方法总结
2014/09/03 Javascript
利用a标签自动解析URL分析网址实例
2014/10/20 Javascript
javascript几个易错点记录
2014/11/26 Javascript
详细解读JavaScript的跨浏览器事件处理
2015/08/12 Javascript
Vue.js学习记录之在元素与template中使用v-if指令实例
2017/06/27 Javascript
Vue使用vue-cli创建项目
2017/09/01 Javascript
Vue2.0设置全局样式(less/sass和css)
2017/11/18 Javascript
Linux Centos7.2下安装nodejs&npm配置全局路径的教程
2018/05/15 NodeJs
vue中实现左右联动的效果
2018/06/22 Javascript
webuploader实现上传图片到服务器功能
2018/08/16 Javascript
vue组件实践之可搜索下拉框功能
2018/11/25 Javascript
15分钟学会vue项目改造成SSR(小白教程)
2019/12/17 Javascript
微信小程序wxs实现吸顶效果
2020/01/08 Javascript
VUE 单页面使用 echart 窗口变化时的用法
2020/07/30 Javascript
springboot+vue+对接支付宝接口+二维码扫描支付功能(沙箱环境)
2020/10/15 Javascript
Python ZipFile模块详解
2013/11/01 Python
Python中的包和模块实例
2014/11/22 Python
PHP实现发送和接收JSON请求
2018/06/07 Python
在django模板中实现超链接配置
2019/08/21 Python
Python程序暂停的正常处理方法
2019/11/07 Python
使用pyshp包进行shapefile文件修改的例子
2019/12/06 Python
如何解决安装python3.6.1失败
2020/07/01 Python
利用pipenv和pyenv管理多个相互独立的Python虚拟开发环境
2020/11/01 Python
英国著名的茶叶品牌:Whittard of Chelsea
2016/09/22 全球购物
Puritan’s Pride(普丽普莱)官方网站:美国最大最全的保健品公司之一
2016/10/23 全球购物
俄罗斯厨房产品购物网站:COOK HOUSE
2021/03/15 全球购物
初中科学教学反思
2014/01/21 职场文书
《海伦?凯勒》教学反思
2014/04/17 职场文书
装配出错检讨书
2014/09/23 职场文书
股东出资证明书(正规版)
2014/09/24 职场文书
离婚协议书怎么写2014
2014/09/30 职场文书
2014年员工工作总结范文
2014/11/18 职场文书
婚宴邀请函
2015/01/30 职场文书
导游词之珠海轮廓
2019/10/25 职场文书
Redis特殊数据类型bitmap位图
2022/06/01 Redis