用python3从零开始开发一款烧脑射击游戏#5
(这一讲需要了解一点计算机网络和线程的基础概念)
上回说到在游戏中加入了子弹,但是游戏里只有一个人,怎么才能打到其他玩家呢?
所以今天我们需要做的就是让别的玩家可以跟我一起玩这个游戏。
但是人是在别人的电脑里玩,他怎么就知道我控制小方块移动了呢?还有发射了子弹呢以及某个人被子弹击中了呢?
他不知道的话,我可以告诉他嘛
而“告诉他”的这种通俗的表达方式,在计算机领域被称为通信,而用计算机网络作为媒介的通信就被称为网络通信
另外,为了避免玩家之前的互不信任,我们采用这样一种模式:
每个玩家把自己的所有操作通知给一个中心服务器
中心服务器对每个玩家的操作进行合法性判断,如果合法的话就负责实现和模拟这些操作发生的过程以及结果
服务器会不断的把在自己内部发生的这些过程和结果都通知给每个玩家
这个就是C/S模式(客户端client/服务器server)
下面我们来看怎么用python代码来实现这种通信
老实说网络编程的原理很复杂,但python封装的很完善,所以了解下面这些概念就可以进行基础的开发了:
可以简单的把计算机网络中收发消息的过程看成现实世界中的收发信件:
A告诉别人自己的邮政编码和收件地址
B写明要发给的人(A)的邮政编码和地址,然后交给邮局
邮递员根据邮政编码和地址把信件送到A的手上
很简单对不对。而计算机网络中,用一个叫做socket的东西来描述计算机世界中的“收件地址”
而“信件”就是一些二进制数据
我们看下对应的代码实现
import socket#导入socket库
对于客户端(某种意义上可以理解成“发件人”)
ADDR = ("127.0.0.1", 11000)#指明目标的网络地址(ip和端口)
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#创建一个socket
clientsocket.connect(ADDR)#与目标socket建立连接,连接成功够就可以像目标服务器发送数据了
#建立连接之后
data = input(">>>")#从窗口输入要发送的内容
clientsocket.send(bytes(data,'utf-8'))#按utf-8编码发送数据
对于服务器(某种意义上可以理解成“收件人”)
ADDR = ("127.0.0.1", 11000)#指明自己的网络地址(ip和端口)
serversocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建一个socket
serversocket.bind(ADDR)#socket绑定到地址上
绑定完成后就是等待远程的客户端连接
clientsocket,addr = serversocket.accept()
#连接成功后
data = clientsocket.recv(BUFSIZE)#通过与对应客户端的连接的socket接收数据
但是这个模型有两个缺陷:
1.服务器启动后需要一直等待客户端的连接,以及等待客户端发送数据
accept() recv()这两个函数,会让程序的执行停在这个地方,直到有客户端连接成功了或者发送了数据后才会执行后面的代码
由于服务还有很多其他重要的事要做,我们可不想让服务器的工作被等待客户端给全部耽误了
2.服务器只能接受一个客户端的连接,无法处理多个客户端同时连接
解决这两个问题有很多种方案,比如使用异步的库(asyncio),或者用selector库来轮询等等
而我打算采用多线程的方法来解决这个问题:
1.主线程处理游戏的表现
2.若干个副线程处理网络的连接和接受客户端的数据,副线程在挂起等待网络事件时,不会影响主线程的游戏逻辑
如果不太理解上面的这种模式,可以想象成本来你是一个人(主线程)工作,但是有很多客户要联系,你不得不等待客户的反馈才能继续做其他的工作。
后来你实在等的不耐烦了,就雇了很多帮手(副线程),让帮手去跟客户联系,然后他们把得到的结果反馈给你,这样你就可以专心做自己的事了。只需要在助手跟你反馈的时候抽出一点时间来对接一下
下面我们看代码的实现:
首先定义一个函数来处理等待消息的接收:
def deal_msg(client):
while True:
data = client.recv(1024)
data = data.decode('utf-8')
print(data)
再来定义一个函数来处理等待客户端的连接,以及连接上了接收消息
def acc_clt():
while True:
clt, addr = g_socket_server.accept()#阻塞等待新的连接
print("one client connect!")
g_conn_pool.append(clt)#将连接上的客户端加入连接池
thread = Thread(target=deal_msg,args=(clt,))#创建一个新的线程,用来处理刚连进来的这个客户端的消息
thread.setDaemon(True)
thread.start()#新线程跑起来
定义好处理等待连接的函数后,我们可以创建一个线程来专门做这件事
thread = Thread(target=acc_clt)#等待连接的线程
thread.setDaemon(True)
thread.start()
由于这篇的内容有点多,如何把这个网络模型应用到游戏中将在下一篇中讲述