基于TCPIP协议的网络聊天室的设计与实现Word文件下载.docx
《基于TCPIP协议的网络聊天室的设计与实现Word文件下载.docx》由会员分享,可在线阅读,更多相关《基于TCPIP协议的网络聊天室的设计与实现Word文件下载.docx(29页珍藏版)》请在冰豆网上搜索。
![基于TCPIP协议的网络聊天室的设计与实现Word文件下载.docx](https://file1.bdocx.com/fileroot1/2023-1/3/204b059e-4220-4974-b541-680cdc737a42/204b059e-4220-4974-b541-680cdc737a421.gif)
套接字为了局域网提供一种安全、快速、杰出的通信机制,是C/S一种经典的通信方式,其中服务器采取多线程并发接管多个客服端,使客服端之间进行实时通讯,服务器端还操纵SQLite函数接口,及时保留客服端发来的数据。
一共分四章,具体内容如下:
第一章介绍了本文的开发背景,选题目的以及研究内容。
第二章介绍了平台系统的所采用开发环境与技术。
第三章主要介绍平台系统的设计,包括框架结构和数据库设计。
第四章介绍了平台的功能,包括界面显示,功能模块和系统测试。
2开发环境与技术
2.1开发环境
2.1.1QtCreator
Qt是诺基亚公司开发的一个跨平台应用程序、C++GUI应用构架,它具有丰富多彩的窗口控件,具有扩展性、面向对象、基于组件编程等特点,其开发的应用程序具有优美的图形用户界面[2]。
使用Qt开发的应用程序具有容易扩展、容易维护的特性,且可以发布多个平台,包括安卓、iOS等移动平台[3]。
Qt除了可以创建漂亮的界面,还有很多其他组件,例如,你不再需要连接数据库、Socket的各种第三方库,这些Qt都已经包含了。
Qt是应用程序开发的最佳方案之一。
2.1.2SQLite
SQLite是一个自包含、无服务器、零配置、事务性的SQL数据库引擎。
SQLite的代码是开源的,这就大大降低了开发成本,具有可移植性、高效性。
SQLite目前应用很广泛,是一个开源的嵌入式关系数据库。
SQLite不同于其他数据库,它和程序运行在同一个进程,运行期间没有进程间通信,导致它速度快、体积小、易于分发,适合运行在单机环境和嵌入式环境,官方网站上的源程序提供了C语言接口,所以可在第三方程序直接调用API接口[4]。
一个SQLite数据库是由表、索引、触发器和视图构成,这些都包含在一个单一的磁盘文件中,SQLite的使用与执行权限是由文件系统而来的[5]。
基于以上这些优点,所以这次开发采用SQLite数据库来存储信息。
2.2开发技术
2.2.1C++
C++语言是一种使用非常广泛的编程语言,是一种静态数据类型检查的,支持多范型的通用程序设计语言。
它继承了C的简洁、高效的等特点,还对C的系统进行了扩充,C++的编译系统能检查出C没有的类型错误,所以C++比C更安全。
它还可以进行以继承和多态为特点的面向对象的程序设计[6]。
因为它与C兼容,导致C++具有双重特点,但它在概念上与C完全不同,更具面向对象的特点。
由于引进了面向对象的概念,使得开发人机交互类型的应用程序更为简单方便。
很多优秀的程序框架都是采取C++的。
2.2.2TCP/IP协议
本文所介绍的系统最重要的就是TCP/IP协议,它是C/S模型的核心。
中文名为传输控制协议/因特网互联协议,又名网络通讯协议,由网络层的IP协议和传输层的TCP协议组成[7]。
它定义了电子设备是如何进入因特网,以及数据之间的传输的标准[8]。
TCP的任务是保证传输过程中数据的安全,如果发现传输期间有问题,它就会发出信号,请求重新传输,直到数据正确安全地传输到正确的位置,IP的任务则是给每一个网络设备分配一个地址。
TCP/IP协议的特点
1、面向连接的通信协议,通过三次握手四次挥手建立连接,确保数据不卡留在网络传输中间。
2、数据流服务,采用超时重发技术,还采用流量控制技术,得知接收方的接收能力,来限制发送方的发送速度,避免数据堵塞。
3、安全可靠性,TCP层将数据包排序并进行错误检测,如果没有按照顺序收到的包可以被排序,损坏的数据包可以被重传[9]。
2.2.3多线程并发
线程被称为轻量级进程,是程序的最小单元。
线程不拥有自己的系统资源,但它与同一个进程的其他线程共享进程的资源,多线程彼此之间使用相同的地址空间,共享大部分数据[10]。
多线程降低了内核的开销,提高了系统性能,节省了CPU时间。
除此之外,多线程作为一种多任务、并发的工作方式,还有其他优点:
(1)提高程序响应时间;
(2)多CPU系统中,提高CPU利用率;
(3)改善程序结构。
本文就是采用服务器多线程并发接受多个客户端连接。
3系统总体设计
3.1系统原形设计
3.1.1系统框架结构
网络聊天室系统分为客户端和服务器端。
服务器端包括:
开启和关闭服务,用户管理,用户查询,文本传输;
客户端包括:
注册操作,登录操作,查找操作,聊天功能;
系统框架流程图如图3-1所示:
图3-1系统框架流程图
3.1.2数据表说明
本系统一共4个表,用户信息表user_info,用户在线表user_online,用户好友表ID_friend,用户socket表user_socket。
(1)用户信息表user_info如表3.1所示:
表3.1用户信息表user_info
字段
数据类型
长度
说明
id
integer
10
用户id
pswd
text
20
用户密码
name
用户名
tel
11
用户电话
(2)用户在线表user_online如表3.2所示:
表3.2用户在线表user_online
sts
1
用户状态
(3)用户好友表ID_friend如表3.3所示:
表3.3用户好友表ID_friend
friend_index
好友个数
Friend_id
好友id
(4)用户socket表user_socket如表3.4所示:
表3.4用户socket表user_socket
socket_index
socket下标
3.2系统原理
本系统通过Socket套接口来实现不同客户端之间的实时聊天。
Socket作为BSDUNIX的进程通信机制,一般称为“套接字”,用于描述IP地址和端口,它是建立在传输层上的一种套接字的规范,是网络通信的基本操作单元[11]。
Socket接口是TCP/IP协议最通用的应用接口,它通过调用系统提供的库函数,并通过进程对Socket的读写操作实现数据传输。
Socket编程分为服务器端和客户端,大致过程如图3-2:
图3-2socket编程流程图
服务器编程步骤:
(1)调用socket()函数创建一个通信的的套接字。
(2)调用bind()函数,给创建好的套接字绑定一个IP地址和端口号。
(3)调用listen()函数,让你创建好的套接字成为一个监听套接字,用于监听是否有其他套接字连接上来[12]。
(4)调用accept()函数,接受客户端的连接,并返回一个新的套接字,服务器就用这个新的套接字和客户端进行通信。
(5)调用read(),write()函数,对套接字进行读写操作,实现接受和发送客户端的数据。
(6)中断连接。
客户端编程步骤:
(1)调用socket()函数创建一个通信的套接字。
(2)配置套接字的地址结构,设置成服务器的IP地址和端口号。
(3)调用connect()函数,建立和服务器的连接。
(4)调用read(),write()函数,对套接字进行读写操作,实现接受和发送服务器的数据。
(5)中断连接。
3.3系统功能介绍
(1)注册操作
1)功能描述:
注册一个新的账号。
2)输入要素:
id、密码、用户名、手机号码。
3)处理流程:
客户端将id传给服务器,服务器判断用户信息表中是否有这个id。
存在提示重新输入,不存在即创建成功,将信息存入数据库的用户信息表、用户在线表、用户好友表、用户socket表。
(2)登录操作
输入账号并登录。
2)输入要素:
id、密码。
3)处理流程:
客户端将id、密码传给服务器,服务器判断id、密码在用户信息表中是否匹配。
不存在提示输入错误,存在后判断用户在线表中该id的值是否等于1,1即在线并提示登录失败用户在线,0即显示登录后的界面,并将id、用户名存入用户在线表中,改变sts为1,注销时sts为0。
(3)查找操作
查询好友并添加。
id。
客户端将对方id传给服务器,服务器判断用户信息表中是否有这个id。
不存在提示该用户不存在,存在并添加,则将好友id添加到用户好友表中。
(4)聊天功能
通过用户加入的好友与好友进行点对点聊天。
用户聊天消息。
服务器查找用户socket表,转发客户端之间的消息。
4系统功能实现
4.1服务器界面
服务器界面设置了两个按钮,一个开启服务,一个停止服务,如图4-1所示:
图4-1服务器界面
主要代码如下:
Widget:
:
Widget(QWidget*parent):
QWidget(parent),
ui(newUi:
Widget)
{
ui->
setupUi(this);
server=newQTcpServer(this);
connect(server,SIGNAL(newConnection()),this,SLOT(onnewconnect()));
db=QSqlDatabase:
addDatabase("
QSQLITE"
);
db.setDatabaseName("
C:
/Users/hxz/Desktop/SQLite/SQLite/test.db"
}
在服务器的构造函数里面创建一个QTcpServer类的server,利用connect()函数创建信号与槽,SIGNAL(newConnection()),SLOT(onnewconnect()),当有客户端连接进来时就会发送newConnection()的信号,然后触发onnewconnect()槽函数。
然后通过sqlite文件路径,设置数据库的属性。
voidWidget:
on_pushButton_clicked()
server->
listen(QHostAddress:
Any,7777);
on_pushButton_2_clicked()
inti;
for(i=0;
i<
list.size();
i++)
{
list.at(i)->
disconnectFromHost();
}
list.clear();
这两个函数是按钮的槽函数,第一个是开始监听有没有客户端连接进来,第二个是停止服务,断开所有连接进来的客户端。
onnewconnect()
socket=server->
nextPendingConnection();
list.append(socket);
Thread*thread=newThread(socket,db,list,0,this);
connect(socket,SIGNAL(readyRead()),thread,SLOT(onread()));
connect(socket,SIGNAL(disconnected()),thread,SLOT(ondisconnect()));
thread->
start();
在onnewconnect()槽函数里面,利用server->
nextPendingConnection()函数获得一个新的socket套接字,服务器就可以利用这个套接字和客服端进行通行。
然后把这个socket加入到QTcpSocket*类型的list链表里面。
接着创建Thread类的thread,并把当前socket、数据库、链表等数据作为参数传进去。
然后连接信号与槽,当socket收到SIGNAL(readyRead())信号,就会触发thread里面的SLOT(onread()))函数。
同理SIGNAL(disconnected())和(ondisconnect())),就是断开连接的意思,最后将线程开启,这样多线程并发就形成了。
4.2登录界面
登录界面包括:
ID、密码、注册,如图4-2所示:
图4-2登录界面
客户端:
MainWindow:
MainWindow(QWidget*parent):
QMainWindow(parent),
MainWindow)
socket=newQTcpSocket(this);
socket->
connectToHost("
192.168.155.1"
7777);
connect(socket,SIGNAL(connected()),this,SLOT(onconnect()));
voidMainWindow:
onconnect()
connect(socket,SIGNAL(readyRead()),this,SLOT(onread()));
connect(socket,SIGNAL(disconnected()),this,SLOT(ondisconnect()));
在MainWindow类的构造函数里面,先创建QTcpSocket类的socket,然后用socket->
connectToHost()函数,设置好服务器的IP、端口号,连接到服务器。
再用connect()函数连接信号与槽SIGNAL(connected())、SLOT(onconnect())。
最后在onconnect()槽函数里面再次用connect()函数连接信号与槽SIGNAL(readyRead())与SLOT(onread()),SIGNAL(disconnected())与SLOT(ondisconnect()),当服务器发送消息时,就会触发onread()的槽函数,当服务器断开连接时,就会触发ondisconnect()槽函数。
QStringgo=QString("
friend_list_wnd"
)+QString("
select*fromuser_infowhereid=%0andpswd='
%1'
"
)
.arg(ui->
id_lineEdit->
text()).arg(ui->
pswd_lineEdit->
text());
write(go.toLocal8Bit());
this->
close();
onread()
if(head=="
id_exist"
str.replace(head,"
friendwnda(ui->
text(),str,0);
a.exec();
id_no_exist"
QMessageBox:
information(this,"
登录失败"
"
QMessageBox:
Ok,QMessageBox:
No);
ID_online"
该ID已在线"
在on_pushButton_clicked()槽函数里面,这是点击登录按钮后,客户端利用socket->
write()函数,添加一个登录协议头,将输入的ID、密码发给服务器。
在on_pushButton_2_clicked()槽函数里面,这是点击退出按钮后,客户端利用socket->
disconnectFromHost()断开连接。
如果客户端收到服务器消息,那么就会立即调用onread()函数,在里面利用socket->
readAll()函数,接受到服务器发来的所有消息,然后提取协议头判断回复的是哪种消息。
“id_exist”是ID存在,弹出friendwnd类的界面并断开连接关掉当面界面。
“id_no_exist”则是ID不存在,弹出登录失败提示窗口。
“id_no_exist”则是ID在线,弹出该ID已在线提示窗口。
服务器端:
voidThread:
)
query.exec("
select*fromuser_info"
intid_online;
boolok;
while(query.next())
QStringid=query.value(0).toString();
QStringpswd=query.value
(1).toString();
QStringname=query.value
(2).toString();
if(QString("
.arg(id).arg(pswd)==str)
QSqlQueryquery_checkonline;
query_checkonline.exec(QString("
select*fromuser_onlinewhereid=%0"
).arg(id));
while(query_checkonline.next())
id_online=query_checkonline.value
(2).toInt(&
ok);
if(id_online==0)
QStringpass_name=QString("