TCP/IP是面向连接的,需要区分服务器和客户端;
服务器端 :
1.使用QTcpServer类
cpp
// 创建服务
int iServerPort = readIniFileByKay("TCP", "ServerPort").toInt();
if(iServerPort == 0){
iServerPort = 8080;
}
mTcpServer = new QTcpServer(this);
if (mTcpServer == NULL) {
outPutMsg(QtDebugMsg, "mTcpServer 服务器启动失败 mTcpServer=NULL");
return;
}
if (!mTcpServer->listen(QHostAddress::Any, iServerPort))
{
outPutMsg(QtDebugMsg, "mTcpServer 服务器启动失败");
}
else {
outPutMsg(QtDebugMsg, "mTcpServer 服务器启动成功");
}
connect(mTcpServer, &QTcpServer::newConnection, this, &CacheCar::newConnection_slot);
2.客户端连接服务器时会调用到槽函数newConnection_slot中:
cpp
// 客户端连接
void CacheCar::newConnection_slot() {
outPutMsg(QtDebugMsg, "newConnection_slot 有客户端申请连接");
//获取最新连接的客户端套接字
//[virtual] QTcpSocket *QTcpServer::nextPendingConnection()
QTcpSocket* s = mTcpServer->nextPendingConnection();
clientList.push_back(s); //将获取的套接字存放到客户端容器中 QList<QTcpSocket*> clientList;
connect(s, &QTcpSocket::readyRead, this, &CacheCar::readyRead_slot);
}
3.当建立连接的客户端发来信息时,会调用槽函数readyRead_slot
cpp
// 客户端发来的数据
void CacheCar::readyRead_slot() {
//删除客户端链表中的无效客户端套接字
for (int i = 0; i < clientList.count(); i++)
{
//返回值:客户端状态,如果是0,表示无连接
if (clientList[i]->state() == 0)
{
clientList.removeAt(i); //将下标为i的客户端移除
}
}
//遍历所有客户端,查看是哪个客户端发来数据
for (int i = 0; i < clientList.count(); i++)
{
if (clientList[i]->bytesAvailable() != 0)
{
QByteArray msg = clientList[i]->readAll();
QString msgStr = QString::fromUtf8(msg);
outPutMsg(QtDebugMsg, "CacheCar::readyRead_slot msgStr: " + msgStr);
QStringList list = msgStr.split("||");
if (list.size()<2) {
return;
}
QString barCode = list[1];
outPutMsg(QtDebugMsg, "CacheCar::readyRead_slot barCode: " + barCode);
if (barCode.size() < 8) {
return;
}
else {
int index = list[0].toInt();
// 1.收到扫码信息,向服务端发起请求,同步等用来获取窗户信息 2.组装测量请求,并发送给PLC
if (!getHttpWindowInfoRequest(index, barCode, index)) {
outPutMsg(QtDebugMsg, "CacheCar::readyRead_slot 提交窗户入库请求失败 barCode: " + barCode);
}
}
}
}
}
客户端:
1.创建连接
cpp
// PLC
mSocketPlc = new QTcpSocket(this);
if (mSocketPlc == NULL) {
return;
}
QString plcIP = readIniFileByKay("TCP", "PLCIP");
QString plcPortStr = readIniFileByKay("TCP", "PLCPort");
int plcPort = plcPortStr.toInt();
mSocketPlc->connectToHost(plcIP, plcPort);
connect(mSocketPlc, &QTcpSocket::errorOccurred, this, &CacheCar::onTcpError_plc);
connect(mSocketPlc, &QTcpSocket::connected, this, &CacheCar::onTcpConnected_plc);
connect(mSocketPlc, &QTcpSocket::disconnected, this, &CacheCar::onTcpDisconnected_plc);
connect(mSocketPlc, &QTcpSocket::readyRead, this, &CacheCar::onReadData_plc);
2.连接报错或断开连接后,自动发起重连
cpp
// PLC服务
void CacheCar::onTcpError_plc(QAbstractSocket::SocketError socketError) {
outPutMsg(QtDebugMsg, "CacheCar::onTcpError_plc Connected to host ");
outPutMsg(QtDebugMsg, "ERROR: "+ mSocketPlc->errorString());
if (socketError == QAbstractSocket::HostNotFoundError || socketError == QAbstractSocket::ConnectionRefusedError) {
QString plcIP = readIniFileByKay("TCP", "PLCIP");
int plcPort = readIniFileByKay("TCP", "PLCPort").toInt();
outPutMsg(QtDebugMsg, "Trying to reconnect...IP:" + plcIP + " port:"+ QString::number(plcPort));
mSocketPlc->connectToHost(plcIP, plcPort);
}
}
void CacheCar::onTcpConnected_plc() {
outPutMsg(QtDebugMsg, "CacheCar::onTcpConnected_plc Connected to host ");
}
void CacheCar::onTcpDisconnected_plc() {
outPutMsg(QtDebugMsg, "CacheCar::onTcpDisconnected_plc Disconnected from host ");
QString plcIP = readIniFileByKay("TCP", "PLCIP");
int plcPort = readIniFileByKay("TCP", "PLCPort").toInt();
outPutMsg(QtDebugMsg, "Trying to reconnect...IP:" + plcIP + " port:" + QString::number(plcPort));
mSocketPlc->connectToHost(plcIP, plcPort);
}
3.服务器发的信息调用onReadData_plc槽函数
cpp
// 读取信息的槽函数
void CacheCar::onReadData_plc() {
if (mSocketPlc == NULL) {
return;
}
if (mSocketPlc->bytesAvailable() > 0) {
QByteArray data = mSocketPlc->readAll(); // 读取所有可用数据
QString messageStr = QString::fromUtf8(data);
outPutMsg(QtDebugMsg, "CacheCar::onReadData_plc messageStr: " + messageStr);
if (messageStr.size() < 8) {
return;
}
else {
// 解析响应,区分是测量返回、还是收到任务的响应、还是完成任务的响应
QStringList msgList = messageStr.split(",");
if (msgList.size() < 2) {
return;
}
int funcId = msgList[0].toInt();
if (funcId == 101) { // 测宽高
if (msgList.size() < 5) {
return;
}
QString barcodeRev = msgList[1];
for (int i = 0; i < windowList.size(); i++) {
if (windowList[i].barCode.compare(barcodeRev) != 0) {
continue;
}
// 处理该信息
windowList[i].boxW = msgList[3].toDouble()/1000.0;
windowList[i].boxH = msgList[4].toDouble()/1000.0;
if(true/*校验窗户尺寸合适*/) {
windowList[i].flag = 1;
}
else {
// 调用服务器接口报错
QString msgStr = getLanguageStr("窗户信息不一致,无法入库",
"The window information is inconsistent and cannot be stored in the warehouse",
"Niespójne informacje o oknie, nie można go zapisać");
errorFeedBack(windowList[i].channelId, msgStr,1);
}
break;
}
QString boxWStr = "", boxHStr = "";
boxWStr = QString::number(msgList[3].toDouble()/1000.0);
boxHStr = QString::number(msgList[4].toDouble()/1000.0);
int channelNo = msgList[2].toInt();
if (channelNo == 1) {
ui.auto_lineEdit_channelW_1->setText(boxWStr);
ui.auto_lineEdit_channelH_1->setText(boxHStr);
setLableBackGroundColor(ui.auto_label_status2_channel_1, "测量完成", GREEN);
} else if (channelNo == 2) {
ui.auto_lineEdit_channelW_2->setText(boxWStr);
ui.auto_lineEdit_channelH_2->setText(boxHStr);
setLableBackGroundColor(ui.auto_label_status2_channel_2, "测量完成", GREEN);
}else if (channelNo == 3) {
ui.auto_lineEdit_channelW_3->setText(boxWStr);
ui.auto_lineEdit_channelH_3->setText(boxHStr);
setLableBackGroundColor(ui.auto_label_status2_channel_3, "测量完成", GREEN);
}
else if (channelNo == 4) {
ui.auto_lineEdit_channelW_4->setText(boxWStr);
ui.auto_lineEdit_channelH_4->setText(boxHStr);
setLableBackGroundColor(ui.auto_label_status2_channel_4, "测量完成", GREEN);
}
setChancelStatus(channelNo, getLanguageStr("测量完成", "Complete", "Zakończone"), GREEN);
outPutMsg(QtDebugMsg, "CacheCar::onReadData_plc 测宽高: " + msgList[1]);
} else if (funcId == 102) { // 入库出库任务返回
for (int i = 0; i < windowList.size(); i++) {
if (windowList[i].commandId == msgList[1].toInt()) {
windowList.removeAt(i--); // 任务已下发给PLC移除
break;
}
}
updateWaitTaskList(); // 更新任务
outPutMsg(QtDebugMsg, "CacheCar::onReadData_plc 入库出库任务返回: " + msgList[1]);
}
else if (funcId == 103) { // 入库出库完成返回
endFeedBack(msgList[1].toInt());
outPutMsg(QtDebugMsg, "CacheCar::onReadData_plc 入库出库任务返回: " + msgList[1]);
}
}
}
}