功能:使用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);
}