简易聊天室

功能:使用socket实现广播式通信,强加了一个共享内存实现登录时背景的变化
(代码中可能bug比较多,多包涵)
在这里插入图片描述
环境Ubuntu 18.4,Qt 5.14.2
参考《Qt5开发与实例》与网友的代码

1服务器端
1.1 server.h

//TCP服务器,监听指定端口的TCP连接
class Server : public QTcpServer
{
    Q_OBJECT

public:
    Server(QObject *parent=0,int port=0);
    QList<TcpClientSocket*> tcpClientSocketList;
    void incomingConnection(qintptr socketDescriptor);//此函数不可更改(重写)
signals:
    void updateServer(QString,int);
public slots:
    void updateClients(QString,int);
    void slotDisconnected(int);
};

1.2 server.cpp

Server::Server(QObject* parent, int port)
    : QTcpServer(parent)
{
    listen(QHostAddress::Any, port);
}
//有新的连接出现,自动调用
void Server::incomingConnection(qintptr socketDescriptor)
{
    TcpClientSocket* tcpClientSocket = new TcpClientSocket(this);

    tcpClientSocket->setSocketDescriptor(socketDescriptor);
    //socketDescriptor套接子描述符
    tcpClientSocketList.append(tcpClientSocket); //(e)

    connect(tcpClientSocket, &TcpClientSocket::updateClients, this, &Server::updateClients);
    connect(tcpClientSocket, &TcpClientSocket::disconnected, this, &Server::slotDisconnected);
}
//广播从客户端发来的信息,
void Server::updateClients(QString msg, int length)
{
    //将这个信号发送给界面
    emit updateServer(msg, length);
    //将收到的信息发送给每个客户端,从套接字列表中找到需要接收的套接字
    int i = 0;
    for (i = 0; i < tcpClientSocketList.count(); i++) {
        QTcpSocket* item = tcpClientSocketList.at(i);
        if (item->write(msg.toUtf8().data(), length) != length) {
            continue;
        }
    }
}
//断开连接,删除列表中的tcpClientSocket
void Server::slotDisconnected(int descriptor)
{
    for (int i = 0; i < tcpClientSocketList.count(); i++) {
        QTcpSocket* item = tcpClientSocketList.at(i);
        if (item->socketDescriptor() == descriptor) {
            tcpClientSocketList.removeAt(i);
            return;
        }
    }
    qDebug("删除列表 success\n");
    return;
}

1.3 TcpClientSocket.cpp

#include "tcpclientsocket.h"
#include <QMessageBox>
//创建用户套接字,实现服务器与用户之间的通信
TcpClientSocket::TcpClientSocket(QObject* parent)
{
    //客户端发送数据过来就会触发readyRead信号
    connect(this, &TcpClientSocket::readyRead, this, &TcpClientSocket::dataReceived);
    connect(this, &TcpClientSocket::disconnected, this, &TcpClientSocket::slotDisconnected);
}
//readRead()信号在有数据到来时发出,触发dataReceived()函数
void TcpClientSocket::dataReceived()
{
    while (bytesAvailable() > 0) //bytesAvailable()查看的是数据缓冲区的数据个数
    {
        int length = bytesAvailable();
        char buf[1024];
        read(buf, length);

        QString msg = buf;
        emit updateClients(msg, length);
        //与server有关
	}
}
//disconnected()信号在断开连接时发出
void TcpClientSocket::slotDisconnected()
{
    emit disconnected(this->socketDescriptor());
}

1.4 tcpserver.cpp

#include "tcpserver.h"
#include "ui_tcpserver.h"
#include <QBuffer>
#include <QMessageBox>

#pragma execution_character_set("utf-8")

TcpServer::TcpServer(QWidget* parent)
    : QWidget(parent)
    , ui(new Ui::TcpServer)
{
    ui->setupUi(this);

    sharedMemory = new QSharedMemory("46");

    setWindowTitle(tr("TCP Server"));
    port = 8010;
    ui->lineEdit->setText(QString::number(port));
    connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(slotCreateServer()));
}

TcpServer::~TcpServer()
{
    delete ui;
    delete sharedMemory;
    delete server;
}

void TcpServer::slotCreateServer()
{
    ui->listWidget->addItem(tr("creat a chatroom successfully"));
    server = new Server(this, port); //创建一个Server对象
    connect(server, SIGNAL(updateServer(QString, int)), this,
        SLOT(updateServer(QString, int))); //(a)
    ui->pushButton->setEnabled(false);
}
//更新服务器上的
void TcpServer::updateServer(QString msg, int length)
{
    ui->listWidget->addItem(msg.left(length));
}

void TcpServer::on_chooseBtn_clicked()
{
    connect(ui->chooseBtn, &QPushButton::clicked,
        this, &TcpServer::loadFromFile);
}
void TcpServer::loadFromFile()
{
    if (sharedMemory->isAttached()) {
        if (!sharedMemory->detach()) {
            QMessageBox::information(this, tr("错误"), tr("共享内存失败"), QMessageBox::Yes);
            return;
        }
    }
    ui->pic_label->setText(QObject::tr("Select an image file"));
    QString fileName = QFileDialog::getOpenFileName(this, "载入图片", "", "Images(*.png *.jpg)");
    QImage image;
    if (!image.load(fileName)) {
        ui->pic_label->setText(QObject::tr("Selected file is not an image, please select another."));
        return;
    }
    ui->pic_label->setPixmap(QPixmap::fromImage(image));

    QBuffer buffer; /* QBuffer用来暂存图片 */
    buffer.open(QBuffer::ReadWrite);
    QDataStream out(&buffer); /* QDataStream从数据流冲去读取buffer数据 */
    out << image;

    if (!sharedMemory->create(buffer.size())) { /* 自动将该内存段连接到本进程上 */
        QMessageBox::information(this, tr("创建共享内存失败"), tr(sharedMemory->errorString().toLatin1().data()), QMessageBox::Yes);
        return;
    }
    //将图像数据写入到共享内存之中
    sharedMemory->lock();
    char* to = (char*)sharedMemory->data(); /* 强制转换一个地址 */
    const char* from = buffer.data().data();
    memcpy(to, from, qMin(sharedMemory->size(), (int)buffer.size()));
    sharedMemory->unlock();
}

2客户端
2.1 TcpClient.cpp

#include "tcpclient.h"
#include "ui_tcpclient.h"
#include <QBuffer>
#include <QDataStream>
#include <QImage>
#include <QMessageBox>
#include <QObject>
#include <QPixmap>
#include <QPainter>

TcpClient::TcpClient(QWidget* parent)
    : QDialog(parent)
    , ui(new Ui::TcpClient)

{
    ui->setupUi(this);

    sharedMemory = new QSharedMemory("46");
    //loadFromMemory();

    status = false;
    port = 8010;
    ui->PortLineEdit->setText(QString::number(port));
    serverIP = new QHostAddress();
    connect(ui->EnterBtn, SIGNAL(clicked()), this, SLOT(slotEnter()));
    connect(ui->SendBtn, SIGNAL(clicked()), this, SLOT(slotSend()));
    //未进入聊天室内不能发送信息
    ui->SendBtn->setEnabled(false);
}

TcpClient::~TcpClient()
{
    delete ui;
    delete sharedMemory;
    delete serverIP;
    delete tcpSocket;
}

void TcpClient::slotEnter()
{
    if (!status) //(a)
    {
        /* 完成输入合法性检验 */
        QString ip = ui->IPLineEdit->text();
        if (!serverIP->setAddress(ip)) //(b)
        {
            QMessageBox::information(this, tr("error"), tr("server ip address error!"));
            return;
        }
        if (ui->UserNameLineEdit->text() == "") {
            QMessageBox::information(this, tr("error"), tr("User name error!"));
            return;
        }
        userName = ui->UserNameLineEdit->text();
        /* 创建了一个QTcpSocket类对象,并将信号/槽连接起来 */
        tcpSocket = new QTcpSocket(this);
        connect(tcpSocket, SIGNAL(connected()), this, SLOT(slotConnected()));
        connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived()));

        tcpSocket->connectToHost(*serverIP, port); 
        status = true;
    } else {
        QString msg = userName + tr(":Leave Chat Room"); 
        ui->listWidget->addItem(msg);
        
        tcpSocket->write(msg.toUtf8().data());
        tcpSocket->disconnectFromHost(); //(f)
        status = false; //将status状态复位

        QPixmap pixmap("/home/test/图片/00.png");
        QPalette palette;
        palette.setBrush(QPalette::Background, QPixmap(pixmap));
        this->setPalette(palette);

        connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(slotDisconnected()));
    }
}

void TcpClient::slotConnected()
{
    ui->SendBtn->setEnabled(true);
    ui->EnterBtn->setText(tr("leave"));
    QString msg = userName + tr(":Enter Chat Room");
   
    tcpSocket->write(msg.toUtf8().data());
    loadFromMemory();
}

void TcpClient::slotSend()
{
    if (ui->SendLineEdit->text() == "") {
        return;
    }
    QString msg = userName + ":" + ui->SendLineEdit->text();
    tcpSocket->write(msg.toUtf8().data());
    ui->SendLineEdit->clear();
}

void TcpClient::slotDisconnected()
{
    ui->SendBtn->setEnabled(false);
    ui->EnterBtn->setText("Enter Chat Room");

}

void TcpClient::dataReceived()
{
    QByteArray array = tcpSocket->readAll();
    ui->listWidget->addItem(array);
}

void TcpClient::loadFromMemory()
{
    // 将共享内存与该进程绑定
    if (!sharedMemory->attach()) {
        QMessageBox::information(this, tr("error"), tr("Unable to attach to shared memory segment."), QMessageBox::Yes);
        return;
    }

    QBuffer buffer;
    QDataStream in(&buffer);
    QImage image;

    sharedMemory->lock();
    buffer.setData((char*)sharedMemory->constData(), sharedMemory->size());
    buffer.open(QBuffer::ReadOnly);
    in >> image;
    
    sharedMemory->unlock();
    sharedMemory->detach();

    QPalette palette;
    palette.setBrush(QPalette::Background, QPixmap::fromImage(image));
    this->setPalette(palette);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值