本文将用实例讲解使用Python3编写简单的服务器程序,实现socket网络编程与MySQL数据库连接。本文的目的在于对网上众多博文的总结,以及对socket编程和MySQL连接与操作的学习总结。 本文不会像众多博文那样,如同教科书一般,详细地讲解socketserver和pymysql模块中有什么东西、各自有什么用,而是实例中用到了什么就讲什么。至于模块的详细讲解与分析,可以看看其他的博文或者源码。
class Database:
def __init__(self, host, user, password, database):
try:
self.conn = pymysql.connect(host, user, password, database)
self.cursor = self.conn.cursor
except:
print("Error: connect mysql error")
def self_sql(self, sql):
try:
self.cursor.execute(sql)
self.conn.commit()
return self.cursor.fetchall()
except:
return -1
Python3使用pymysql模块实现MySQL数据库连接与操作,而Python2连接MySQL需要使用MySQLdb模块。使用pip安装:
pip3 install pymysql
使用前先import。
下面讲解上面的代码:
初始化函数init()需要传递的参数分别为数据库服务器的地址、账户、密码和库名。connect()连接数据库,返回一个Connection对象(有兴趣的可以翻翻源码),之后可以通过该对象的cursor来操作数据库。
在Database类中定义了一个self_sql()函数,参数sql为SQL语句,类型是字符串。execute()执行SQL;fetchall()会获得SQL执行返回的所有结果。若要只获得SQL执行返回的第一个结果,可用fetchone()。SQL执行返回的数据是元组,需要注意处理。
class Server(socketserver.BaseRequestHandler):
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.database = Database("localhost", "root", "123456", "shaking")
print("[" + self.client_address[0] + "] " + "Connect database successfully.")
try:
self.handle()
finally:
print("[" + self.client_address[0] + "] " + "End service.")
def handle(self):
while True:
receive = str(self.request.recv(1024), encoding="utf8").split('|')
print(receive)
if receive[0] == "11":
self.login(receive)
elif receive[0] == "01":
self.game_result(receive)
elif receive[0] == "000":
self.top()
elif receive[0] == "001":
self.register(receive)
else:
break
def login(self, receive):
print("[" + self.client_address[0] + "] " + "Ask to login.")
sql = "select strcmp('" + receive[2] + "', (select passwd from users where username ='" + receive[1] + "'));"
if 0 == self.database.self_sql(sql)[0][0]:
global users
users[self.client_address[0]] = receive[1]
print(users)
self.request.sendall(bytes("1\n", encoding="utf8"))
print("[" + self.client_address[0] + "] " + "Login successfully.")
else:
self.request.sendall(bytes("0\n", encoding="utf8"))
print("[" + self.client_address[0] + "] " + "Fail to login.")
def game_result(self, receive):
print("[" + self.client_address[0] + "] " + "Game Result.")
sql = "select score from users where username = '" + users[self.client_address[0]] + "';"
if int(receive[1]) > self.database.self_sql(sql)[0][0]:
sql = "update users set score = " + receive[1] + " where username = '" + users[self.client_address[0]] + "';"
self.database.self_sql(sql)
def top(self):
print("[" + self.client_address[0] + "] " + "Ask for the Top.")
sql = "select username, score from users order by score desc;"
sends = self.database.self_sql(sql)
for send in sends:
print(send)
self.request.sendall(bytes(send[0] + '|' + str(send[1]) + '\n', encoding="utf8"))
self.request.sendall(bytes('0\n', encoding="utf8"))
def register(self, receive):
print("[" + self.client_address[0] + "] " + "Ask to register.")
sql = "select username from users where username ='" + receive[1] + "';"
if not self.database.self_sql(sql):
sql = "insert into users(username, passwd) values('" + receive[1] + "', '" + receive[2] + "');"
self.database.self_sql(sql)
print("[" + self.client_address[0] + "] " + "Register successfully.")
self.request.sendall(bytes("1\n", encoding="utf8"))
else:
print("[" + self.client_address[0] + "] "
+ ' Fail to register: The name has already existed.')
self.request.sendall(bytes("0\n", encoding="utf8"))
socketserver是socket编程中的高级模块,很多事情(比如bind、listen)已经封装好了。我们可以通过重写setup()、handle()、finish()三个函数来实现服务器的编成。它们分别对应的作用是在连接建立之前完成初始化工作、连接之后的工作、连接断开后的首尾工作。使用前需要导入socketserver模块。
这儿,在handle()中用一while循环实现多次数据传输。recv()用于接收数据,接收后转换为字符串便于处理。实例中,通过if-elif-else实现各功能的选择,也可以通过字典来实现switch的功能。sendall()用于发送数据。在发送的数据中,可以看到有换行符(’\n’),这是因为客户端(Android端)接收用的是readline()。
if __name__ == '__main__':
socketserver.ThreadingTCPServer(("192.168.43.102", 9999), Server).serve_forever()
ThreadingTCPServer()是Threading和TCPServer的组合,从名字也能看出,是多线程和TCP服务器。虽然也不是很明白GIL锁对于Python的多线程有多大的影响,但是这儿使用多线程实现对多个客户端请求的响应。serve_forever()使得程序一直运行。
代码下载