目录
学习内容:
1. 服务器端(QTcpServer)
1> 使用QTcpServer类实例化一个对象,就得到了一个服务器端
2> 调用该类对象的成员函数 listen 将服务器启动监听,该函数会进行绑定ip和端口号
ip地址可以指定也可以由系统自动绑定,端口号也可以自己指定和由系统自动指定
3> 当有客户端发来连接请求后,该服务器就会自动发射一个newConnection的信号
我们可以将该信号绑定到自定义的槽函数中完成相关逻辑
4> 可以使用类中的成员函数 nextPenddingConnection 可以获取最新连接的客户端套接字
5> 可以使用该客户端套接字进行数据收发
read、readLine、readAll读取数据
write发送数据
6> 当服务器收到客户端的消息后,该服务器会自动发射一个readyRead的信号
可以将该信号连接到对应的槽函数中,处理客户端发来的消息
7> 调用成员函数close关闭监听
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTcpServer> //tcp服务器类
#include<QTcpSocket> //tcp客户端类
#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 readyRead_slot(); //自定义处理readyRead信号的槽函数
private:
Ui::Widget *ui;
QTcpServer *server; //定义服务器指针
QList<QTcpSocket *> socketList; //定义存放客户端信息的容器
};
#endif // WIDGET_H
源文件
#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("启动服务器");
}
}
//自定义处理newConnection信号的槽函数的实现
void Widget::newConnection_slot()
{
qDebug()<<"有新的客户端发来连接请求了";
//可以通过成员函数 nextPendingConnection函数获取最新连接的客户端套接字的地址
//函数原型:QTcpSocket *nextPendingConnection();
//无参函数
//返回值:最新的一个连接的套接字地址
QTcpSocket * s = server->nextPendingConnection();
//将该套接字放入客户端链表中
socketList.push_back(s);
//程序执行至此,一个服务器可以对应多个客户端,已经建立了连接
//此时,如果有某个客户端发来数据,那么该客户端套接字就会自动发射一个readyRead的信号
//我们可以将该信号连接到自定义的槽函数中处理相关逻辑
connect(s, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);
}
//自定义处理readyRead信号的槽函数的实现
void Widget::readyRead_slot()
{
//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. 客户端(QTcpSocket)
1> 使用该类实例化一个对象,就创建了一个客户端
2> 使用该类对象的成员函数 connectToHost 向服务器发送连接请求
如果连接服务器成功,那么该客户端套接字就会自动发射一个 connected 的信号
可以将该信号连接到自定义的槽函数中处理相关逻辑
3> 可以使用该客户端套接字进行数据收发
read、readLine、readAll读取数据
write发送数据
4> 如果客户端收到服务器发来的消息后,该客户端就会自动发射一个readyRead的信号
可以将该信号连接到对应的槽函数中,处理客户端发来的消息
5> 调用成员函数 disconnectFromHost 断开跟服务器的连接
头文件
#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 on_connectBtn_clicked();
void connected_slot(); //自定义处理connected信号的槽函数
void readyRead_slot(); //自定义处理readyRead信号的槽函数
void disconnected_slot(); //自定义处理disconnected信号的槽函数
void on_sendBtn_clicked();
void on_disConnectBtn_clicked();
private:
Ui::Widget *ui;
//定义通信用的变量
QTcpSocket *client; //定义套接字指针
QString userName; //用户名
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//实例化客户端对象
client = new QTcpSocket(this);
//当客户端与服务器建立联系后,如果客户端接受到服务器发来的消息
//客户端自身就会自动发射一个 readyRead的信号,我们可以将该信号连接到自定义的槽函数中执行相关逻辑
connect(client, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);
//如果当前客户端成功连接的服务器,那么该客户端就会自动发射一个connected的信号
//我们可以将该信号连接到自定义的槽函数中处理相关逻辑
connect(client, &QTcpSocket::connected, this, &Widget::connected_slot);
//当客户端断开了与服务器的连接后,该客户端就会自动发射一个disconnected的信号
//我们可以将该信号连接到自定义的槽函数中处理相关逻辑
connect(client, &QTcpSocket::disconnected, this, &Widget::disconnected_slot);
}
Widget::~Widget()
{
delete ui;
}
//连接服务器按钮对应的槽函数
void Widget::on_connectBtn_clicked()
{
//获取ui界面上的数据
QString ip = ui->ipEdit->text(); //ip地址
quint16 port = ui->portEdit->text().toUInt(); //端口号
userName = ui->nameEdit->text(); //用户名
//调用套接字成员函数,连接服务器
//函数原型:void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
//参数1:要被连接的服务器ip地址
//参数2:服务器的端口号
//参数3:默认为可读可写
//返回值:无
client->connectToHost(ip, port);
}
//处理connected信号的槽函数的定义
void Widget::connected_slot()
{
QMessageBox::information(this,"成功","连接成功");
//将相关组件禁用
ui->ipEdit->setEnabled(false);
ui->nameEdit->setEnabled(false);
ui->portEdit->setEnabled(false);
ui->connectBtn->setEnabled(false);
//向服务器发送一条消息
QString msg = userName + ": 进入聊天室";
client->write( msg.toLocal8Bit() );
}
//自定义处理readyRead信号的槽函数
void Widget::readyRead_slot()
{
//从套接字中读取数据
QByteArray msg = client->readAll();
//将读取下来的数据展示到ui界面上
ui->msgListWidget->addItem( QString::fromLocal8Bit(msg) );
}
//消息发送按钮对应的槽函数
void Widget::on_sendBtn_clicked()
{
//组织要发送的消息
QString msg = userName + ": " + ui->msgEdit->text();
//将消息发送给服务器
client->write(msg.toLocal8Bit());
//将消息展示到自己界面上
//准备一个QListWidgetItem类的对象
QListWidgetItem *item = new QListWidgetItem(msg);
item->setTextAlignment(Qt::AlignRight); //将文本右对齐
ui->msgListWidget->addItem(item);
//清空消息发送框的内容
ui->msgEdit->clear();
}
//断开连接按钮对应的槽函数
void Widget::on_disConnectBtn_clicked()
{
//执行断开连接的操作
//准备发送消息给服务器
QString msg = userName + ": 离开聊天室";
client->write(msg.toLocal8Bit());
//断开连接
client->disconnectFromHost();
}
//自定义处理disconnected信号的槽函数的定义
void Widget::disconnected_slot()
{
QMessageBox::information(this, "提示", "成功断开与服务器的连接");
//将相关组件启用
ui->ipEdit->setEnabled(true);
ui->nameEdit->setEnabled(true);
ui->portEdit->setEnabled(true);
ui->connectBtn->setEnabled(true);
}
课外作业:
使用绘制事件完成钟表的绘制
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QBrush>
#include <QTimer>
#include <QTime>
#include <QDebug>
#include <QPaintEvent>
#include <QPainter>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent *event) override;
private:
Ui::Widget *ui;
QTimer *timer;
int count = 0;
int hour;
int minute;
int second;
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
timer = new QTimer;
timer->start(1000);
connect(timer,&QTimer::timeout,[&](){
count++;
update();
});
QString t = QTime::currentTime().toString("h:m:s ap");
QStringList list1 = t.split(" ");
QStringList list2 = list1[0].split(":");
hour = list2[0].toUInt();
minute = list2[1].toUInt();
second = list2[2].toUInt();
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
//绘制圆
QPainter p(this);
QPen pen(QColor("skyblue"));
pen.setWidth(3);
QBrush b("pink");
p.setPen(pen);
p.setBrush(b);
p.translate(this->width()/2,this->height()/2);
p.drawEllipse(QPoint(0,0),200,200);
//绘制刻度
pen.setColor(QColor("black"));
p.setPen(pen);
for (int i = 0;i<60;i++) {
p.rotate(6);
p.drawLine(QPoint(200,0),QPoint(195,0));
}
pen.setWidth(5);
p.setPen(pen);
for(int i=0;i<12;i++){
p.rotate(30);
p.drawLine(QPoint(200,0),QPoint(190,0));
p.drawText(QPoint(-5,-165),QString("%1").arg(i+1));
}
//制作时针
pen.setWidth(6);
pen.setColor("blue");
p.setPen(pen);
p.rotate(hour*30+6*second/60/12+30*minute/60+6*count/60/12);
p.drawLine(QPoint(0,-50),QPoint(0,5));
//制作分针
QPainter p1(this);
p1.translate(this->width()/2,this->height()/2);
p1.drawEllipse(QPoint(0,0),200,200);
pen.setWidth(6);
pen.setColor("green");
p1.setPen(pen);
p1.rotate(6*count/60+6*minute+6*second/60);
p1.drawLine(QPoint(0,-100),QPoint(0,2));
//制作秒针
QPainter p2(this);
p2.translate(this->width()/2,this->height()/2);
p2.drawEllipse(QPoint(0,0),200,200);
pen.setWidth(6);
pen.setColor("black");
p2.setPen(pen);
p2.rotate(6*count+6*second);
p2.drawLine(QPoint(0,-100),QPoint(0,2));
}