Qt TCP/IP(多客户端连接服务器)多个客户端同时登陆的聊天室示例
一、TCP和UDP的区别
这里我会用一个表格来显示这两者的区别
比较项 |
TCP |
UDP |
是否连接 |
面向连接 |
无连接 |
传输是否可靠 |
可靠 |
不可靠 |
流量控制 |
提供 |
不提供 |
工作方式 |
全双工 |
可以是全双工 |
应用场合 |
大量数据 |
少量数据 |
速度 |
慢 |
快 |
二、incomingConnection函数
这个函数和之前讲过的newConnection信号功能差不多,只要有新的连接出现,就会自动调用这个函数。
然后我们只需在这个函数中新建一个QTcpSocket对象,并且将这个套接字指定为这个函数的参数socketDescriptor,然后将这个套接字存放到套接字列表中就可以实现多个客户端同时登陆了。
这里我们简单看一下这个函数里的内容
1 void Server::incomingConnection(int socketDescriptor)
2 {
3 TcpClientSocket *tcpclientsocket = new TcpClientSocket(this);//只要有新的连接就生成一个新的通信套接字
4 //将新创建的通信套接字描述符指定为参数socketdescriptor
5 tcpclientsocket->setSocketDescriptor(socketDescriptor);
6
7 //将这个套接字加入客户端套接字列表中
8 tcpclientsocketlist.append(tcpclientsocket);
9 }
Server这个类是继承于QTcpServer类的,所以我们需要在Server类中重写incomingConnection函数。
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
三、多个客户端同时登陆的小聊天室示例。
首先说明一下这个小示例的功能,当有一个客户端进入聊天室的时候,会向服务器发送一个信息,告诉服务器这个账号进入了聊天室, 然后服务器会在界面中显示哪个账号进入了聊天室,并且服务器会向所有的客户端发送消息,告诉所有客户端有哪个账号进入了聊天室。某个账号发送信息的时候也是如此,服务器会将收到的信息发送给所有的客户端。
如果需要实现网络通信,就必须在pro文件中加入 QT += network
服务器端
在服务器端需要添加三个类,一个类用来创建界面,一个类用来监听,这个类的基类是QTcpServer,最后一个类用来通信,通信的类的基类是QTcpSocket类。
1、用来创建界面的Tcpserver类
1 #ifndef TCPSERVER_H2 #define TCPSERVER_H3 4 #include <QWidget>5 #include "server.h"6 7 namespace Ui {8 class TcpServer;9 }
10
11 class TcpServer : public QWidget
12 {
13 Q_OBJECT
14
15 public:
16 explicit TcpServer(QWidget *parent = 0);
17 ~TcpServer();
18
19 private:
20 Ui::TcpServer *ui;
21 int port;
22 Server *server;
23
24 protected slots:
25 void slotupdateserver(QString, int);//接收到server发过来的信号就更新界面的信息
26
27
28 private slots:
29 void on_pushButtonCreateChattingRoom_clicked();
30 };
31
32 #endif // TCPSERVER_H
1 #include "tcpserver.h"2 #include "ui_tcpserver.h"3 4 5 TcpServer::TcpServer(QWidget *parent) :6 QWidget(parent),7 ui(new Ui::TcpServer)8 {9 ui->setupUi(this);
10 port = 8888;
11
12 }
13
14 TcpServer::~TcpServer()
15 {
16 delete ui;
17 }
18
19 void TcpServer::on_pushButtonCreateChattingRoom_clicked()
20 {
21 server = new Server(this, port);
22 connect(server, &Server::updateserver, this, &TcpServer::slotupdateserver);
23 ui->pushButtonCreateChattingRoom->setEnabled(false);
24 }
25
26 void TcpServer::slotupdateserver(QString msg, int length)
27 {
28 ui->textEdit->append(msg);
29 }
2、用来监听的类Server
1 #ifndef SERVER_H2 #define SERVER_H3 4 #include <QTcpServer>5 #include <QObject>6 #include <QList>7 #include "tcpclientsocket.h"8 9 class Server : public QTcpServer
10 {
11 Q_OBJECT //为了实现信号和槽的通信
12 public:
13 Server(QObject *parent = 0, int port = 0);
14 QList<TcpClientSocket*> tcpclientsocketlist;
15 protected:
16 void incomingConnection(int socketDescriptor);//只要出现一个新的连接,就会自动调用这个函数
17 protected slots:
18 void sliotupdateserver(QString, int);//用来处理tcpclient发过来的信号
19 void slotclientdisconnect(int);
20
21 signals:
22 void updateserver(QString, int);//发送信号给界面,让界面更新信息
23
24 };
25
26 #endif // SERVER_H
1 #include "server.h"2 #include <QHostAddress>3 4 Server::Server(QObject *parent, int port):QTcpServer(parent)5 {6 listen(QHostAddress::Any, port); //监听7 }8 9 void Server::incomingConnection(int socketDescriptor)
10 {
11 TcpClientSocket *tcpclientsocket = new TcpClientSocket(this);//只要有新的连接就生成一个新的通信套接字
12 //将新创建的通信套接字描述符指定为参数socketdescriptor
13 tcpclientsocket->setSocketDescriptor(socketDescriptor);
14
15 //将这个套接字加入客户端套接字列表中
16 tcpclientsocketlist.append(tcpclientsocket);
17
18
19 //接收到tcpclientsocket发送过来的更新界面的信号
20 connect(tcpclientsocket, &TcpClientSocket::updateserver, this, &Server::sliotupdateserver);
21 connect(tcpclientsocket, &TcpClientSocket::clientdisconnected, this, &Server::slotclientdisconnect);
22
23 }
24
25 void Server::sliotupdateserver(QString msg, int length)
26 {
27 //将这个信号发送给界面
28 emit updateserver(msg, length);
29
30 //将收到的信息发送给每个客户端,从套接字列表中找到需要接收的套接字
31 for(int i = 0; i < tcpclientsocketlist.count(); i++)
32 {
33 QTcpSocket *item = tcpclientsocketlist.at(i);
34 // if(item->write((char*)msg.toUtf8().data(), length) != length)
35 // {
36 // continue;
37 // }
38 item->write(msg.toUtf8().data());
39 }
40
41 }
42
43 void Server::slotclientdisconnect(int descriptor)
44 {
45 for(int i = 0; i < tcpclientsocketlist.count(); i++)
46 {
47 QTcpSocket *item = tcpclientsocketlist.at(i);
48 if(item->socketDescriptor() == descriptor)
49 {
50 tcpclientsocketlist.removeAt(i);//如果有客户端断开连接, 就将列表中的套接字删除
51 return;
52 }
53 }
54 return;
55 }
3、用来通信的类TcpClientSocket
1 #ifndef TCPCLIENTSOCKET_H2 #define TCPCLIENTSOCKET_H3 4 #include <QTcpSocket>5 6 class TcpClientSocket : public QTcpSocket7 {8 Q_OBJECT //添加这个宏是为了实现信号和槽的通信9 public:
10 TcpClientSocket(QObject *parent = 0);
11 protected slots:
12 void receivedata();//处理readyRead信号读取数据
13 void slotclientdisconnected();//客户端断开连接触发disconnected信号,这个槽函数用来处理这个信号
14
15 signals:
16 void updateserver(QString, int);//用来告诉tcpserver需要跟新界面的显示
17 void clientdisconnected(int); //告诉server有客户端断开连接
18 };
19
20 #endif // TCPCLIENTSOCKET_H
1 #include "tcpclientsocket.h"2 3 TcpClientSocket::TcpClientSocket(QObject *parent)4 {5 //客户端发送数据过来就会触发readyRead信号6 connect(this, &TcpClientSocket::readyRead, this, &TcpClientSocket::receivedata);7 connect(this, &TcpClientSocket::disconnected, this, &TcpClientSocket::slotclientdisconnected);8 }9
10 void TcpClientSocket::receivedata()
11 {
12 // while(bytesAvailable() > 0)
13 // {
14 // int length = bytesAvailable();
15 // char buf[1024]; //用来存放获取的数据
16 // read(buf, length);
17 // QString msg = buf;
18 // //发信号给界面,让界面显示登录者的信息
19 // emit updateserver(msg, length);
20 // }
21 int length = 10;
22 QByteArray array = readAll();
23 QString msg = array;
24 emit updateserver(msg, length);
25 }
26
27 void TcpClientSocket::slotclientdisconnected()
28 {
29 emit clientdisconnected(this->socketDescriptor());
30 }
客户端
客户端只需要一个类就行了,这个类我们只需要创建一个通信套接字来和服务器进行通信就可以了。
1 #ifndef TCPCLIENT_H2 #define TCPCLIENT_H3 4 #include <QWidget>5 #include <QTcpSocket>6 7 namespace Ui {8 class TcpClient;9 }
10
11 class TcpClient : public QWidget
12 {
13 Q_OBJECT
14
15 public:
16 explicit TcpClient(QWidget *parent = 0);
17 ~TcpClient();
18
19 private slots:
20 void on_pushButtonEnter_clicked();
21 void slotconnectedsuccess();//用来处理连接成功的信号
22 void slotreceive();//接收服务器传过来的信息
23 void on_pushButtonSend_clicked();
24 void slotdisconnected();//用来处理离开聊天室的信号
25
26
27 private:
28 Ui::TcpClient *ui;
29 bool status;//用来判断是否进入了聊天室
30 int port;
31 QHostAddress *serverIP;
32 QString userName;
33 QTcpSocket *tcpsocket;
34 };
35
36 #endif // TCPCLIENT_H
1 #include "tcpclient.h"2 #include "ui_tcpclient.h"3 #include <QHostAddress>4 #include <QMessageBox>5 6 TcpClient::TcpClient(QWidget *parent) :7 QWidget(parent),8 ui(new Ui::TcpClient)9 {10 ui->setupUi(this);11 //将进入聊天室的标志位置为false12 status = false;13 //端口为888814 port = 8888;15 ui->lineEditServerPort->setText(QString::number(port));//界面中端口默认显示888816 17 serverIP = new QHostAddress();18 19 //未进入聊天室内不能发送信息20 ui->pushButtonSend->setEnabled(false);21 }22 23 TcpClient::~TcpClient()24 {25 delete ui;26 }27 28 //进入聊天室29 void TcpClient::on_pushButtonEnter_clicked()30 {31 //首先判断这个用户是不是在聊天室中32 if(status == false)33 {34 //不在聊天室中就和服务器进行连接35 QString ip = ui->lineEditServerIp->text();//从界面获取ip地址36 if(!serverIP->setAddress(ip))//用这个函数判断IP地址是否可以被正确解析37 {38 //不能被正确解析就弹出一个警告窗口39 QMessageBox::warning(this, "错误", "IP地址不正确");40 return;41 }42 if(ui->lineEditUserName->text() == "")43 {44 //用户名不能为空45 QMessageBox::warning(this, "错误", "用户名不能为空");46 return;47 }48 49 //从界面获取用户名50 userName = ui->lineEditUserName->text();51 //创建一个通信套接字,用来和服务器进行通信52 tcpsocket = new QTcpSocket(this);53 54 //和服务器进行连接55 tcpsocket->connectToHost(*serverIP, port);56 57 //和服务器连接成功能会触发connected信号58 connect(tcpsocket, &QTcpSocket::connected, this, &TcpClient::slotconnectedsuccess);59 //接收到服务器的信息就会触发readyRead信号60 connect(tcpsocket, &QTcpSocket::readyRead, this, &TcpClient::slotreceive);61 62 63 64 //将进入聊天室的标志位置为true;65 status = true;66 }67 else//已经进入了聊天室68 {69 int length = 0;70 QString msg = userName + ":Leave Chat Room";71 // if((length = tcpsocket->write((char*)msg.toUtf8().data(), msg.length())) != msg.length())72 // {73 // return;74 // }75 tcpsocket->write(msg.toUtf8().data());76 tcpsocket->disconnectFromHost();77 status = false;78 //离开聊天室就会触发disconnected信号79 connect(tcpsocket, &QTcpSocket::disconnected, this, &TcpClient::slotdisconnected);80 }81 }82 83 //用来处理连接成功的信号84 void TcpClient::slotconnectedsuccess()85 {86 //进入聊天室可以发送信息了87 ui->pushButtonSend->setEnabled(true);88 //将进入聊天的按钮改为离开聊天室89 ui->pushButtonEnter->setText("离开聊天室");90 91 int length = 0;92 //将用户名发送给服务器93 QString msg= userName + " :Enter Chat Room";94 95 // if((length = tcpsocket->write((char*)msg.toUtf8().data(), msg.length())) != msg.length())96 // {97 // return;98 // }99 tcpsocket->write(msg.toUtf8().data());
100 }
101
102
103 void TcpClient::slotreceive()
104 {
105 // while(tcpsocket->bytesAvailable() > 0 )
106 // {
107 // QByteArray datagram;
108 // datagram.resize(tcpsocket->bytesAvailable());
109 // tcpsocket->read(datagram.data(), datagram.size());
110 // QString msg = datagram.data();
111 // ui->textEdit->append(msg.left(datagram.size()));
112 // }
113 QByteArray array = tcpsocket->readAll();
114 ui->textEdit->append(array);
115 }
116
117 void TcpClient::on_pushButtonSend_clicked()
118 {
119 if(ui->lineEditSend->text() == "")
120 {
121 return;
122 }
123 QString msg = userName + ":" + ui->lineEditSend->text();
124 // tcpsocket->write((char*)msg.toUtf8().data(), msg.length());
125 tcpsocket->write(msg.toUtf8().data());
126 ui->lineEditSend->clear();
127 }
128
129 void TcpClient::slotdisconnected()
130 {
131 ui->pushButtonSend->setEnabled(false);
132 ui->pushButtonEnter->setText("进入聊天室");
133 }
编译完之后运行的界面就是这样的
整个通信的步骤
首先我们点击服务器的创建聊天室,TcpServer类的中的相应的槽函数会创建一个Server类的对象,然后调用构造函数会进行监听。一旦有客户端点击进入聊天室的按钮,客户端的tcpsocket就会和服务器进行连接,只要服务器监听到新的连接,Server类对象就会自动调用incomingConnection(int socketDescripter)这个函数。
接着我们只需要在这个函数中创建一个通信套接字也就是TcpClientSocket对象,并且将这个套接字描述符设定为参数socketDescripter,然后将这个套接字加入套接字列表中。这个时候客户端和服务器连接成功了,客户端就会触发一个connected信号,需要我们写一个槽函数来处理这个信号,这里的处理就是将这个用户名和“Enter Chat Room”发送给服务器。
服务器一旦收到信息,就会触发readyRead信号,这个时候我们需要在写一个相应的槽函数来读取套接字中的信息。我们这里是用readall来读取套接字中的所有信息。
当收到数据的时候,我们需要发一个信号给Server,将读取的信息再发送给所有在聊天室里的用户,所以在Server类中要添加一个相应的槽函数来将数据发送给所有的用户。
因为同时需要在服务器界面中显示所有用户发过来的消息,所以需要在刚刚那个槽函数中将自己定义的一个信号发送给TcpServer类,让其将收到的消息显示在界面中。客户端收到消息之后也会触发readyRead信号,客户端只需要将从套接字中读取的消息显示在见面中就行了。
客户端发送消息的时候需要从QLineEdit对象中读取内容,然后通过tcpsocket->write()函数将消息发送给服务器就行了。服务器同样会将收到的信息发送给所有的用户,同时显示在自己的界面中。最后客户端退出聊天室,客户端点击离开聊天室按钮,进入相应的槽函数中调用disconnectFromHost()函数,就会和服务器断开连接,一旦有调用了这个函数,就会触发disconnected信号,然后我们需要写一个相应的槽函数来处理这个信号,我们所做的处理就是将这个用户名和“Leave Chat Roo”发送给服务器。服务器那一端检测到有客户端断开连接也会触发disconnected信号,这个时候我们需要将套接字列表中将对应的套接字删除就可以了。
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
Qt TCP/IP(多客户端连接服务器)多个客户端同时登陆的聊天室示例相关推荐
- python tcp多个客户端连接服务器
一.传输层** 该层为两台主机上的应用程序提供端到端的通信.传输层有两个传输协议:TCP(传输控制协议)和 UDP(用户数据报协议).其中,TCP是一个可靠的面向连接的协议,udp是不可靠的或者说无连 ...
- MQTT客户端连接服务器协议,mqtt客户端和服务器长连接
mqtt客户端和服务器长连接 内容精选 换一换 介绍设置客户端和服务器的安全认证方式的相关参数.参数说明:表明与服务器建立链接后,不进行任何操作的最长时间.参数类型:USERSET取值范围:整型,0- ...
- t6服务器验证密码失败,T6客户端连接服务器报错误-SA用户登陆失败
固定资产恢复月末结账时提示BOF或EOF中有一个值是真错误解决方法固定资产恢复月末结账时提示BOF或EOF中有一个值是真错误 问题名称:固定资产恢复月末结账时提示BOF或EOF中有一个值是真错误问题现 ...
- 金蝶系统怎么在新客户端连接服务器地址,金蝶客户端怎么连接云服务器
金蝶客户端怎么连接云服务器 内容精选 换一换 本章节指导您使用MongoDB客户端,通过弹性云服务器内网方式连接GaussDB(for Mongo)副本集实例.操作系统使用场景:弹性云服务器的操作系统 ...
- 服务器修改了 金碟软件用不到,金蝶软件金蝶软件KIS系统客户端连接服务器时,有时会有连接不上的情况,提示服务器不是有效的,请重新设置...
金蝶软件金蝶软件KIS系统客户端连接服务器时,有时会有连接不上的情况,提示服务器不是有效的,请重新设置 金蝶软件KIS系统客户端连接服务器时,有时会有连接不上的情况,提示"服务器不是有效的, ...
- t3客户端连接服务器就未响应,t3标准版客户端连接服务器方法
t3标准版客户端连接服务器方法 内容精选 换一换 SSL证书是一种遵守SSL协议的服务器数字证书,可以在客户端和服务器端之间建立加密通道,保证数据在传输过程中不被窃取或篡改.为了提高数据安全性,Gau ...
- 重装金蝶专业版后服务器不显示错误,金蝶软件KIS系统客户端连接服务器时,有时会有连接不上的情况,提示服务器不是有效的,请重新设置...
金蝶软件KIS系统客户端连接服务器时,有时会有连接不上的情况,提示服务器不是有效的,请重新设置 金蝶软件KIS系统客户端连接服务器时,有时会有连接不上的情况,提示"服务器不是有效的,请重新设 ...
- 虚拟机客户端怎么连接服务器,虚拟机客户端连接服务器
虚拟机客户端连接服务器 内容精选 换一换 本章节指导您使用MongoDB客户端和Robo 3T工具,通过公网连接集群实例.通过MongoDB客户端和Robo 3T工具连接实例的方式有普通连接和SSL连 ...
- u8系统怎么连接服务器,怎么U8客户端连接服务器
怎么U8客户端连接服务器 内容精选 换一换 本章节为您介绍以下内容:准备弹性云服务器作为GDS服务器在使用GDS导入导出数据之前,需要准备一台或多台与GaussDB(DWS) 集群在相同VPC内的Li ...
最新文章
- 除了Tapd,还有哪些好用的项目管理工具,适用于100+人的大中型团队?
- 找一个可以@user的gem。
- 定义了浮动元素后margin-bottom失效的解决办法
- Spring多线程批量发送邮件(ThreadPoolTaskExecutor)
- 【BZOJ4237】稻草人
- 本地搭建wp,更新升级时需要ftp的解决办法
- 利用 VBA 和 HTML自制兼容 WPS及 EXCEL(32位/64位)的颜色选择器
- bzoj4513 [Sdoi2016]储能表 dp
- 朋友圈终于对利诱打卡行为动手了!多款英语学习类软件中枪
- 【BZOJ3489】A simple rmq problem(树套树)
- 小D课堂 - 新版本微服务springcloud+Docker教程_3-06 服务注册和发现之Eureka Client搭建商品服务实战...
- stm32f adc matlab 串口,基于Matlab和串口通信的ADC动态性能FFT测试法
- VB.Net视频教程和VBA简单介绍下载
- net-java-php-python-社会福利保障系统计算机毕业设计程序
- dell电脑装双系统linux,戴尔电脑怎么装双系统?戴尔装win7+win10双系统详细教程
- 【Angular中的RxJS】- Subject 分类介绍
- 深入理解android虚拟机
- Citrix Receiver for Linux安装和cert证书添加解决SSL错误
- 一句话木马、中国菜刀、图片一句话制作、过狗一句话
- java进行参数快捷校验,ehi-verification
热门文章
- 杂项-职位-软件测试工程师:软件测试工程师
- Git常用的指令整理
- java win10窗口启动假死_win10桌面假死如何解决
- 实时监控linux的日志命令
- 【强化学习】用强化学习通关超级马里奥!
- 【Hadoop】Hive HSQ 使用 自定义HQL函数
- GB50204-2015 混凝土结构工程施工质量验收规范 免费下载
- linux系统tomcat升级,Tomcat 7 升级到 Tomcat 8 历程
- .NET Core 开源工具 IPTools - 快速查询 IP 地理位置、经纬度信息
- 视觉软件 VisionPro 定位引导3(卡尺CogFindLineTool工具以及辅助工具)