如何使用QT5编写一个利用TCP协议的聊天室 (一)

关于Tcp协议的知识点

TCP协议是一种基于传输层的协议,具有可靠性,需要连接,工作方式为全双工,传输速度相较于UPD更慢的特点,一般用于传输大量的数据,传输过程不允许丢包的情况.一般情况下聊天类软件均采用UDP协议,此处采用TCP是为了了解TCP的特点,以及保证实验过程不因为丢包影响实验结果.

实验思路

我将服务器端分为三个部分,每个部分分别实现不同的功能.

第一部分:服务器端的外形设计,服务器端应有一个对话框显示客户端的登入登出以及在登入期间所发送的信息,一个文本框显示端口号,一个按钮来开启服务器

第二部分:服务器功能,服务器需要监听一个固定的端口,当有客户端接入该端口时,创建一个TCP套接字对象来为该客户服务(以便在服务端实现与客户端的通信),服务器还需要能存储每一个接入的客户端的信息

第三部分:一个TCP套接字对象的功能,该对象需要能读取套接字中的数据,将其传输给服务器,

具体实现

以下代码为.cpp中的部分代码,代码参考<<Qt5开发及实例(第三版)>>,若内容有错误理解希望能提出指正

第一部分:

1 Tcpserver::Tcpserver(QWidget *parent,Qt::WindowFlags f)//构造函数 2 : QDialog(parent,f) 3 { 4 setWindowTitle(tr("TCP Server"));//更改名称 5 ContentListWidget = new QListWidget;//初始化一个对话框,显示相应的信息 6 PortLabel = new QLabel(tr("端口"));初始化一个标签,让其显示相应的内容 7 PortLineEdit = new QLineEdit;//初始化一个对话框显示固定的端口号 8 CreateBtn = new QPushButton(tr("create chat room"));//初始化一个按钮,让其显示相应的内容 9 mainLayout = new QGridLayout(this);//初始化一个QGridLayout的布局,并将所有部件塞进布局10 mainLayout->addWidget(ContentListWidget,0,0,1,2);11 mainLayout->addWidget(PortLabel,1,0);12 mainLayout->addWidget(PortLineEdit,1,1);13 mainLayout->addWidget(CreateBtn,2,0,1,2);14 port = 8010;//监听8010端口15 PortLineEdit->setText(QString::number(port));//将端口号显示16 connect(CreateBtn,SIGNAL(clicked()),this,SLOT(slotCreateServer()));//按下按钮发送信号,触发槽函数,该槽函数功能为创建一个服务器17 }18 TcpServer::~Tcpserver()//析构函数19 {20 }21 void Tcpserver::slotCreateServer()//槽函数的实现22 {23 server = new Server(this,port);//创建一个Server对象,并将端口号传给该对象,是其对该端口进行监听24 connect(server,SIGNAL(updateServer(QString,int)),this,SLOT(updateServer(QString,int)));//若server发送updateServer信号,触发tcpserver的槽函数updateServer25 //使其更新对话框内容26 CreateBtn->setEnabled(false);//创建服务器之后 无法再点击该按钮27 }28 void Tcpserver::updateServer(QString msg,int length)//更新内容的槽函数29 {30 ContentListWidget->addItem(msg.left(length));31 }

第二部分:

1 Server::Server(QObject *parent,int port) : QTcpServer(parent) 2 { 3 listen(QHostAddress::Any,port);//在指定的端口对任意地址监听 4 } 5 void Server::incomingConnection(qintptr socketDescriptor)//当出现一个新的连接的时候,TcpServer便会触发incomingConnection()函数,每有一个新的连接都会触发一次,即都创建一个新的对象 6 { 7 TcpClientSocket *tcpClientSocket = new TcpClientSocket(this);//创建一个新的TcpClientSocket与客户端通信 8 //出现一个新连接才会创建一个 TcpClientSocket 的对象,如果连接后有数据传入,该对象将其读下来 通过updateClient信号来发送 9 connect(tcpClientSocket,SIGNAL(updateClients(QString,int)),this,SLOT(updateClients(QString,int)));//连接TcpClientSocket的updateClients信号10 //当有新数据传入后,TcpClientSocket便会发出updateClients的信号,此时触发Server中的updateClients的槽函数,将信号中的msg与length传入该槽函数11 connect(tcpClientSocket,SIGNAL(disconnected(int)),this,SLOT(slotDisconnected(int)));//连接TcpClientSocket中的disconnected信号12 //当TcpClientSocket的对象断开连接后,发出disconnected的信号,此时触发Server中的slotDisconnected的槽函数13 tcpClientSocket->setSocketDescriptor(socketDescriptor);//将新创建的TcpClientSocket的套接字描述符指定为参数socketDescriptor14 tcpClientSocketList.append(tcpClientSocket);//将tcpClientSocket这个对象添加入tcpClientSocketList这个列表中15 }16 void Server::updateClients(QString msg,int length)17 {18 emit updateServer(msg,length);//发出updateServer的信号,通知服务器对话框更新相应的显示状态19 for(int i = 0;i<tcpClientSocketList.count();i )//实现广播,即将新发送到服务器中的数据进行广播,发送到每一个连接的对象,进行同步更新对话框20 {21 QTcpSocket *item = tcpClientSocketList.at(i);//22 if(item->write(msg.toLatin1(),length)!=length)23 {24 continue;25 }26 }27 }28 void Server::slotDisconnected(int descriptor)//从tcpClientSocketList列表中将断开连接的TcpClientSocket对象删除29 {30 for (int i = 0;i<tcpClientSocketList.count();i ) {31 QTcpSocket *item = tcpClientSocketList.at(i);32 if(item->socketDescriptor()==descriptor)33 {34 tcpClientSocketList.removeAt(i);35 return;36 }37 }38 return;39 }

第三部分:

1 TcpClientSocket::TcpClientSocket(QObject *parent) 2 { 3 connect(this,SIGNAL(readyRead()),this,SLOT(dateReceived()));//readyRead()是QIODevice的一个信号函数,由QTcpSocket继承而来,在有数据来时发出信号 4 connect(this,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));//disconnected()是QIODevice的一个信号函数,由QTcpSocket,断开连接时发出信号 5 } 6 void TcpClientSocket::dateReceived() 7 { 8 while(bytesAvailable()>0)//当有数据来时,bytesAvailable()从套接字中检测所来的数据,返回等待读取的传入字节数 9 {10 int length = bytesAvailable();11 char buf[1024];12 read(buf,length);//将套接字中的数据读取到buf中,读取长度为length13 QString msg = buf;14 emit updateClients(msg,length);//发出信号15 }16 }17 void TcpClientSocket::slotDisconnected()18 {19 emit disconnected(this->socketDescriptor());//发出disconnected信号20 }

部分代码详细解析

1.监听

监听主要依托listen()函数实现,listen()函数由QTcpServer的对象调用,接受两个参数,第一个参数为QHostAddress的枚举,上文中的QHostAddress::Any为任意地址的意思,包括IPv4IPv6.而第二个参数为整型的类型,用来代表端口,listen()函数返回一个布尔类型来表示监听是否成功.

2.连接

当客户端连接到端口时(如何连接将在客户端实验在阐述),由于已经建立了监听的关系,我们便可以利用一个QTcpServer中一个虚函数–incomingConnection(),该函数当有服务器端连入时便会触发,我们可以通过重写该函数来完成客户端接入的信息传递,该函数接受一个qintstr 类型的参数,该参数代表了接受连接的本机套接字描述符,该参数的作用将在读取数据时体现

3.读取数据

我们想要接受客户端发送的信息,除了监听相应的端口,与之建立连接还需要一个服务器端的套接字来接受客户端发送的数据,创建一个套接字我们可以使用QTcpSocket 的对象来使用一个setSocketDescriptor()函数,该函数的作用为初始化套接字,接受一个qintstr的参数(就是在在上文中提到的那个)来指明该套接字接受哪个的数据,当成功创建了一个套接字时,我们便可以采用readyRead()函数来发出信号,该函数当有新的可读数据在套接字时,便可以发出信号,为之我们建立相应的槽函数.在槽函数中可以使用byteAvailable()函数来获取可读取数据的字节数,而正式读取数据时使用read()函数便可以了,read()函数有两个参数,第一个参数为读取数据的存储位置,第二个参数为读取多少数据,我们可以使用byteAvailable()返回的值来完成全部读取.

相关新闻

联系我们
联系我们
在线咨询
分享本页
返回顶部