郭骁 陆海燕 陶星州 王浩
摘要:针对典型聊天室开发中所涉及的服务端与客户端程序设计、数据同步、客户端UI设计、群组管理、扫码进群方面的基本技术做了较为详尽的介绍,详细地分析了各个功能模块的实现方法,提出了一系列在Unity3D开发客户端程序的设计思路,为在Unity3D平台实现网络聊天功能提供了较为完整的解决方案,并以实例证明了该方案的可行性。
关键词:Unity3D;聊天室;服务端;客户端;扫码进群;群组管理
中图分类号:TP311 文献标识码:A
文章编号:1009-3044(2019)15-0073-03
Abstract: This paper introduces in detail the basic technologies of client and server programming, data synchronization, client UI design, group management, scanning and grouping involved in the development of typical chat rooms, analyses in detail the implementation methods of each functional module, and puts forward a series of design ideas for developing client program in Unity3D, in order to realize network chat on Unity3D platform. The sky function provides a more complete solution, and the feasibility of the solution is proved by an example.
Key words: U3D; chat room; server; client; sweep code into group; group management
1 背景
隨着网络通信技术的迅猛发展,特别是微信、QQ和钉钉等社交软件的应用普及,采用聊天室方式的信息沟通成为生活与工作中的重要需求。在各种实际工作场景中,聊天室这一用于实现消息通知、文件及音视频传输的应用,已经成为一种基本功能。通常一个具体的网络应用程序都拥有属于自己的用户群体,而聊天室正是用户群体间信息交流中不可或缺的一部分。
Unity3D作为一款游戏引擎,正在越来越多地被应用于游戏之外的各种领域如建筑、汽车、医学、教育等传统行业;同时Unity3D也是VR技术发展中一股重要的力量,可以预见在不远的将来,会出现各种具有聊天室功能并以Unity3D为平台的网络应用程序。本文采用面向客户/服务器模型(Client/Server,C/S)设计的Socket技术[1],基于Unity3D平台,探索典型聊天室开发的解决方案。本文开发环境采用Windows10操作平台,MySQL5.6Server数据库和Unity2018.3.10f1 (64-bit)个人版。
2 总体方案设计
2.1 需求分析
在本项目中,将基于Unity平台实现网络聊天室的基本功能,包括登录、群聊、群组管理、扫码进群等。
2.2 基本设计概念和处理流程
服务器首先将Socket初始化,绑定指定的端口,进行端口监听,调用BeginAccept方法实现异步接收,等待客户端连接,同时服务器连接到MySQL数据库,读取数据库中信息,载入历史配置。当有客户端发出连接请求时,服务器将实例化一个Conn对象至连接池,并初始化Conn对象的Socket,如果连接成功,便可以在客户端与服务端之间传输数据。服务端接收客户端发送的数据请求并处理,处理结束后将处理结果发送给指定客户端,该客户端接收到服务端发来的数据,并利用该数据执行各种业务逻辑。当结束数据传输之后则关闭连接,客户端与服务端不再进行交互。
该聊天室采用面向连接的传输控制协议,即TCP(Transmission Control Protocol),在传输数据之前必须先建立连接[2],完成数据输出之后应释放连接。服务器采用异步接收,可以并发处理多个用户的连接请求,需要注意的是,每一次开辟新的线程来处理连接时,都需要向系统申请内存,随着连接数的增加,系统内存损耗也会迅速上升,因此当客户端不再传输数据时要及时销毁线程,释放内存。
3 服务端设计
在服务端程序中首先需要绑定一个固定端口并监听来自客户端的连接,同时连接到数据库并同步历史数据(如群组信息、资源文件路径等),初始化连接池,等待接收来自客户端的连接。
在客户端发出连接请求时,服务端与该客户端建立Socket连接,若连接成功,将在连接池中实例化Conn对象,同时开辟一个新的线程来处理服务端与该客户端的连接。使用多线程的策略是因为服务端需要同时处理多个客户端的连接[3-4],在实际应用场景中,在同一时刻往往会有大量客户端访问同一服务端,使用单线程逐个响应显然在效率上是无法接受的。
在成功建立连接后,服务端将处理来自客户端的各种请求,并在处理完成将结果回传给客户端,如果这些请求中包含了数据库相关的操作,服务端将访问启动时连接的数据库,对数据库进行增、删、改、查等操作。
3.1 绑定端口
实例化ServNet对象,并调用Start函数,在该函数中,将调用Bind方法绑定端口与具体的IP地址,在本地测试时将使用本机地址127.0.0.1,若需要在公网状态下测试,需要重新配置Socket。在服务端程序中提供了手动配置的接口,测试人员可以通过命令行对正在运行的服务端程序进行重启程序、重置端口等操作,便于在不同环境下的调试。
3.2 建立连接
在成功启动服务器后,程序将自动调用BeginAccept方法,与发出连接请求的客户端建立连接[5],如果出现连接失败的情况,程序将抛出异常,并在连接池中销毁错误的Conn对象。在BeginAccept方法中需要传入一个回调函数,在该回调函数中将继续调用BeginAccept方法,实现服务端不断接收客户端的连接请求,同时调用BeginReceive方法,不断接收客户端发送的信息。
3.3 响应处理
针对客户端的各种数据请求,服务端将采取不同的处理策略,对于聊天内容,服务端首先将发送者ID、内容、所属群组、发送时间等信息整合后存入数据库作为聊天记录,然后遍历该消息所属群组的成员,将消息广播至所有群组成员所属的客户端。对于命令类的数据请求,如登录账号、创建群组、管理群组等,服务端首先判断该命令是否有效,确认为合法命令后解析命令字段,获取命令的具体类型,然后在ProcessData方法中使用分支结构,对不同命令进行相应的处理,然后将处理结果发回该客户端,供客户端执行后续的操作。
4 聊天室典型功能的实现
聊天室典型功能有登录账号、建立群组、群聊及群员管理等,下面介绍具体实现。
1)登录功能的实现
系统将所有用户的信息组织为一张表userdata,该表存放在与服务端相连的数据库msgboard中。用户输入账号与密码后单击Login按钮,客户端向服务器发出登录请求,在收到登录请求后,服务端将根据客户端提供的账号信息与数据库中的信息进行验证,并回传验证结果。验证成功后,服务器会为发出请求的Conn对象初始化User信息(包括用户名、权限等级等),将该用户设置为在线状态,从而实现用户的登录。
2)群聊功能的实现
群聊功能可以拆分为创建群组、发送信息、接收信息、消息提示几个部分,下面将分别论述各部分功能的具体实现方法。
创建群组的实现。用户在主界面中可以找到创建群组选项,输入群组信息后单击确认按钮,客户端将向服务端发出创建群组请求,服务端根据服务端发送的初始数据(群名、人数等)在数据库msgboard中的表grouptable中添加记录,同时生成一张与群组名对应的表,用于存放群聊记录,从而完成新群组在数据库的注册。
发送信息的实现。客户端通过交互界面获取用户将要发送的字符串消息,将字符串使用Base64码制编码后,利用Socket提供的异步发送方法BeginSend将字节流发送至服务器,这一过程将在客户端开辟新的线程,从而避免系统阻塞。由于存在编码后报文长度较长的情况,因此本项目中统一使用了分包的策略,当发送信息时,系统首先根据信息的长度将数据分为若干个数据包,并在包头写入包体长度等验证信息,然后再发送给服务器。当服务器接收到所有的数据包后,将会根据包头中附带的长度信息检验包体的完整性,然后再发送给群组中的其他用户。
接收信息的实现。由于客户端与服务器均使用了分包发送策略,因此在客户端实际接收到的数据也是多个数据包,这里同样根据包头附带信息校验完整性,并进行粘包操作,整合报文,然后对整合后的Base64编码进行解码操作得到常用的UTF-8编码,最后在交互界面进行相应的显示。
消息提示的实现。无论用户是否正处在群聊界面,每当客户端收到消息都会把消息实例化为MessageCache对象(MessageCache为本项目中用于描述消息属性的类)并存储在MessageCacheList中。如果用户不在群聊界面,将提示用户收到新消息(仅显示数目),而当用户正在群聊时,则会遍历MessageCacheList,根据列表中每一个MessageCache对象中的数据逐条显示消息内容。
3)扫码进群聊功能的实現:
扫码进群功能分为两个基本步骤,即生成群组二维码与解析群组二维码。
群组二维码的生成。本项目中使用了草料二维码的开源库ZXing.DLL,通过交互界面读取用于生成二维码的群组信息,然后调用ZXing自带的Encode接口,即可得到相应的Texture2D对象,再将该Texture2D对象赋值给Unity原生的RawImage组件即可实现在屏幕上显示二维码。用户也可以选择单击二维码生成界面的Save按钮将二维码图片保存至本地。
扫描二维码。首先利用ZXing在BarcodeReader类中提供的读取摄像头内容的方法,将拍摄到的画面实例化为RawImage对象,并在Update方法中根据摄像头获取的画面持续更新RawImage对象,从而实现对画面的实时捕捉。同时在Update方法中监听BarcodeReader类提供的Decode方法,当该方法将摄像头获取到的画面识别为有效二维码时,二维码解析成功,从屏幕捕捉到的二维码将被转换为普通的UTF-8字符串,客户端解析字符串中所包含的群组信息后,向服务器发送加入群组请求,服务器收到请求后,将用户信息插入群组对应的表中,完成用户信息在数据库的同步,从而实现扫码进群。
5 客户端设计
客户端程序首先通过套接字连接到服务器IP地址(在本地测试中为本机IP地址,即127.0.0.1),默认端口3000。客户端与服务端建立连接后,客户端通过图形交互界面获取用户输入流,并向服务端发送信息,同时监听服务器端口[6],以接收来自服务器的信息。客户端的具体工作流程如下:在使用客户端提供的各项功能之前,需要用户输入要进行连接的服务端IP与端口,当用户单击确认按钮后,程序调用Connect方法与服务端建立连接,服务端才可以接收并处理客户端发出的各项请求。
本项目中使用了Unity作为客户端可视化图形界面开发工具,与传统的GUI开发工具(如MFC、QT等)不同,Unity作为一个游戏引擎,展示给用户的是场景(Scene)而不是对话框(Dialog),因此在实际工程中,不同功能所对应的UI将被分别放在不同的场景中实现。
1)登录界面。在Title4Android场景中,为用户提供了四个Inputfile组件用于输入服务器IP、Port以及用户的账号、密码,并在登录按钮中绑定了Connect与Login方法,用于实现连接服务器、登录账号功能。
2)主界面。在MainUI4Android场景中,展示了用户的个人信息以及当前加入的群组,其中群组信息通过一组GroupInfo预制体与其父物体上的GridGroup组件共同组成,每当用户进入MainUIOnPC场景或单击InitGroup按钮来手动刷新群组信息时,客户端都会向服务器发出查询请求,通过服务器返回的查询结果将各个群组的信息分别实例化为GroupInfo对象,并挂载在父物体Groups上,从而实现群组信息的列表化展示。
3)群聊界面。在ChatOnPC场景中,展示了用户当前访问的群组信息以及群组中各个成员发送的群聊消息。消息的列表化展示与群组信息类似,同样使用了Prefab+GridGroup(即预制体+网格组)的策略[7],即先设计承载一条消息内容(包括发送者ID、消息内容)的预制体,然后将多个预制体实例化后依次挂载在GridGroup组件下。每当收到群员消息时,客户端程序都会将收到报文解析为发送者信息与内容字段,并以此为参数实例化预制体对象,然后挂载到父物体,从而实现消息的动态显示。
6 结束语
本项目结合Socket网络通讯、MySQL数据库技术,实现了数据信息在网络中的传输,并基于Unity平台实现了网络聊天室的基本功能。程序中的各功能模块之间较好地实现了松散耦合,因此本项目很容易单独作为一个功能嵌入到其他Unity工程中。
(下转第82页)
(上接第75页)
但由于服务器使用了多线程技术,系统会为每一个连接开辟新的线程[8],内存开销较大,因此服务器的负载能力有限,同时由于程序本身机制所限,在同时处理大量连接以及传输数据包体过大时,系统的运行速度会明显降低,还需要对系统性能做进一步的优化。
参考文献:
[1] 陳洁, 孟晓景. 基于Socket接口的Linux与Windows网络聊天室设计与实现[J]. 软件导刊, 2015(6).
[2] 葛福鸿, 张丽萍. 基于Socket技术的聊天软件设计与实现[J]. 电脑编程技巧与维护, 2012(5).
[3] 杨心强, 陈国有. 数据通信与计算机网络[M]. 北京: 电子工业出版社, 2018(6).
[4] 常颖. 基于ASP网络聊天室设计与实现[J]. 电子技术与软件工程, 2017(3).
[5] 罗培羽. Unity3D网络游戏实战[M]. 北京: 机械工业出版社, 2015.
[6] 宋传磊, 刘俊婷, 张光亮, 等. Unity3D项目脚本优化分析与研究[J]. 中国新通信, 2017(1).
[7] 宣雨松. Unity 3D游戏开发[M]. 北京: 人民邮电出版, 2012.
[8] 邓亚君, 杨刚. 基于Python的网络聊天室设计[J]. 电子技术与软件工程, 2019(3).
【通联编辑:谢媛媛】