【QT】实现TCP服务器,客户端之间的通信
使用QT完成了服务器端和客户端的简单搭建,和纯C语言还是有一定的区别,实现了服务器能够接收客户端连接,能够接收客户端发来的信息显示在窗口中
目录
1、服务器端代码
2、客户端代码
输出结果如下:
1、服务器端代码
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QMessageBox>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_startSerBtn_clicked();
void newConnection_slot(); //自定义处理newConnection槽函数
void readRead_solt(); //
private:
Ui::Widget *ui;
QTcpServer *server; //定义服务器指针
QList<QTcpSocket *> socketList; //定义存放客户端信息的容器
};
#endif // WIDGET_H
widget.h
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
server = new QTcpServer(this);
}
Widget::~Widget()
{
delete ui;
}
//启动服务器按钮对应的槽函数
void Widget::on_startSerBtn_clicked()
{
if(ui->startSerBtn->text() == "启动服务器")
{
//执行启动服务器的动作
//获取ui界面上的端口号
quint16 port = ui->portEdit->text().toUInt();
//启动监听:bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
//参数1:要监听的ip地址,如果写Any,表示监听该主机上的所有网络接口
//参数2:要监听的端口号,如果不指定,系统会默认给绑定一个随机的端口号
//返回值:成功监听返回真,否则假
if(!server->listen(QHostAddress::Any, port))
{
QMessageBox::critical(this, "错误", "服务器启动失败");
return ;
}
//程序执行至此,表示服务器启动成功
QMessageBox::information(this, "成功", "服务器启动成功");
//将行编辑器设置为不可用
ui->portEdit->setEnabled(false);
//将按钮文本内容设置成关闭服务器
ui->startSerBtn->setText("关闭服务器");
//如果有客户端发来连接请求,那么该服务器就会自动发射一个newConnection的信号
//我们可以将该信号连接到自定义的槽函数中,处理后续操作
connect(server, &QTcpServer::newConnection, this, &Widget::newConnection_slot);
}
else
{
//执行关闭服务器的动作
server->close();
//将行编辑器设置成可用状态
ui->portEdit->setEnabled(true);
//将按钮文本内容设置为启动服务器
ui->startSerBtn->setText("启动服务器");
}
}
//定义存放客户端信息的容器
void Widget::newConnection_slot()
{
qDebug()<<"有新的客户端发来连接请求";
//可以通过成员函数 newPendingConnection函数获取最新连接的客户端的套接字的地址
//函数原型:virtual QTcpSocket *nextPendingConnection();
//无参函数
//返回值:最新的一个链接的套接字地址
QTcpSocket *s = server->nextPendingConnection();
//将该套接字放入客户端链表中
socketList.push_back(s);
//程序执行至此,一个服务器可以对应多个客户端,已经建立了连接
//此时,如果有某个客户端发来数据,那么该客户端就会自动发射一个readyRead的信号
//我们可以将该信号连接到自定义的槽函数中处理相关逻辑
connect(s, &QTcpSocket::readyRead, this, &Widget::readRead_solt);
}
//自定义处理readyRead信号的槽函数的实现
void Widget::readRead_solt()
{
//1、遍历链表中的所有客户端,如果客户端的状态为未连接,则直接移除出链表
for(int i = 0; i < socketList.size(); i++)
{
//判断当前套接字 socketList[i] 是否失效
//函数原型:SocketState state() const;
//功能:返回当前套接字的状态
//返回结果为0时,表示该套接字是未连接状态
if(socketList[i]->state() == 0)
{
//将该套接字移除出链表
socketList.removeAt(i);
}
}
//2、遍历所有客户端,判断客户端中是否有数据可读,如果有数据可读,则表示是该客户端发来的信息
for(int i=0; i<socketList.count(); i++)
{
//判断当前客户端是否有客户端可读
//函数原型:qint64 bytesAvailable() const override;
//参数无
//返回值:返回当前客户端套接字中的待读数据,如果没有数据,则返回0
if(socketList[i]->bytesAvailable() != 0)
{
//读取当前套接字的内容
QByteArray msg = socketList[i]->readAll();
//将接收的消息展示到ui界面上
ui->msgListWidget->addItem(QString::fromLocal8Bit(msg));
//将收到的信息,全部发送给其他客户端
for(int j = 0; j<socketList.length();j++)
{
if(i != j) //防止自己连接到自己
{
socketList[j]->write(msg);
}
}
}
}
}
2、客户端代码
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpSocket>
#include <QMessageBox>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void connect_slot(); //自定义处理connected的信号
void on_connectButton_clicked();
private:
Ui::Widget *ui;
//定义通信用的变量
QTcpSocket *client; //定义套接字指针
QString userName; //用户名
};
#endif // WIDGET_H
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widegt.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//实例化客户端对象
client = new QTcpSocket(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_connectButton_clicked()
{
//获取ui界面上的数据
QString ip = ui->ipEdit->text(); //ip地址
quint16 port = ui->portEdit->text().toUInt(); //端口号
userName = ui->nameEdit->text(); //用户名
//调用套接字成员函数连接服务器
//函数原型:virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
//参数1:要被连接的服务器ip地址
//参数2:服务器的端口号
//参数3:默认为可读可写
//返回值:无
client->connectToHost(ip, port);
//如果当前客户端成功连接的服务器,那么该客户端就会自动发送一个connected的信号
//我们可以将该信号连接到自定义的槽函数中处理相关逻辑
connect(client, &QTcpSocket::connected, this, &Widget::connect_slot);
}
void Widget::connect_slot()
{
QMessageBox::information(this, "成功", "连接成功");
//将相关组件禁用
ui->ipEdit->setEnabled(false);
ui->nameEdit->setEnabled(false);
ui->portEdit->setEnabled(false);
ui->connectButton->setEnabled(false);
//向服务器发送一条消息
QString msg = userName + ":进入聊天室";
client->write(msg.toLocal8Bit());
}
输出结果如下:
可以看到能够成功连接到客户端