使用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中用datetime包进行对时间的一些操作
Jun 23 Python
利用Python命令行传递实例化对象的方法
Nov 02 Python
Python 基础之字符串string详解及实例
Apr 01 Python
python好玩的项目—色情图片识别代码分享
Nov 07 Python
python 获取list特定元素下标的实例讲解
Apr 09 Python
Python3.5内置模块之random模块用法实例分析
Apr 26 Python
Python3进制之间的转换代码实例
Aug 24 Python
Django框架下静态模板的继承操作示例
Nov 08 Python
基于Python 中函数的 收集参数 机制
Dec 21 Python
Python如何脚本过滤文件中的注释
May 27 Python
PyCharm vs VSCode,作为python开发者,你更倾向哪种IDE呢?
Aug 17 Python
教你利用python实现企业微信发送消息
May 23 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&mysql(六)
2006/10/09 PHP
mysql5写入和读出乱码解决
2006/11/25 PHP
PHP setcookie() cannot modify header information 的解决方法
2009/01/09 PHP
PHP 数组教程 定义数组
2009/10/23 PHP
mod_php、FastCGI、PHP-FPM等PHP运行方式对比
2015/07/02 PHP
Zend Framework开发入门经典教程
2016/03/23 PHP
PHP入门教程之正则表达式基本用法实例详解(正则匹配,搜索,分割等)
2016/09/11 PHP
ajax+php实现无刷新验证手机号的实例
2017/12/22 PHP
laravel框架分组控制器和分组路由实现方法示例
2020/01/25 PHP
扩展jQuery 键盘事件的几个基本方法
2009/10/30 Javascript
一个背景云变换js特效 鼠标移动背景云变化
2012/12/28 Javascript
在JavaScript里嵌入大量字符串常量的实现方法
2013/07/07 Javascript
深入理解javascript变量声明
2014/11/20 Javascript
js 递归和定时器的实例解析
2017/02/03 Javascript
详解vue-cli 脚手架项目-package.json
2017/07/04 Javascript
vue中路由验证和相应拦截的使用详解
2017/12/13 Javascript
NodeJS安装图文教程
2018/04/19 NodeJs
Vue v-text指令简单使用方法示例
2019/09/19 Javascript
vue.js自定义组件实现v-model双向数据绑定的示例代码
2020/01/08 Javascript
微信小程序实现电子签名功能
2020/07/29 Javascript
vue中利用three.js实现全景图的完整示例
2020/12/07 Vue.js
使用pandas把某一列的字符值转换为数字的实例
2019/01/29 Python
Python 数据库操作 SQLAlchemy的示例代码
2019/02/18 Python
解决pandas展示数据输出时列名不能对齐的问题
2019/11/18 Python
Python之Sklearn使用入门教程
2021/02/19 Python
html5使用canvas实现弹幕功能示例
2017/09/11 HTML / CSS
美国校服网上商店:French Toast
2019/10/08 全球购物
护士自我评价范文
2014/01/25 职场文书
会计专业求职信范文
2014/03/16 职场文书
小学“向国旗敬礼”网上签名寄语活动总结
2014/09/27 职场文书
关于调整工作时间的通知
2015/04/24 职场文书
财务管理制度范本
2015/08/04 职场文书
写好求职信的技巧解密
2019/05/14 职场文书
仅用一句SQL更新整张表的涨跌幅、涨跌率的解决方案
2021/05/06 MySQL
Python 中random 库的详细使用
2021/06/03 Python
MySQL系列之七 MySQL存储引擎
2021/07/02 MySQL