目录
一、一服务端一客户端示例
以下为服务端代码,客户端采用NetAssist网络调试助手调试。
1、**.ui
添加textEdit窗体
2、**.h文件
#include <QTcpServer>
#include <QTcpSocket>
...
private:
Ui::MainWindow *ui;
QTcpServer *tcpServer; //监听套接字
QTcpSocket *tcpSocket; //通信套接字
3、**.cpp文件
#include <QTcpServer>//监听套接字
#include <QTcpSocket>//通信套接字
...
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpServer = NULL;
tcpSocket = NULL;
...
//TCP通信服务端代码
//TCP-监听
tcpServer = new QTcpServer(this);
tcpSocket = new QTcpSocket(this);
tcpServer->listen(QHostAddress::Any, 9999);
//newConnection代表有新的连接
connect(tcpServer, &QTcpServer::newConnection,
[=]()
{
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
//获取对方的IP和端口
QString ip = tcpSocket->peerAddress().toString();
quint16 port = tcpSocket->peerPort();
QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);
qDebug() << temp << endl;
ui->textEditRead->setText(temp);//不用settext 这样会覆盖之前的消息
//接收信息 必须放到连接中的槽函数 不然tcpsocket就是一个野指针
//connect(tcpSocket, &QTcpSocket::readyRead, this, &MainWindow::ReadData);
connect(tcpSocket, &QTcpSocket::readyRead,
[=]()
{
//从通信套接字中取出内容
QString str = tcpSocket->readAll();
qDebug() << str << endl;
//在编辑区域显示
ui->textEditRead->append("客户端:" + str);//不用settext 这样会覆盖之前的消息
Sleep(1000);
if (str == "Volume") {
QString l = ui->lineEdit_ObjectLength->text().toUtf8();
QString w = ui->lineEdit_ObjectWidth->text().toUtf8();
QString h = ui->lineEdit_ObjectHeight->text().toUtf8();
QString v = ui->lineEdit_ObjectIntegralVolume->text().toUtf8();
QString data = "Length:" + l + "," + "Width:" + w + "," + "Height:" + h + "," + "Volume:" + v;
// 定义 { } 对象
QJsonObject interestObj;
// 插入元素,对应键值对
interestObj.insert("Length", l);
interestObj.insert("Width", w);
interestObj.insert("Height", h);
interestObj.insert("Volume", v);
//tcpSocket->write(data.toUtf8());
QJsonDocument document = QJsonDocument(interestObj);
QByteArray array = document.toJson();// 转换成QByteArray
tcpSocket->write(array);
}
});
});
4、测试
上述示例存在一个问题,当多客户端连接时,只要有其中一个发送请求,所有客户端都会收到响应数据。
二、一服务端多客户端示例
服务端代码:
**.h文件
int m_iClientCount;
QList<QTcpSocket*> listClient;
QTcpServer *tcpServer; //监听套接字
QTcpSocket *tcpSocket; //通信套接字
**.cpp文件
//TCP-监听
tcpServer = new QTcpServer(this);
//tcpSocket = new QTcpSocket(this);
tcpServer->listen(QHostAddress::Any, 9999);
//newConnection代表有新的连接
connect(tcpServer, &QTcpServer::newConnection,
[=]()
{
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
listClient.append(tcpSocket);//将生成的socket添加到容器里
m_iClientCount++;
//获取对方的IP和端口
QString ip = tcpSocket->peerAddress().toString();
quint16 port = tcpSocket->peerPort();
QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);
qDebug() << temp << endl;
ui->textEditRead->append(temp);//不用settext 这样会覆盖之前的消息
//接收信息 必须放到连接中的槽函数 不然tcpsocket就是一个野指针
//connect(tcpSocket, &QTcpSocket::readyRead, this, &MainWindow::ReadData);
connect(tcpSocket, &QTcpSocket::readyRead,
[=]()
{
QByteArray buffer;
//利用for循环循环列表中的每一个连接进来的客户端,判断是哪一个客户端发的数据
for (int i = 0; i < listClient.count(); i++)
{
tcpSocket = listClient.at(i);
buffer = tcpSocket->readAll();
if (buffer.length() != 0) //如果检测到接收到的数据长度不为0,则代表是这个客户端发送的数据
ui->textEditRead->append("客户端:" + buffer);//不用settext 这样会覆盖之前的消息
if (buffer == "Volume") {
QString l = ui->lineEdit_ObjectLength->text().toUtf8();
QString w = ui->lineEdit_ObjectWidth->text().toUtf8();
QString h = ui->lineEdit_ObjectHeight->text().toUtf8();
QString ev = ui->lineEdit_ObjectExternalVolume->text().toUtf8();
QString iv = ui->lineEdit_ObjectIntegralVolume->text().toUtf8();
//QString data = "Length:" + l + "," + "Width:" + w + "," + "Height:" + h + "," + "Volume:" + v;
// 定义 { } 对象
QJsonObject interestObj;
// 插入元素,对应键值对
interestObj.insert("Length", l);
interestObj.insert("Width", w);
interestObj.insert("Height", h);
interestObj.insert("ExternalVolume", ev);
interestObj.insert("IntegralVolume", iv);
//tcpSocket->write(data.toUtf8());
QJsonDocument document = QJsonDocument(interestObj);
QByteArray array = document.toJson();// 转换成QByteArray
tcpSocket->write(array);
}
}
});
});
断开连接槽函数:
connect(tcpSocket_new, &QTcpSocket::disconnected, [=]()
{
//tcpSocket->disconnectFromHost();//主动和客户端断开连接
for (int i = 0; i < listClient.count(); i++)
{
QTcpSocket* tcpSocket1 = listClient.at(i);
if (tcpSocket1 == tcpSocket_new)
{
qDebug() << tcpSocket_new << "disconnected!";
listClient.removeOne(tcpSocket_new);
//删除套接字
//tcpSocket_new->deleteLater();
}
}
});
连接状态:
if (tcpSocket_new->state() == QAbstractSocket::UnconnectedState)
{
return;
}
三、问题解决
TCP通信接收数据不全,存在丢包现象,因为网络数据的传输是分包进行的,而readAll()只会读取当前可用的数据,并不保证一次性读取完整。
解决方案:
while (tcpSocket->bytesAvailable() > 0)
{
NewDelayTime(2000);
QByteArray buffer = tcpSocket->readAll();
bufferall.append(buffer);
qDebug() << bufferall;
if (bufferall.startsWith("{") && bufferall.endsWith("}"))
{
//处理bufferall数据
}
}
//延时
void MainWindow::NewDelayTime(DWORD times)
{
DWORD dwStart = GetTickCount();
DWORD dwEnd = dwStart;
do
{
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
dwEnd = GetTickCount();
} while ((dwEnd - dwStart) <= times);
}