使用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对html代码进行escape编码的方法
May 04 Python
轻量级的Web框架Flask 中模块化应用的实现
Sep 11 Python
python爬虫_实现校园网自动重连脚本的教程
Apr 22 Python
Python中的CSV文件使用"with"语句的方式详解
Oct 16 Python
django 多对多表的创建和插入代码实现
Sep 09 Python
Python ORM框架Peewee用法详解
Apr 29 Python
Python如何使用队列方式实现多线程爬虫
May 12 Python
解决python执行较大excel文件openpyxl慢问题
May 15 Python
利用Python实现某OA系统的自动定位功能
May 27 Python
Python decimal模块使用方法详解
Jun 08 Python
django ObjectDoesNotExist 和 DoesNotExist的用法
Jul 09 Python
python缺失值的解决方法总结
Jun 09 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
Protoss兵种对照表
2020/03/14 星际争霸
php中smarty变量修饰用法实例分析
2015/06/11 PHP
jQuery+Ajax+PHP“喜欢”评级功能实现代码
2015/10/08 PHP
PHP二维数组去重算法
2016/12/17 PHP
Laravel5.* 打印出执行的sql语句的方法
2017/07/24 PHP
PHP开发中解决并发问题的几种实现方法分析
2017/11/13 PHP
JS 无限级 Select效果实现代码(json格式)
2011/08/30 Javascript
jquery ajax修改全局变量示例代码
2013/11/08 Javascript
jquery插件jquery倒计时插件分享
2013/12/27 Javascript
js document.write()使用介绍
2014/02/21 Javascript
jQuery函数的第二个参数获取指定上下文中的DOM元素
2014/05/19 Javascript
理解javascript闭包
2015/12/15 Javascript
全面解析Bootstrap中tab(选项卡)的使用方法
2016/06/06 Javascript
javascript中使用未定义变量或值的情况分析
2016/07/19 Javascript
简单实现js悬浮导航效果
2017/02/05 Javascript
ES6新特性六:promise对象实例详解
2017/04/21 Javascript
详解nodeJS之二进制buffer对象
2017/06/03 NodeJs
jQuery实现checkbox即点即改批量删除及中间遇到的坑
2017/11/11 jQuery
JavaScript数组及常见操作方法小结
2019/11/13 Javascript
vue.js+element 默认提示中英文操作
2020/11/11 Javascript
寻找网站后台地址的python脚本
2014/09/01 Python
举例讲解Linux系统下Python调用系统Shell的方法
2015/11/07 Python
python运行时间的几种方法
2016/06/17 Python
Python数据结构与算法之图的基本实现及迭代器实例详解
2017/12/12 Python
Python3利用Dlib19.7实现摄像头人脸识别的方法
2018/05/11 Python
python爬虫之验证码篇3-滑动验证码识别技术
2019/04/11 Python
Pycharm如何打断点的方法步骤
2019/06/13 Python
python获取百度热榜链接的实例方法
2020/08/25 Python
深入浅析css3 border-image边框图像详解
2015/11/24 HTML / CSS
Html5实现单张、多张图片上传功能
2019/04/28 HTML / CSS
详解HTML5之pushstate、popstate操作history,无刷新改变当前url
2017/03/15 HTML / CSS
KOHLER科勒美国官网:国际著名卫浴橱柜领先品牌
2020/06/27 全球购物
历史系毕业生自荐信
2013/10/28 职场文书
创业女性典型材料
2014/05/02 职场文书
市政工程技术专业自荐书
2014/07/06 职场文书
SpringBoot深入分析讲解监听器模式下
2022/07/15 Java/Android