[Qt]tcp服务器连接多个客户端的实现
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
要求:数据按字节接收,以1~255个字节循环发送
编译环境:Qt 5.9.5
客户端的实现:
代码如下:
TcpClient.h:
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <QWidget>
#include <QTcpSocket>
#include <iostream>
#include <string>
using namespace std;
namespace Ui {
class TcpClient;
}
class TcpClient : public QWidget
{
Q_OBJECT
public:
explicit TcpClient(QWidget *parent = 0);
~TcpClient();
private slots:
void on_pushButtonEnter_clicked();
void slotconnectedsuccess(); //处理连接成功的信号
void slotreceive(); //接收服务器传过来的信息
void on_pushButtonSend_clicked();
void slotdisconnected(); //处理离开聊天室的信号
private:
Ui::TcpClient *ui;
bool status; //用来判断是否进入了服务器
size_t strSize;
int port;
QHostAddress *serverIP;
QString userName;
QTcpSocket *tcpsocket;
};
#endif // TCPCLIENT_H
TcpClient.cpp:
#include "tcpclient.h"
#include "ui_tcpclient.h"
#include <QHostAddress>
#include <QMessageBox>
TcpClient::TcpClient(QWidget *parent) :
QWidget(parent),
ui(new Ui::TcpClient)
{
ui->setupUi(this);
//将进入服务器的标志位置为false
status = false;
port = 8888;
ui->lineEditServerPort->setText(QString::number(port));
serverIP = new QHostAddress();
//未连接内不能发送信息
ui->pushButtonSend->setEnabled(false);
}
TcpClient::~TcpClient()
{
delete ui;
}
//进入服务器
void TcpClient::on_pushButtonEnter_clicked()
{
//首先判断这个用户是不是在服务器中
if(status == false) { //不在服务器中就和服务器进行连接
QString ip = ui->lineEditServerIp->text();
if(!serverIP->setAddress(ip)) { //判断IP地址是否可以被正确解析
QMessageBox::warning(this, "错误", "IP地址不正确");
return;
}
if(ui->lineEditUserName->text() == "") {
QMessageBox::warning(this, "错误", "用户名不能为空");
return;
}
userName = ui->lineEditUserName->text();
tcpsocket = new QTcpSocket(this);
tcpsocket->connectToHost(*serverIP, port);
//和服务器连接成功能会触发connected信号
connect(tcpsocket, &QTcpSocket::connected, this, &TcpClient::slotconnectedsuccess);
//接收到服务器的信息就会触发readyRead信号
connect(tcpsocket, &QTcpSocket::readyRead, this, &TcpClient::slotreceive);
//将进入服务器的标志位置为true;
status = true;
}
else { //已经进入了服务器
//int length = 0;
QString msg = userName + ":disconnected";
tcpsocket->write(msg.toUtf8().data());
tcpsocket->disconnectFromHost();
status = false;
//离开服务器就会触发disconnected信号
connect(tcpsocket, &QTcpSocket::disconnected, this, &TcpClient::slotdisconnected);
}
}
//用来处理连接成功的信号
void TcpClient::slotconnectedsuccess()
{
ui->textEdit->append("连接成功");
//进入服务器可以发送信息了
ui->pushButtonSend->setEnabled(true);
//将连接服务器的按钮改为离开服务器
ui->pushButtonEnter->setText("离开服务器");
QString msg = userName + ": connected";
tcpsocket->write(msg.toUtf8().data());
}
void TcpClient::slotreceive()
{
QByteArray array = tcpsocket->readAll();
ui->textEdit->append(array.data());
}
void TcpClient::on_pushButtonSend_clicked()
{
if(ui->lineEditSend->text() == "") {
return;
}
//获取编辑区内容
QString str = ui->lineEditSend->text();
//加头信息
qint64 textSize = str.size();
//userName = userName + ": ";
QString strHead = QString("%1##%2##%3").arg(userName + ": ").arg(textSize).arg(str);
tcpsocket->write(strHead.toUtf8().data());
ui->lineEditSend->clear();
}
void TcpClient::slotdisconnected()
{
ui->pushButtonSend->setEnabled(false);
ui->pushButtonEnter->setText("连接服务器");
}
服务端的实现:
tcpserver.h:
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QWidget>
#include "server.h"
namespace Ui {
class TcpServer;
}
class TcpServer : public QWidget
{
Q_OBJECT
public:
explicit TcpServer(QWidget *parent = 0);
~TcpServer();
protected slots:
void slotupdateserver(QString, int); //接收server发过来的信号就更新界面信息
private slots:
void on_Button_waitConnect_clicked();
private:
Ui::TcpServer *ui;
int port;
Server *server;
};
#endif // TCPSERVER_H
tcpserver.cpp:
#include "tcpserver.h"
#include "server.h"
#include "ui_tcpserver.h"
#include <QDebug>
TcpServer::TcpServer(QWidget *parent) :
QWidget(parent),
ui(new Ui::TcpServer)
{
ui->setupUi(this);
port = 8888;
}
TcpServer::~TcpServer()
{
delete ui;
}
void TcpServer::on_Button_waitConnect_clicked()
{
server = new Server(this, port);
connect(server, &Server::updateserver, this, &TcpServer::slotupdateserver);
ui->textEdit->append("创建成功,可以连接");
ui->Button_waitConnect->setEnabled(false);
}
void TcpServer::slotupdateserver(QString msg, int length)
{
ui->textEdit->append(msg);
}
重写incomingconnected() , 监听
server.h
#ifndef SERVER_H
#define SERVER_H
#include <QTcpServer>
#include <QObject>
#include <QList>
#include "tcpclientsocket.h"
class Server : public QTcpServer
{
Q_OBJECT
public:
explicit Server(QObject *parent = 0, int port = 0);
QList<TcpClientSocket*> tcpclientsocketlist;
protected:
virtual void incomingConnection(qintptr socketDescriptor); //只要出现一个新的连接,就会自动调用这个函数
protected slots:
void slotupdateserver(QString, int); //用来处理tcpclient发过来的信号
void slotclientdisconnect(int);
signals:
void updateserver(QString, int); //发送信号给界面, 让界面更新信息
};
#endif // SERVER_H
server.cpp:
#include "server.h"
#include "tcpclientsocket.h"
#include <QHostAddress>
#include <QDebug>
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);
//将新创建的套接字加入到客户端套接字列表中
tcpclientsocketlist.append(tcpclientsocket);
//接收到tcpclientsocket发过来的更新界面的信号
connect(tcpclientsocket, &TcpClientSocket::updateserver, this, &Server::slotupdateserver);
connect(tcpclientsocket, &TcpClientSocket::clientDisconnected, this, &Server::slotclientdisconnect);
//return emit QTcpServer::newConnection();
}
void Server::slotupdateserver(QString msg, int length)
{
//将这个信号发送给界面
emit updateserver(msg, length);
QString userName = msg.section("##", 0, 0);
QString text = msg.section("##", 1, 1);
//将收到的信号发送给每个客户端,从套接字列表中找到需要接收的套接字
for(int i = 0; i < tcpclientsocketlist.count(); i++) {
QTcpSocket *item = tcpclientsocketlist.at(i);
QString sendInfo = userName + " ";
item->write(sendInfo.toUtf8().data());
//处理数据,按格式发送
QString str = text;
string buf = str.toLatin1().data();
string sendText; //存放要发送的数据
size_t j = 0;
while(j < buf.size()) {
for(size_t i = 1; i <= 11; i++) {
if((j + i) <= buf.size()) {
if(i == 11) {
i = 1;
}
sendText = buf.substr(j, i);
//发送数据
sendText = sendText + "~~~";
//tcpsocket->write(sendText.data());
item->write(sendText.data());
}
else {
sendText = buf.substr(j, (buf.size() - j));
//发送数据
sendText = sendText + "~~~";
item->write(sendText.data());
break;
}
j += i;
// if(i == 255) {
// i = 1;
// }
sendText.clear();
}
break;
}
}
}
void Server::slotclientdisconnect(int descriptor)
{
for(int i = 0; i < tcpclientsocketlist.count(); i++) {
QTcpSocket *item = tcpclientsocketlist.at(i);
//如果有客户端断开连接,将列表中的套接字删除
if(item->socketDescriptor() == descriptor) {
tcpclientsocketlist.removeAt(i);
return;
}
}
return;
}
通信
tcpsocket.h:
#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H
#include <QTcpSocket>
#include <iostream>
#include <string>
using namespace std;
class TcpClientSocket : public QTcpSocket
{
Q_OBJECT
public:
explicit TcpClientSocket(QObject *parent = nullptr);
protected slots:
void recvdata(); //处理readyRead信号读取数据
void slotclientdisconneceted(); //客户端断开触发disconnected,处理这个信号
signals:
void updateserver(QString, int); //用来告诉tcpserver需要更新界面
void clientDisconnected(int); //告诉server有客户端断开
public slots:
private:
size_t strSize;
};
#endif // TCPCLIENTSOCKET_H
tcpsocket.cpp:
#include "tcpclientsocket.h"
#include <QDebug>
TcpClientSocket::TcpClientSocket(QObject *parent) : QTcpSocket(parent)
{
//客户端发过来的数据触发readyRead信号
connect(this, &TcpClientSocket::readyRead, this, &TcpClientSocket::recvdata);
connect(this, &TcpClientSocket::disconnected, this, &TcpClientSocket::slotclientdisconneceted);
}
void TcpClientSocket::recvdata()
{
QByteArray array = readAll();
int length = array.size();
QString userName = QString(array).section("##", 0, 0);
strSize = QString(array).section("##", 1, 1).toInt();
//正文内容
string strText = QString(array).section("##", 2, 2).toLatin1().data();
string buf;
for(size_t i = 0; i < strText.size(); i++) {
buf.append(strText.substr(i, 1));
}
if(strSize != buf.size()) {
qDebug() << "读取出错";
return;
}
if(buf.size() == 0) {
QString msg = userName;
emit updateserver(msg, length);
}
else {
QString msg = userName + "##" + buf.data();
emit updateserver(msg, length);
}
}
void TcpClientSocket::slotclientdisconneceted()
{
emit clientDisconnected(this->socketDescriptor());
}
界面布局