该小游戏,使用tcp服务进行网路通信,包含简单的用户匹配、英雄对抗和战绩显示等功能。该程序比较小,如果某些代码可以优化的,也可以在评论打出来。
工欲善其事,必先利其器。我们要使得整个项目完成,就必须将基地打好,先来回顾一下socket创建TCP的流程:
那么,我们先来完成王者小游戏服务器的搭建:
# Server.py
# -*- coding: utf-8 -*-
# @Auther:Summer
import socket
def main():
# 创建套接字
WangZhe_Server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定相关端口
WangZhe_Server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定端口
WangZhe_Server.bind(("", 8888))
# 该主动为被动
WangZhe_Server.listen(128)
# 等待客户端连接accept
while True:
recv_data = client_socket.recv(1024) # 接收24个字节
if recv_data:
print("接收的数据为:", recv_data.decode("gbk"))
client_socket.send("Thank You!".encode("gbk"))
else:
break
client_socket.close()
WangZhe_Server.close()
if __name__ == '__main__':
main()
测试可以下载一个TCP测试工具,用来查看Server是否可靠。
同样的,你也可以自己写一个客户端来验证:
# Client.py
# -*- coding: utf-8 -*-
# @Auther:Summer
import socket
def main():
# 1.创建套接字
WangZhe_Client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.绑定服务器
WangZhe_Client.connect(("127.0.0.1", 8888))
# 3.发送数据
while True:
send_data = input("请输入要发送的数据:")
if send_data:
WangZhe_Client.send(send_data.encode("gbk"))
print(WangZhe_Client.recv(1024).decode('gbk')) # 打印返回的信息,这个必须写,不然会在server中出现报错信息
else:
break
# 4.关闭套接字
WangZhe_Client.close()
if __name__ == '__main__':
main()
当TCP的最基础完成之后,我们就需要将该代码进行升级一下。我们的代码如果按照现在这样,每次就只能给一个客户端服务,只有当一个客户端断开之后,才能进行第二个,因此,我们想要为多个客户端服务,那就必须使用多进程。
多进程使用multiprocessing
库,在target放入对应的函数名称,tags放入想要的client即可。对于进程间的通信使用的是自己的队列,后面再进行赘述,这里就先展示最简单的一个多进程版本:
# -*- coding: utf-8 -*-
# @Auther:Summer
import socket
import multiprocessing
# 收发信息
def recv(client_socket):
while True:
recv_data = client_socket.recv(1024) # 接收24个字节
if recv_data:
print("接收的数据为:", recv_data.decode("gbk"))
client_socket.send("Thank You!".encode("gbk"))
else:
break
client_socket.close()
def main():
# 创建套接字
WangZhe_Server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定相关端口
WangZhe_Server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定端口
WangZhe_Server.bind(("", 8888))
# 该主动为被动
WangZhe_Server.listen(128)
try:
# 等待客户端连接accept
while True:
# 接受相应
wz_client, clint_addr = WangZhe_Server.accept()
# 注册多进程,
p = multiprocessing.Process(target=recv, args=(wz_client,))
p.start()
wz_client.close()
except:
# 关闭套接字
WangZhe_Server.close()
if __name__ == '__main__':
main()
以上就是最基础的一个socket的版本,为了适应后期的代码版本,我们把这个变成面向对象版本。
面向对象还比较简单,首先是init的时候先设置最基础的socket配置,然后设置一个函数用来进行数据的收发:
# Server.py
# -*- coding: utf-8 -*-
# @Auther:Summer
import socket
import multiprocessing
class WangZheServer:
def __init__(self):
# 创建套接字
self.WangZhe_Server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定相关端口
self.WangZhe_Server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定端口
self.WangZhe_Server.bind(("", 8888))
# 该主动为被动
self.WangZhe_Server.listen(128)
def runferver(self):
try:
# 等待客户端连接accept
while True:
# 接受相应
wz_client, clint_addr = self.WangZhe_Server.accept()
# 注册多进程,
p = multiprocessing.Process(target=self.recv, args=(wz_client,))
p.start()
wz_client.close()
except:
# 关闭套接字
self.WangZhe_Server.close()
# 收发信息
def recv(self, client_socket):
while True:
recv_data = client_socket.recv(1024) # 接收24个字节
if recv_data:
print("接收的数据为:", recv_data.decode("gbk"))
client_socket.send("Thank You!".encode("gbk"))
else:
break
client_socket.close()
def main():
wangzhe = WangZheServer()
wangzhe.runferver()
if __name__ == '__main__':
main()
# Client.py
# -*- coding: utf-8 -*-
# @Auther:Summer
import socket
class WangZheClient:
def __init__(self):
# 1.创建套接字
self.WangZhe_Client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.绑定服务器
self.WangZhe_Client.connect(("127.0.0.1", 8888))
def runforver(self):
# 3.发送数据
while True:
send_data = input("请输入要发送的数据:")
if send_data:
self.WangZhe_Client.send(send_data.encode("gbk"))
print(self.WangZhe_Client.recv(1024).decode('gbk')) # 打印返回的信息,这个必须写,不然会在server中出现报错信息
else:
break
# 4.关闭套接字
self.WangZhe_Client.close()
def main():
wangzheClient = WangZheClient()
wangzheClient.runforver()
if __name__ == '__main__':
main()
我们的主体部分就是要实现登录、选择模式、人员匹配、对战模式和最后的对战信息展示
由于socket无法传送字典和元组,因此我们就需要规定一下信息的传输格式:收发数据模式step:data
然后来规定一下每一步都需要进行什么操作:
1. 登录 step == 0
2. 模式选择 step == 1
3. 人员匹配(管道) step == 2
4. 英雄选择 step == 3
5. 开始对战 step == 4
6. 展示结果 step == 5 -> step == 0
7. 服务器内部错误 step == 500