注意:要加库文件,服务端客户端都要加 network
客户端的头文件
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket>//客户端类
#include <QMessageBox>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_sendbtn_clicked();void on_disconnectbtn_clicked();void on_connectbtn_clicked();
public slots:void connected_slot();void readyRead_slot();void disconnected_slot();private:Ui::Widget *ui;//定义客户端指针QTcpSocket *socket;//定义存储用户名QString username;
};
#endif // WIDGET_H
客户端主函数
#include "widget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
客户端构造函数
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//初始化界面//设置按钮不可以状态ui->msgEdit->setEnabled(false);ui->sendbtn->setEnabled(false);ui->disconnectbtn->setEnabled(false);//给客户端指针实例化空间socket=new QTcpSocket(this);connect(socket,&QTcpSocket::connected,this,&Widget::connected_slot);//此时客户端和服务器已经建立连接,如果服务器发来数据,那么客户端就会自动发射 一个readyRead()信号//我们可以将该信号连接到自定义的槽函数中,读取服务器端的数据,又由只需要连接一次,在构造函数写连接connect(socket,&QTcpSocket::readyRead,this,&Widget::readyRead_slot);//如果成功与服务器断开连接,那么该客户端就会自动发送一个disconnected信号//我们就可以将该信号连接到自定义函数中,处理逻辑代码,由于只要连接一次,所有在构造函数中连接connect(socket,&QTcpSocket::disconnected,this,&Widget::disconnected_slot);
}Widget::~Widget()
{delete ui;
}//连接服务器按钮对应的槽函数处理
void Widget::on_connectbtn_clicked()
{//获取ui界面上的ip和端口号QString ip=ui->ipEdit->text();quint16 port =ui->portEdit->text().toUInt();//将客户连接到服务器//参数1:主机地址//参数2:端口号socket->connectToHost(ip,port);//如果成功连接服务器,那么客户端将发送一个conneted信号//我们就可以将该信号连接到自定义的槽函数中处理逻辑代码,由于只需要连接一次,所有我们在构造函数中写连接}void Widget::connected_slot()
{QMessageBox::information(this,"","连接服务器成!");//告诉服务器,用户上线username =ui->userEdit->text();QString msg=username+": 进入聊天室";//将信息发送给服务器socket->write(msg.toLocal8Bit());//将ui界面上的组件进行相关设置//可用状态ui->msgEdit->setEnabled(true);ui->sendbtn->setEnabled(true);ui->disconnectbtn->setEnabled(true);//不可以状态ui->userEdit->setEnabled(false);ui->ipEdit->setEnabled(false);ui->portEdit->setEnabled(false);ui->connectbtn->setEnabled(false);//此时客户端和服务器已经建立连接,如果服务器发来数据,那么客户端就会自动发射 一个readyRead()信号//我们可以将该信号连接到自定义的槽函数中,读取服务器端的数据,又由只需要连接一次,在构造函数写连接}void Widget::readyRead_slot()
{//将服务器端的数据读取出来QByteArray msg =socket->readAll();//将数据放入ui界面上ui->msgWidget->addItem(QString::fromLocal8Bit(msg));
}//disconnected信号对应的槽函数的实现
void Widget::disconnected_slot()
{QMessageBox::information(this,"","断开服务器成功");ui->msgEdit->setEnabled(false);ui->sendbtn->setEnabled(false);ui->disconnectbtn->setEnabled(false);ui->userEdit->setEnabled(true);ui->ipEdit->setEnabled(true);ui->portEdit->setEnabled(true);ui->connectbtn->setEnabled(true);}//发送按钮对应的槽函数
void Widget::on_sendbtn_clicked()
{//获取ui界面上的数据QString msg=ui->msgEdit->text();//整合信息msg=username+":"+msg;//将数据发送给服务器socket->write(msg.toLocal8Bit());//清空发送框里的内容ui->msgEdit->clear();
}//端口服务器按钮的槽函数处理
void Widget::on_disconnectbtn_clicked()
{//告诉大家我走了QString msg =username+":离开聊天室";//信息发送给服务器socket->disconnectFromHost();//如果成功与服务器断开连接,那么该客户端就会自动发送一个disconnected信号//我们就可以将该信号连接到自定义函数中,处理逻辑代码,由于只要连接一次,所有在构造函数中连接
}
客户端ui界面
服务端头文件
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpServer>
#include <QList> //链表容器
#include <QTcpSocket> //客户端的类
#include <QMessageBox> //消息对话框类QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_staetBtn_clicked();void newConnection_solt(); //newconnection信号对应的槽函数void readyRead_slot();private:Ui::Widget *ui;//定义服务器指针QTcpServer *server;//定义客户端容器QList<QTcpSocket *> socketList;};
#endif // WIDGET_H
服务端主函数
#include "widget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
服务的构造函数
#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_staetBtn_clicked()
{//获取ui界面上的端口号quint16 port = ui->portEdit->text().toInt();//将服务器设置监听状态//函数原型:bool//参数1:主机地址 可以是任意//参数2:端口号if(server->listen(QHostAddress::Any,port)){QMessageBox::information(this,"","启动服务器成功!");}else{QMessageBox::information(this,"","启动服务器失败!");}//此时说明服务器已经进入监听状态,如果有客户端发来连接请求,那么服务器端就会自动发射newconnection//将该信号连接到自定义的槽函数中,获取客户端的套接字connect(server,&QTcpServer::newConnection,this,&Widget::newConnection_solt);
}//newConnection信号对应槽函数处理
void Widget::newConnection_solt()
{qDebug() <<"有新的用户连接" ;//获取最新连接的客户端套接字//函数原型:virtual QTcpSocket *nextPendingConnection();//返回值:是客户端套接字的指针QTcpSocket *s =server->nextPendingConnection();//将套接字放入容器中socketList.push_back(s);//程序运行至此,说明服务端和客户端已经建立起联系,如果客户端发来数据,那么客户端就会自动发射readyrRead()信号//我们就可以将信号连接自定义的槽函数中,读取客户端的数据connect(s,&QTcpSocket::readyRead,this,&Widget::readyRead_slot);}
//readyRead信号对应槽函数声明
void Widget::readyRead_slot()
{//移除无效客户端//count :在容器中的所有元素个数for(int i=0;i<socketList.count();i++){//函数原型:socketstate state() constif(socketList.at(i)->state()==0){//移除socketList.removeAt(i);//将下表为i的 客户端移除}}//遍历有效客户端,寻找哪个客户端有数据待读for (int i=0;i<socketList.count();i++){//函数原型://判断是否有数据if(socketList.at(i)->bytesAvailable()!=0){//读取套接字中的数据QByteArray msg = socketList.at(i)->readAll();//将数据放在ui界面上ui->msgWidget->addItem(QString::fromLocal8Bit(msg));//将数据广播给(发送)给所有客户端for(int j=0;j<socketList.count();j++){//将数据写入到套接字中socketList.at(j)->write(msg);}}}
}
服务端ui界面