一、 通信模型
1.1 信息传输
软件由两个主要部分组成:客户端和服务器端。两个客户端之间不是直接连接的,而是通过服务器沟通,一个客户端可以同时和多个其它客户端通信,框图如下:
图1 聊天软件模型
服务器对客户端接收的信息处理并派发给接收信息的客户端,服务器为每个客户端分配唯一ID,作为用户的唯一标志。客户端与服务器之间实现双向通信,加密和解密都是在客户端实现的,可以根据用户的选择自动设置加密解密。
1.2 文件传输
文件传输分为大型文件传输和小型文件传输(在程序中以2M分界),对小型文件的传输,是通过服务器转发的。对大型文件而言,为了节省传输时间,文件传输请求和接收请求通过服务器转发,之后将失去中转权,所有大型文件(理论上可传输超过1000G的文件)都将实行点到点的通信模式。
图2 文件传输模型
如图2所示,无论是大型文件,还是小型文件,都必须首先经过1—2—3—4路径。文件发送请求信息包括文件名、文件大小、接收该文件的ID,发送文件的ID信息,服务器对文件请求指令中的文件大小作判断,如果文件属于小型文件(小于2M),则文件开始传输,路径为6—7(图中的绿色路径)。反之,如果是大型文件,则文件传输的路径为5(图中的红色路径)。无论是大型文件还是小型文件,传输和接收都在心开辟的线程中执行,不会影响消息通信。
软件可应用于所需安全性较高的通信场合,如高科技技术交流,国家机密文件传输等等
二、客户端
2.1 主界面
客户端由多个Dialog控件组成,登录主界面中使用了ListControl控件,控件中每个Item保存了一个用户的信息,包括用户名和ID。在ListControl控件中处理双击事件,当用户双击某个Item的时候,生成新的Dialog,用于与用户传递信息。Menu栏报还了常用操作,包括“发送信息”、“添加好友”、“更改信息”、“注册用户”、“退出”。其中的“添加好友”、“更改信息”、“注册用户”三项是与服务器互动的形式完成的,即响应这些菜单后,会向服务器发送相信的信息,服务器处理信息并返回结果给客户端。。
2.2登录界面
初次登录
用户输入信息后,保存用户信息到LOGINFO结构体,此结构体保存了用户的登录信息,包包括ID,NAME,服务器IP,端口等。点击登录界面的“ok”通过指定端口向指定ip发送LogIn信息,表示请求登录,如果服务器收到客户端的LogIn请求,开始验证用户信息,包括检测用户密码和用户ID,如果两者匹配,向客户端返回LogIn信息,提示用户登录成功(图3)。如果用户登录成功,设置用户登录状态为TRUE。如果客户端发出LogIn消息之后没有收到消息或者收到服务器的登录错误消息,客户端将不能登录。
图3 登录界面
重新登录
当前在线用户再次点击登录按键之后,会再次弹出登录窗口,用户输入信息并发出请求之后,会收到服务器的返回LogIn信息。客户端会提示用户是否更改用户登录(图4),选择“是”则新用户登录,并更新LOGINFO结构体对象,选择“否”返回,设置用户窗体相关信息并设置登录状态为FALSE。
图4 提示更换用户界面
2.3注册界面
一般软件注册不需要用户登录,这里主要是为了确认服务器的位置,所以要求用户先登录,然后才能注册。客户端发送注册请求,服务器收到注册信息之后,会给用户生成一个唯一ID,并返回提示信息给用户注册成功(图5)。
图5 注册
2.4修改信息界面
登录用户只能对自己的信息进行修改,包括修改用户名和密码,点击”OK”之后会向服务器发送修改信息请求,服务器端判断当前用户ID和密码是否输入正确,正确则保留用户新信息,并返回修改成功信息。否则返回修改不成功信息(图6)。
图6 修改信息
2.5添加好友界面
用户添加好友,只能够添加服务器现有的用户。通过指定用户ID或用户名查找用户,并向服务器发出添加用户请求,有该用户则返回用户存在信息,包括用户名,用户ID。客户端收到服务器的消息之后,再次提示用户是否添加该好友,是则向list控件中添加新用户,否则返回(图7)。如果用户选择了“按用户名查找”可能会返回很多要求用户确认处理的信息,原因是,在服务器端,没有对用户名的唯一性加限制,也就是说,用户的ID是唯一的,但是,用户名却是随意的,只要满足用户名的命名规范即可。选择确认添加后,如果好友已经在自己的好友列表,则不能再次添加。
图7 添加好友界面
2.6聊天窗口
1) 聊天窗口
聊天窗口的生成有三种方式,一是选择菜单中的“发送文本”,二是双击好友列表中的一用户,三是当用户收到某另一客户发来的聊天信息,用户确认与其聊天后打开。聊天窗口定义为非模态对话框,这样用户就可以同时和多个用户聊天,而且聊天信息在不同的窗口中显示。每新建一个聊天窗口,都是新生成一个ChatDlg类的实例,该实例有唯一句柄,可以对其控制。
2)多人聊天的实现
主应用程序新建了一个动态链表chatUserMap,链表的每一个节点就是一个键值对,分别为聊天用户的ID和指向该ID对应聊天窗口的指针(也可以保存窗口的句柄作为窗口的唯一标识)。每当新建一个用户,就向此链表中插入一个节点,当关闭某一聊天窗口时,就从该链表中删除与此ID相关的节点。用户接收到信息之后,首先从链表中查找是否存在与此信息相关ID,是则通过CWnd指针传入聊天信息,显示到相对应的窗口,否则提示用户收到XXX的聊天信息,是否新建聊天窗口。图8显示了多聊天窗口原理,用户打开了多少个聊天窗口,此链表就包含多少个节点。
ID1 窗口实例指针1 |
ID2 窗口实例指针2 |
… |
IDn 窗口实例指针n |
图8 聊天窗口链表
2.7信息编解码
信息编解码是对聊天窗口中输入的信息作字符创处理。程序中设置最多五层编码的演示(设置更多层次原理一样)。聊天窗口右侧添加了五个ComBox控件,相应限制都在控件的响应事件中完成。如第一层不允许直接对信息摩尔编码,所以第一层的combox中没有此种编码。“键盘编码”只能处理26个英文字母,所以不能在“手机编码”之后,当某一层选择了“手机编码”之后,后面的编码层键盘编码自动消失,“摩尔编码”只能处理数字字符,所以必须在手机编码之后,当上一层没有选择“手机编码”,这一层是不会出现“摩尔编码”的,只有先选择了“手机编码”,后一层才会出现“摩尔编码”(图9)。
图9 信息加密
“栅栏编码”和“倒序编码”可以对任何字符串处理(包括摩尔编码和手机编码之后的处理),所以程序中没有对这两种编码作特别限制,程序中也没有限制最后两种编码必须为“手机编码”和“摩尔编码”,这一点与设计要求有点不一样。我个人认为:作为一种加密工具,只要符合加密规则,就不应该限制加密的方式,明显“手机编码”和“摩尔编码”之后还是可以使用倒序编码和栅栏编码的。如果限制最后两种加密方式,人人都知道有最后限制的两种加密方式,对于密文的截获者来说,是不是就更容易破解呢(个人观点)。
解密部分未对解密规则作任何限制,原因是对于一个密文的解释,方法应该越多越好,如果不知道解密方式,只能通过组合的方式测试并人为判断解密内容。所以,解密方案越多,越能提高密文的安全性。
用户设置了与某一用户的聊天加密解密后,之后用户发送或者接收的消息都会进行加密解密步骤。与不用的用户聊天,可以设置不同的加密解密方式。其原理如同2.1.6节,加密解密都是通过链表中的窗体指针操作的。
2.8 文件发送
客户端的文件发送包括普通文件发送和加密文件发送两种文件,普通文件发送之前不需要设置文件加密设置,如mp3、doc、rmvb、rar文件等,而加密文件发送之前需要设置对文件的加密方式,接收方也必须设置解密方式才能查看该文件的内容。无论是普通文件还是发送文件,都在新打开的进程中发送和接收,并以TCP为通信基础,防止信息丢失,传输过程实时显示在对应的窗口。文件传输和信息传输互不影响。可以实现同时传输多个文件到不同客户,也可以同时接收多个客户传来的文件,还可以自发自收传送文件。
服务器 |
客户1 |
客户2 |
1 |
2 |
3 |
4 |
5 |
图10 文件传输模型
1) 普通文件发送
图10表示了文件传输的具体过程,下面以各个路径上的数字解释具体传输细节。
步骤1、客户1发送“文件发送请求”到服务器,文件发送请求包括:自己ID、文件接收ID、文件名、文件大小等信息(路径标号1)。
步骤2、服务器接收到客户1发送的“文件发送请求”信息,对信息解析,得出接收文件的ID,判断该ID是否在线,如果在线,向该ID发送“文件发送请求”(路径标号2),等待客户2的信息反馈(是否接收该文件)。客户2如果反馈信息为不接收文件(路径标号3),则服务器发送“拒绝接收文件”信息到客户1(路径标号4),客户1取消文件发送。如果客户2反馈信息为接收该文件(路径标号3),则服务器开始判断该文件是否属于大型文件,是大型文件发送“文件发送”信息到客户1(路径标号4),该信息中还包括客户2的IP地址,客户1接收到信息后直接发送文件流到客户2的IP地址(路径标号5),如果服务器未返回客户2的IP地址,则发送文件流到服务器,服务器再转发到客户2(路径标号1--2)。
步骤3、服务器在步骤2中只处理了在线用户的文件发送情况,如果服务器发现客户2不在线,则判断该文件是否属于大型文件,如果是直接返回拒绝消息(路径标号4),提示用户文件过大,不能离线发送。如果不属于大型文件,则返回接收文件消息(路径标号4),客户1将该文件发送到服务器相应位置(路径标号1)。
步骤4、客户1发送离线文件到服务器后,文件保存在服务器,当客户2上线后,服务器在本地查找是否有该用户的离线文件,如果有,则发送含有文件名和发送者ID的信息到客户2,客户2接收消息并解析消息,如果有多个文件,则以列表的形式显示在对话框上面,客户2选择文件并接收,可以从服务器下载离线文件(路径标号2)。多个文件的下载只能单独下载,点击menu菜单中的“离线文件”,会显示还未下载的离线文件,可以从中选择并下载。
步骤5、除了文件可以离线发送之外,消息也可以离线发送,当服务器接收到客户1发送的消息之后,如果客户2不在线,则将该消息以文本的形式保存在服务器,当有多条离线消息发送过来之后,会不停地向相应文本中追加消息,下一次客户2上线后,会自动将消息发送到客户2。
图11 文件发送实例
以上只举例两个客户端之间的通信模型,多个客户端之间的通信和上面的模型类似。
2) 加密文件发送
文件加密只针对txt文本文件,具体的传输过程和普通文件传输没有区别,唯一区别在加密文件传输前需要设置加密模式,输入的加密模式必须在加密规则内,否则不允许加密。文件传输前,先讲文件中相应的文本加密处理,放到程序运行目录下的临时文件中(文件名“temp.txt”),发送文件实际是发送的该临时文件。接收方接收的文件以“.txt.key”为文件后缀(程序中规定的,仅用于与普通非加密文本的区分),接收方点击“文件解密”并手动选择需要解密的文件,如果解密正确,则能输出正确的结果当当前目录下的“相应文件名.txt”文本中(注意:如果目录下有同名文件,会覆盖先前的文件),否则返回。和信息解密类似,程序中没有对文件解密方案作规则限制,目的是增加解密难度,使密文的截获者难以解释正确。
2.9 文件接收
用户接收到客户发送的文件接收请求之后,开启接收文件线程,同时通知文件发送方开启文件发送线程,双方开始文件传输,同样,文件接收也会显示文件接收进度。
离线文件的接收和普通文件接收原理一样,只不过这里的文件发送方为服务器,如果服务器端有多个离线文件,会以列表的形式返回给客户,客户选择接收,每次只能选择一条文件记录并接收,接收完成之后,点击menu菜单中的“离线文件”菜单选项,可以继续下载没有下载完的文件。
2.2 服务器
2.2.1、主界面
应用程序服务器主界面由三个页面组成,分别用于管理用户信息、接收客户的发送信息显示、以及发送的离线文件或信息管理。
图12 服务器界面
管理界面(图11)显示所有注册用户,用户所有信息在服务器都是透明的,包括用户名,ID,用户密码,已经用户登录IP,可以对用户的信息在服务器端操作,包括修改、删除、查询、新建等。服务器修改用户注册信息是不安全的,可能影响到用户的下一次登录,所以如果在服务器端修改了用户信息,在关闭服务器的时候会提示用户是否保存修改。用户上线后,服务器会记录该用户当前IP到列表中。
2.2 离线文件管理
文件界面用于管理用户发送的离线文件或消息,包括该文件或信息的接收ID、发送ID、类型(发送的是文件还是消息)、文件名称、以及发送的时间信息。如图12所示,第一行表示的信息为:ID12345发送了文件“新建文本文档.txt.key”文件给ID10014,发送时间为2011.07.30 16:21:30.
消息和文件分开管理,用户发送的离线文件存储于应用程序目录下的Files文件夹下,当某一离线文件发送后,自动将其在服务器删除,以便释放磁盘空间。用户的离线消息存储与应用程序目录下的Msgs文件夹下,以用户的ID命名文本,如12345.txt,表示发送给ID为12345用户的离线消息。用户上线后,并不是将此文本发送给对方,而是将此文本打开,得到文本类容并发送出去。
图13 离线文件信息管理界面
2.2、信息处理
服务器对客户的信息处理包括“注册信息”“登录信息”“消息发送”“请求添加好友”“退出登录”等,每收到相应信息,服务器会做出处理并向客户端返回。两个客户端发送信息时,服务器起到中介作用,服务器首先判断接收信息的ID当前是否在线,“是”则向其IP发送相关信息,“否”则保存相关信息到本地文件中,在下一次用户上线之后,会发送到该用户。
2.3、用户信息保存
为了防止注册用户信息在掉电之后消失的情况,所有用户信息均保存在工程目录下的txt文件中,客户端也采用相同处理措施。登录成功则加载文本信息到list控件显示。
2.4、端口设置
客户端和服务器在不同的主机运行,设置端口均为6000(默认),当服务器更改端口后,会结束掉初始化的socket重新实现绑定,并广播所有在线用户服务器不可用或者更改端口号,此后客户端只有向新端口发送信息,服务器才能收到。在同一主机测试程序时,需要打开两个端口(测试中采用6000和6001)分别用于服务器的坚挺端口和客户端的监听端口,但是不能模拟两个或更多客户端通信,除非为每一个客户端打开一个监听端口。要实现在单机上面测试,需要在程序中修改端口号,具体不细说。
二、 工程文件功能列表
客户端:
ChangeInfoDlg.cpp 改变用户信息对话框类
ChatClient.cpp 工程应用程序主文件
ChatClientDlg.cpp 客户端主界面对话框类
ChatDlg.cpp 聊天窗口对话框类
ComUse.cpp 服务器和客户端公共定义的变量声明和函数文件
DecodeChooseDlg.cpp 解密设置对话框类
FileCode.cpp 文件加密对话框类
FileDecode.cpp 文件解密对话框类
FileDLoad.cpp 离线文件下载对话框类
FileTrans.cpp 文件传输类,用于管理文件的收发,内含新开辟线程实现方法,此文件客户端和服务器公用
FindFriendDlg.cpp 添加好友对话框类
LogInDlg.cpp 登录对话框类
MySocket.cpp Socket通信封装类,服务器和客户端公用
PassWord.cpp 加密解密文件
RegisterDlg.cpp 注册新用户对话框类
VirtualScreenDlg.cpp 屏幕截图实现类,程序中该功能暂未完善
服务器:
AddNewUserDlg.cpp 添加新用户对话框类
ChatSever.cpp 应用程序主类
ChatSeverDlg.cpp 服务器主界面父类,用于管理各个页面
ComUse.cpp 该文件请见客户端说明
FilePage.cpp 管理离线文件和消息页面
FileTrans.cpp 该文件请见客户端说明
FindInfo.cpp 注册用户按ID查找对话框类
FindUserDlg.cpp 注册用户查找对话框类
ManPageDlg.cpp 注册用户管理主界面类
MsgPageDlg.cpp 用户消息显示对话框
MySocket.cpp 该文件请见客户端说明
SSetPortDlg.cpp 服务器重置端口对话框
注:以上文件只包含原cpp文件,对应的头文件列表未列出。
三、 软件扩展
该软件可作以下方面扩展:
1、设置定时器,判断用户是否登录超时等操作。另登录用户可定时向服务器发送在线确认消息,服务器如果超时接收不到确认消息,可将用户当离线处理,其它用户发送的信息将相应处理,防止信息丢失。
2、文件拖动发送,实现类似QQ聊天软件的文件拖拽发送功能,方便客户使用。
3、实现图片传输和显示,声音的传输和显示。
4、获取屏幕DC,实现屏幕截图功能。
5、尝试加密图像,由于图像是二维的,初步打算将图像的每一个像素加密,或者将像素重排,在接收端设置解密,解释加密图像。
6、尝试加密音频文件,音频文件是一维的,初步打算采取和字符串加密的方式处理
7、美化界面,使设计更人性化。