社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

使用Python开发SQLite代理服务器

Python小屋 • 9 年前 • 863 次点击  

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小屋”公众号近期主要活动:

1、赠书活动:详情请进入公众号以后通过菜单“最新资源”===>“历史文章分类表”进行查看

2、Python师资培训班:8月6日-12日,济南,面向全国高校老师和企业朋友,通知详见关于举办2017年暑期全国高校教师 “Python编程及应用”培训班通知



今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/ZV27rGfZWR
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/2421