本次开发的是一个基于Socket编程、GUI编程、多线程编程等技术的网络程序,实现的是两个玩家联机比赛走迷宫的功能。
设计说明
本程序采用Java程序设计语言,在Eclipse平台下编辑、编译与调试。通过Socket编程技术实现客户端之间、客户端与服务器之间的通信,通过GUI编程技术实现窗口显示,通过多线程技术实现多用户同时使用。
具体实现的功能如下:
使用基本的Client-Server模型。用户使用Client端进行连接时,将会跳出弹窗提示输入服务器的IP地址和自定义的用户名与Server端连接。如果服务器未开启,将跳出错误提示。
当两个用户都完成连接之后,显示完整地图,比赛开始。角色将出现在地图右上角,游戏的终点在地图左下角。窗口的左边显示自己的地图,右边显示对方的游戏状态。
用户可以通过方向键控制角色移动,角色不能穿越障碍物。
比赛过程中可以看到对方的当前位置、移动信息、比赛进度等。
当有一方先到达终点,比赛结束。胜负方可以看到各自的胜负信息。
总体设计
功能模块设计
本程序需实现的主要功能有:
- 使用Socket进行通信,服务器开启之后,客户端可以申请连接。
- 客户端与服务器之间实现信息交换。
- 使用GUI显示对手以及自己的游戏界面。
- 多线程操作,服务器可以同时管理多个客户端
程序的总体功能如图1所示:
流程图设计
程序总体流程如图2所示:
详细设计
GUI图形显示
在本项目中,使用java的图形界面组件来实现地图和角色的显示、角色位置的刷新以及弹窗的显示。要注意的是,在JFrame组件的显示时,在相同位置上进行组件叠加时,只能够显示添加的第一个组件,这给角色的位置刷新带来一定难度和格式限制。
其具体实现如下所示:
地图的显示:
1 | static JFrame frame = new JFrame("test"); |
角色位置的刷新:
1 | player = new JLabel(); |
弹窗显示:
在本次项目中,使用弹窗来输入连接的IP和用户名信息,并在游戏结束之后使用弹窗来提示游戏结果。实现方式如下:
1 | JOptionPane.showMessageDialog(frame, "YOU WIN!!", "Message", JOptionPane.INFORMATION_MESSAGE); |
游戏逻辑
实现迷宫的游戏逻辑较为简单,将地图信息使用二维数组进行存储。在角色进行移动时,读取角色下一个位置的地图信息,如果存在障碍物,将无法通过,否则将能够继续通过。在角色进行移动之后,在窗口中刷新角色位置。如果先于对方角色移动到终点,则获胜,游戏结束。
Socket连接
网络相关的模块分成两部分,一部分是连接,另一部分是通信。
连接部分,客户端填写要连接的IP地址,作为TCP报文段中的字段,然后通过TCP连接上服务器, 并把自己的UDP端口号发送给服务器。服务器通过TCP和客户端连上后收到客户端的UDP端口号信息, 并将客户端的IP地址和UDP端口号封装成一个Client对象, 保存在容器中。
因为服务器收到链路层帧后会提取出网络层数据报, 源地址的IP地址在IP数据报的首部字段中, Java对这一提取过程进行了封装, 所以我们能够直接在Java的api中获取源地址的IP。
服务器封装完Client对象后, 为客户端的主机分配一个id号, 这个id号将用于往后游戏的网络传输中进行标识。
同时服务器也会把自己的UDP端口号发送客户端, 因为服务器自身会开启一条UDP线程, 用于接收转发UDP包。
客户端收到坦克id后设置到自己的主战坦克的id字段中. 并保存服务器的UDP端口号。
这部分的实现方式如下:
客户端部分:
1 | public void connect(String ip){ |
服务器部分:
1 | public void start(){ |
Socket通信
Socket通信通过自定义应用层的协议实现,每个应用层协议有消息类型和消息数据两个部分组成,不同的协议数据格式不同。
在本项目中,总共有四种传递协议,分别对应游戏逻辑中的连接、移动、获胜(其中连接部分需要两个协议,新加入的玩家通知已存在的玩家,已存在的玩家给新玩家提供响应,从而分别在两者的地图上添加对方的信息)。
在本项目中,设计Msg接口,定义应用层协议的格式,每个协议在此基础上定义具体的实现类,通过多态进行实现。
Msg接口的定义如下:
1 | public interface Msg { |
连接部分的协议,新用户发送的连接信息用connectMSG类进行实现,消息数据包含用户id、用户名字段。在新用户连接成功后进行发送
1 | public void send(DatagramSocket ds, String IP, int UDP_Port){ |
已有用户的响应信息用connectToOriMsg进行实现,协议格式实现方式与connectMsg类似,包含用户id和用户名两个字段,在已有用户收到新用户的连接信息之后发送。
1 | public void send(DatagramSocket ds, String IP, int UDP_Port){ |
移动的信息通过MoveMsg协议进行传输,每个用户在操纵角色进行移动之后,要像对手发送信息报告角色的新位置,并在对方的地图上刷新角色位置。该协议数据包含用户id、角色新位置的水平、垂直坐标。
1 |
|
传递获胜信息的协议在WinMsg中定义。当一方角色移动到终点时,发送获胜信息,并在当前窗口中弹窗提示获胜。另一方的客户端接收到对方的获胜信息后,弹窗提示失败信息。游戏结束。该协议中的数据仅包含获胜者的用户id。
1 |
|
测试与运行
程序运行
在程序代码基本完成后,经过不断的调试与修改,能够完成上述功能。
运行客户端会跳出连接提示输入服务器IP和用户名进行连接:
连接成功后客户端和服务器的命令行提示如下所示:
客户端1:
客户端2:
服务器:
服务器端可视化情况如下:
程序测试
游戏过程的测试如下所示:
如果服务器未开启将提示连接错误:
连接成功后将等待另一个用户连接,此时的游戏界面如下:
左边是自己的地图,在对方用户连接成功后,将在右边显示对方的游戏界面,游戏开始的初始状态如下所示:
游戏进行一段时间后,左边是自己的游戏状态,右边是对方的游戏状态:
率先到达终点的用户将获得获胜提示:
与此同时在对方的游戏界面将提示失败信息:
完整代码请见github,欢迎star :)