基于TCP和UDP Socket编程实现的网路聊天室支持文件传输

sohot

发布日期: 2019-03-22 09:08:19 浏览量: 492
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

1、环境

1.1 开发平台

Intel i5处理器,4GB内存,500GB机械硬盘,1GBps网卡速率;操作系统为Windows 10,开发平台为Qt creator5.9.2,除Qt的部分动态链接库以外,无其他组件。

1.2 运行平台

服务器运行平台与开发平台相同,由于需要多个用户同时在线,故其中一个用户也运行在开发平台上;其他用户运行在虚拟机上,虚拟机是Vmware workstation上安装的Windows 7虚拟机,内存2GB,磁盘空间50GB。第三方组件为Qt的动态链接库。

2、系统功能需求

基于TCP和UDP协议实现一个即时通讯工具,具体功能要求包括:

  • 工具包括服务器端和客户端

  • 具备用户注册、登录、找回密码功能(基于TCP协议)

  • 两个用户如果同时在线,采用点到点通信方式进行聊天,信息不需要通过服务器中转,服务器也不保存,且消息要在五秒内送达对方(基于TCP协议)

  • 支持离线消息,且在登录后一分钟内全部收到(基于TCP协议)

  • 支持点到点可靠文件传输(基于UDP协议)

  • 存储在服务器端的数据需要进行强加密

  • 支持不少于两组用户同时在线交流和传输文件

  • 文件传输具有良好的性能,能够充分利用网路带宽

  • 人机交互友好,软件易用性强

3、系统设计

3.1 系统架构

由于开发环境是qt,而qt中有其独特的信号和槽机制,通过信号的触发来调用槽函数,所以系统的功能函数基本都是槽函数,通过用户的按键等操作来实现调用,所以不像一般的系统都是一个主函数调用若干个子功能函数,本系统中的众多功能函数相对比较分散、独立,各有各的触发开关,执行自己的功能。

3.2 功能模块

由于系统分为服务器和客户端,所以功能模块也需要各自讨论。

3.2.1 服务器

服务器主要功能是接受用户的注册消息、登录消息和离线消息等,所以主要功能模块如下:

  • 用户连接:处理用户的tcp连接请求

  • 注册功能:处理用户的注册消息

  • 找回密码:处理用户的找回密码请求

  • 获取IP:用于处理聊天的用户发来的获取IP的请求

  • 离线消息:处理用户发来的离线消息

  • 获取用户列表:获取所有用户,区分在线和离线用户

  • 保存&读取数据:保存已注册用户的所有账号信息并加密,读取用户信息并解密

3.2.2 客户端

客户端的功能远远多于服务器,大体上可以分成tcp模块和udp模块,而这两个模块下又有很多子模块。

  • 注册账号:新用户注册新账号

  • 找回密码:老用户根据密保问题找回密码

  • 登录:最基本的功能,是聊天和发文件的基础

  • 文本聊天:选择好友进行文字聊天

  • 文件传送:选择好友进行文件传输

  • 接收聊天信息:接受好友或服务器发来的聊天信息并显示

  • 接收文件:接受好友发来的文件并保存

  • 界面控制:根据不同的用户状态,在界面上显示或隐藏不同的控件

  • 获取好友列表:获取所有好友,并在列表中显示器账号

3.3 应用层协议

3.3.1 文本传输

用Qbytearray向qtcpsocket中写入数据,为了供数据接收方识别,需要根据数据的功能加上各自的头部,比如注册信息可加上“reg”头部,普通聊天消息可加“msg”,发送方要根据当前用户所处的状态来加不同的头部,而接收方在取出头部后,需要根据头部的值来判断这是何种信息,接下来该执行何种操作,这是应用层协议的关键,双方需要协商好每一种信息使用何种头部。

3.3.2 文件传输

使用qdatastream向qudpsocket中写入从文件中读出的数据,每个包的数据长度自己定义,由于udp只用于文件传输,所以发送和接收方都不会发生混淆,不需要加消息头部标识,但是还是需要在头部添加数据包的序号。接收方采用累积确认法,接受一个包就发送该包对应序号的ack,当收到的数据包不是已确认序号的下一个时,仍发送最后一个已确认序号的ack信号。发送方采用快速重传法,并且使用tcp reno算法控制拥塞窗口大小。当收到三个冗余的ack时,重新发送冗余ack后的所有分组,窗口长度减半,当发生超时,拥塞窗口减为1,再用慢启动恢复窗口长度。

4、系统实现

4.1 服务器

保存&读取数据:这是服务器一切功能的基础。服务器启动时,从内存中导入已注册用户的信息,形成一个用户链表,表中的每个结点分别对应一个用户,其中存放了用户的账号、密码等个人信息,此外还有是否在线的标记,以及与服务器建立连接的tcp套接字。

在读取文件恢复链表时, 只需要恢复账号密码信息即可,其他在线信息暂且不用管。在保存数据时,也只需要保存账号信息。需要注意的是,对于密码,需要进行加密然后再写入文件,读出文件时使用与之配套的解密算法进行解密。

从内存读取数据并恢复用户链表的流程图如下图所示,保存数据的过程与之类似,但顺序相反。

用户连接:当有用户发送连接请求时,服务器要用一个套接字来与之连接。与服务器进行连接的用户有多种情况:注册、找回密码和登录,对于前两种情况,该套接字在操作完成后就失效了,但对于登录的用户,则需要长久保持连接。因此,我们先创建一个套接字建立这一连接,等到确认该用户是登录用户时,就将该套接字挂载到用户链表中的对应节点上,并将链表中用户是否在线的标志变量置为1,并获取登录方的IP地址,也存到用户结点中。若用户是进行注册和找回密码,则需要在一定时间以后销毁该套接字,以免占用内存。

客户端接受用户各类连接信息的流程图如下图所示。

注册功能:用户与服务器建立连接,发送注册的账号和密码,服务器到用户链表中查找是否已经存在该用户,假如不存在,则新建一个用户结点,把该用户添加到用户链表中;否则,发送注册失败的消息,告诉用户,该账号已经被注册了。

注册功能是服务器处理各类连接中的一类情况,其操作流程已经包含在服务器处理连接的流程图中。

找回密码:用户与服务器建立连接,发送密保问题和答案,并附带新密码。需要注意的是,我们生活中常用的软件附带的找回密码功能,实际上都是让用户设置新密码,而并不是直接把旧的密码告诉用户,因为这样更安全,所以此处也借鉴了这一做法。服务器根据用户账号去用户链表中找到该用户,判断用户的密保问题是否回答正确,若是,则将新密码替换旧密码,并反馈给用户修改成功的信息;否则,告知用户问题回答错误,无法找回密码。

找回密码功能是服务器处理各类连接中的一类情况,其操作流程已经包含在服务器处理连接的流程图中。

获取IP:系统要求用户点对点聊天时,消息不经过服务器,那么,用户就必须知道聊天对方的IP地址,才能与之交流,所以用户必须向服务器索要IP。服务器收到获取IP的请求后,根据用户所请求的账号,去用户链表中查找该用户是否在线,若是,则把用户的IP发回请求方,否则,告知请求方,你所请求的用户不在线,假如要跟它聊天,请直接把离线消息发给服务器。

离线消息:对于不在线的用户,与之聊天时只能把消息发给服务器。发送方要在消息头部注明该消息是发给谁的,服务器收到以后,为该用户建立一个文件,把离线消息全都存起来。当离线的用户上线时,服务器立马检索是否有该用户的离线消息,若有,则打开文件,把消息读出,并发给用户,然后还要记得销毁文件,以免占用内存。

获取用户列表:为了方便服务器管理用户,在要求服务器端显示所有用户的账号,对于在线的用户,还需要显示IP地址。可以设置一个获取用户的按键,按键触发时,遍历用户的链表,判断该用户是否在线,假如在线,则将其昵称和IP地址显示到在线用户列表,否则,将账号显示到离线用户列表。

获取IP、发送离线消息和获取用户列表三者都是某用户在线条件下向服务器发送消息的功能,所以三者是同一处理程序的不同分支,其流程图如下图所示。

4.2 客户端

注册账号:获取用户输入的账号信息,包括账号、密码、密保问题、问题答案,将这些信息打包发给服务器,加上“reg”首部表示是注册信息,等待服务器响应,若服务器返回确认信息,则注册成功,可以使用注册的账号登录,否则,注册账号失败,说明该账号已存在。

注册账号的流程图如下图所示。

找回密码:用户输入已注册的账号,该账号的密保问题和答案,以及新的密码,将这三项信息加上头部“psd”,打包发给服务器,若服务器返回确认信息,说明找回密码成功,可以使用新密码登录;否则,说明密保问题回答错误,找回密码失败。找回密码的流程与注册账号相似,流程图结构也相似。

登录:将账号和密码,以及头部“login”发给服务器,等待服务器响应,若返回确认信息,则登录成功,否则登录失败,账号与密码不匹配。登陆流程与注册和找回密码相似,流程图结构也类似。

获取好友列表:点击获取用户按键,向服务器发送一个请求信息,请求服务器将当前所有已经注册的用户反馈过来,这样,我就可以选择其中的用户进行聊天。收到服务器的反馈以后,建立一个用户链表,每个结点代表一个用户,并将每个人的账号显示在列表中。当需要与某人聊天时,双击即可。获取用户列表的流程图如下图所示。

文本聊天:在已经获取了好友列表以后,在好友列表中点击一位好友,客户端将向服务器发送获取IP地址的请求,请求服务器把我选中的用户的IP地址进行反馈,以便我与他取得联系。服务器可能有两种响应:返回用户IP,或者返回错误信息。正常返回用户IP时,客户端向该IP地址发送tcp连接请求,连接成功后,在文本框内输入消息,点击发送,就可以使用已经建立的tcp连接把消息发送过去。假如服务器返回了错误信息,说明该用户当前不在线,所以也就不存在IP地址一说,故不能直接和他聊天,而应该以离线消息的形式发给服务器。在用户看来,对方在线或者不在线是没有区别的,消息都成功发送出去了,但是在内部实现时,要根据对方是否在线,决定将消息发向对方或者是发向服务器。

文本聊天的程序流程图如下图所示。

接收聊天信息:接收好友或者服务器发来的消息。对于好友发来的消息,在消息框内进行显示,并在界面左上角显示当前联系人,也就是发消息过来的人,这些消息都是包含在对方发来的报文中的,直接提取即可。对于服务器发来的离线消息,处理程序也类似,只是在消息显示时,注明是离线消息,以便让用户知道这不是当前发的消息。

文件传送:点击获取文件,弹出文件选择框,选择待发送的文件,获取其文件名,将文件名发送给好友,并向其发送udp端口请求,请求对方提供他的udp端口号,以便进行udp文件传输。对方收到文件名以后,创建文件,并反馈端口号,即刻,文件传输开始。一次从文件中读取1400个字节的数据,再加上一个从1开始的序号,用udp发送给对方。虽然是udp文件传输,但是在拥塞控制上,使用了tcp的reno算法,拥塞窗口长度一开始为1,然后在每次收到正确的ack以后长度翻倍。当收到三个冗余ack时,窗口减半;当发生超时,窗口减为1。发送结束后,关闭套接字和文件。文件发送的流程图如下图所示,由于发送过程的处理逻辑过于复杂,全部用流程图展出难度太大,所以对有些过程进行了缩减,只画出了大致流程。

接收文件:根据好友发送的文件名,在本地创建文件,并将收到的数据写入文件。若收到的序号是递增的,则将数据写入文件,并返回递增的ack序号。若收到的数据包序号不是当前想要的,就发送重复的ack,直到收到正确的数据包。文件接受完毕后,关闭文件。接收文件的逻辑没有发送文件那么复杂,其流程图如下图所示。

界面控制:在不同的用户操作状态下,界面上要显示不同的组件,可以设立一个状态变量,界面控制函数里有一个状态机,它会根据当前所处的状态进行控件的隐藏和显示。这个不涉及到系统的核心功能,没有太多技术含量,只是为了让界面尽可能友好,调用的主要就是控件的show、hide、settext函数。

5、系统测试及结果说明

测试系统为本机+虚拟机,本机配置为:Intel i5处理器,4GB内存,500GB机械硬盘,1GBps网卡速率,Windows10操作系统; 虚拟机为:Vmware workstation上安装的Windows 7虚拟机,内存2GB,磁盘空间50GB。

5.1 注册

选择注册功能,填写要注册的账号信息。这里注册账号为胖虎,密码为panghu,密保问题是我的真实姓名,答案是胖虎的真实姓名“冈田武”的拼音。点击确认,注册成功。

5.2 登录

输入账号密码。

登录成功,进入用户界面,右侧显示本机ip地址和分配的tcp以及udp端口号。点击获取用户列表,获取到当前已注册的所有用户。

5.3 点对点聊天

现在想要与小明聊天,故双击小明,右侧显示“connected”表示成功与小明建立连接(此时小明也成功登录了)。向小明发送消息,与他问好,小明成功收到了消息。

小明收到消息以后,应该回复消息,两人可以按照任意顺序,随意进行聊天。

5.4 多组用户聊天

当胖虎与小明聊天时,其他在线用户可能向小明和胖虎发送消息,此时他们要能够正常接收其他用户发来的消息。现假定小红也在线,她向正在聊天的小明和胖虎分别发去了消息。

此时小明和胖虎都收到了小红发的消息。

5.5 离线消息

当前共有三人在线,而小强不在线,小明和胖虎想叫小强出去打篮球,所以他们都向小强发了消息。

此时,两人发送的消息在服务器端存了起来,等到小强上线时才会发送给他。几分钟后,小强上线了,他立马就收到了来自服务器的消息。

5.6 密码找回

假设小刚想要登录,但是发现自己忘记了密码,他应该选择找回密码功能,填写账号和密保问题,并设置新密码。

点击确认,找回密码成功。再用新密码进行登录,登录成功。

5.7 文件传输

小刚向胖虎发送视频文件狐狸爸爸fox.mkv,用时23秒,如下图所示。在文件夹中可以看到保存成功的文件。

文件传输过程中拥塞窗口长度变化的部分图像。

图中横坐标为时间,单位是毫秒,纵坐标是拥塞窗口的长度。由于传输过程达到了上千毫秒,所以只能选取部分过程进行分析,只要该部分中包含有传输中可能出现的各种情况,都是可取的。这里选取了开始的半秒钟,可见在半秒钟时间内,窗口长度就发生了多次剧变。一开始窗口长度以指数型方式增长,超过阈值,然后线性增长;当网络轻度拥塞,产生三个冗余ack,拥塞窗口减半再加三,以线性增长方式恢复;当网络重度拥塞,窗口长度减为1,然后再以指数型方式恢复。这与我们的网络传输协议的设计是相符的,也就相当于tcp拥塞控制协议中的reno算法,只不过是用在了udp传输中而已。

上传的附件 cloud_download 基于TCP和UDP Socket编程实现的网路聊天室支持文件传输.7z ( 15.51mb, 10次下载 )
error_outline 下载需要15点积分

发送私信

我想要你幸福,但我希望我是你幸福的原因

15
文章数
11
评论数
最近文章
eject