server.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget),server(new QTcpServer(this)) // 给服务器指针对象实例化空间{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}//启动服务器按钮对应的槽函数
void Widget::on_startBtn_clicked()
{//获取ui界面上的端口号quint16 port = ui -> portEdit -> text().toUInt();//服务器设置监听// bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);// 参数1:监听的地址,指定的主机,或任意// 参数2:监听的端口号,可以是指定,也可是系统提供bool ret = server -> listen(QHostAddress::Any, port);if(ret == false){QMessageBox::information(this, "", "启动服务器失败");return;}ui -> startBtn -> setText("已启动");//此时若有客户端发来连接请求,那么服务器就会自动发射一个newConnect()信号//我们就可以将该信号连接到自定义的槽函数中,获取客户端的套接字connect(server, &QTcpServer::newConnection, this, &Widget::new_connection_slot);}//有新的客户端连接 connect 对应的槽函数
void Widget::new_connection_slot()
{//连接最先连接的客户端套接字// virtual QTcpSocket *nextPendingConnection();//返回值客户端的套接字QTcpSocket * s = server -> nextPendingConnection();//将获取到的客户端放入客户端容器中 尾插socketList.push_back(s);//程序运行至此,客户端和服务端成功建立了连接//若客户端发来数据,那么客户端就会自动发送一个readyRead()函数//connect(s, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);
}//readyRead()信号对应的槽函数
void Widget::readyRead_slot()
{//遍历客户端容器,移除无效客户端for(int i = 0; i < socketList.count(); i++){if(socketList.at(i) -> state() == 0){//移除无效客户端socketList.removeAt(i);i--;}}//读取发来的数据for (int i = 0; i < socketList.count(); i++) {//判断客户端是否有数据待读//bytesAvailable();if(socketList.at(i) -> bytesAvailable() != 0){//读取数据QByteArray msg = socketList.at(i) -> readAll();//将读取的数据放入ui界面ui -> listWidget -> addItem(QString::fromLocal8Bit(msg));//将数据广播给所有的客户端for (int j = 0; j < socketList.count(); j++) {//不发送信息到发送信息的客户端if(i == j){continue;}socketList.at(j) -> write(msg);}}}
}
sever.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket> //客户端的类
#include <QMessageBox>
#include <QList> // 链表容器QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();public slots:void on_startBtn_clicked();void new_connection_slot();void readyRead_slot();private:Ui::Widget *ui;QTcpServer *server;//定义一个存放客户端的容器//template <typename T>//class QListQList<QTcpSocket *> socketList;
};
#endif // WIDGET_H
client.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget),socket(new QTcpSocket(this)) //给客户端实例空间 ,指定父对象this
{ui->setupUi(this);//初始化界面ui -> usrEdit -> setText("风呤");ui -> portEdit -> setText("8888");ui -> ipEdit -> setText("192.168.127.22");ui -> msgEdit -> setEnabled(false);ui -> sendbtn -> setEnabled(false);ui -> disLinkEdit -> setEnabled(false);
// ui -> listWidget -> setMaxLength();//设置文本自动换行ui -> listWidget -> setWordWrap(true);connect(socket, &QTcpSocket::connected, this, &Widget::connected_slot);connect(socket, &QTcpSocket::disconnected, this, &Widget::disconnect_slot);}Widget::~Widget()
{delete ui;
}//连接服务器按钮对应的槽函数
void Widget::on_linkBtn_clicked()
{//获取ui界面上的ip和端口号QString ip = ui -> ipEdit -> text();quint16 port = ui -> portEdit -> text().toUInt();//让客户端连接服务器//virtual void connectToHost(const QString &hostName, quint16 port,//OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);socket -> connectToHost(ip, port);//若成功连接服务器,那么客户端就会发送一个connected()信号//那么我们就可以将该信号连接到自定义的槽函数中处理逻辑代码,由于只需连接一次,故连接函数可写入构造函数中connect(socket, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);
}void Widget::connected_slot()
{//告诉服务器我来了usrname = ui -> usrEdit -> text();QString msg = usrname + ":已进入聊天室";//将信息发送到服务器socket -> write(msg.toLocal8Bit());//连接服务器成功QMessageBox::information(this, "", "已连接");//组件可用的相关设置ui -> msgEdit -> setEnabled(true);ui -> sendbtn -> setEnabled(true);ui -> disLinkEdit -> setEnabled(true);ui -> ipEdit -> setEnabled(false);ui -> linkBtn -> setEnabled(false);ui -> portEdit -> setEnabled(false);ui -> usrEdit -> setEnabled(false);//程序运行到此,说明客户端成功与服务器建立连接,若服务器发来数据,那么客户端就会自动发射一个readyRead信号//我们就可以将该信号连接到自定义的槽函数,读取数据,
}void Widget::disconnect_slot()
{ui -> msgEdit -> setEnabled(false);ui -> sendbtn -> setEnabled(false);ui -> disLinkEdit -> setEnabled(false);ui -> ipEdit -> setEnabled(true);ui -> linkBtn -> setEnabled(true);ui -> portEdit -> setEnabled(true);ui -> usrEdit -> setEnabled(true);
}void Widget::readyRead_slot(){//QByteArray msg = socket -> readAll();//ui -> listWidget -> addItem(QString::fromLocal8Bit(msg));}//发送按钮对应的槽函数
void Widget::on_sendbtn_clicked()
{//获取ui界面上的信息QString msg = ui -> msgEdit -> text();QString msg1 = msg + ":" + usrname;msg = usrname + ":" + msg;//发送给服务器socket -> write(msg.toLocal8Bit());//将发送的文本输出到listWidget中QListWidgetItem * item= new QListWidgetItem(msg1);ui-> listWidget -> addItem(item);item->setTextAlignment(Qt::AlignRight);//右对齐//清空行编辑器ui -> msgEdit -> clear();
}void Widget::on_disLinkEdit_clicked()
{//告诉服务器断开连接QString msg = usrname + ":已离开";socket -> write(msg.toLocal8Bit());socket -> disconnectFromHost();//若成功断开,那么客户端就会发送一个disconnected信号//我们就可以将该信号连接到自定义的槽函数}
client.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket>
#include <QMessageBox>
#include <QLineEdit>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_linkBtn_clicked();void connected_slot();void disconnect_slot();void readyRead_slot();void on_sendbtn_clicked();void on_disLinkEdit_clicked();private:Ui::Widget *ui;QTcpSocket *socket;QString usrname;
};
#endif // WIDGET_H