1、在Qt项目中的.pro中添加 network模块,用于Socket网络编程使用
QT += network
2、相关Tcp网络通信协议头文件
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QHostAddress>
3、Qt socket运行实例。关于IP地址和Port端口号的设置:服务器代码设置的是服务器IP地址和Port端口号,客户端设置的是需要连接的服务器地址和端口号。所以不管是服务器还是客户端都设置的是服务器地址和端口号。
服务器端widget.h代码:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QTableView>
#include <QStandardItemModel>
#include <QHeaderView>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
// 初始化server操作
void initServer();
// close server
void closeServer();
// 更新当前状态
void updateState();
// 设置数据表格tableView
void setTableView();
private:
Ui::Widget *ui;
//server用于监听端口,获取新的tcp连接的描述符
QTcpServer *server;
//存储已连接的socket对象
QList<QTcpSocket*> clientList;
// 设置表格模式
QStandardItemModel *model;
};
#endif // WIDGET_H
服务器端widget.cpp代码:
#include "widget.h"
#include "ui_widget.h"
#include<QtNetwork/QHostAddress>
#include <QMessageBox>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置tableList
setTableView();
setWindowTitle("Server");
initServer();
ui->btnListen->setStyleSheet("color: rgb(0, 100, 100); background-color: rgb(255, 100, 50);");
}
Widget::~Widget()
{
// 关闭server
closeServer();
delete ui;
}
void Widget::initServer()
{
// 创建Server对象
server = new QTcpServer(this);
//点击监听按钮,开始监听
connect(ui->btnListen,&QPushButton::clicked,[this]{
//判断当前是否已开启,是则close,否则listen
if(server->isListening()){
//server->close();
closeServer();
//关闭server后恢复界面状态
ui->btnListen->setText("Listen");
ui->btnListen->setStyleSheet("color: rgb(0, 100, 100); background-color: rgb(255, 100, 50);");
ui->editAddress->setEnabled(true);
ui->editPort->setEnabled(true);
}
else
{
//从界面上读取ip和端口
//可以使用 QHostAddress::Any 监听所有地址的对应端口
const QString address_text=ui->editAddress->text();
const QHostAddress address=(address_text=="Any")
?QHostAddress::Any
:QHostAddress(address_text);
const unsigned short port=ui->editPort->text().toUShort();
//开始监听,并判断是否成功
if(server->listen(address,port)){
//连接成功就修改界面按钮提示,以及地址栏不可编辑
ui->btnListen->setText("Close");
ui->btnListen->setStyleSheet("color: rgb(100, 100, 100); background-color: rgb(0, 255, 0);");
ui->editAddress->setEnabled(false);
ui->editPort->setEnabled(false);
}
else
QMessageBox::information(NULL, "提示", "服务器ip设置错误", QMessageBox::Yes);
}
updateState();
});
//监听到新的客户端连接请求
connect(server,&QTcpServer::newConnection,this,[this]{
//如果有新的连接就取出
while(server->hasPendingConnections())
{
//nextPendingConnection返回下一个挂起的连接作为已连接的QTcpSocket对象
//套接字是作为服务器的子级创建的,这意味着销毁QTcpServer对象时会自动删除该套接字。
//最好在完成处理后显式删除该对象,以避免浪费内存。
//返回的QTcpSocket对象不能从另一个线程使用,如有需要可重写incomingConnection().
QTcpSocket *socket=server->nextPendingConnection();
clientList.append(socket);
ui->textRecv->setTextColor(QColor(0,200,0));
// ui->textRecv->clear();
ui->textRecv->append(QString("[%1:%2] Soket Connected")
.arg(socket->peerAddress().toString())
.arg(socket->peerPort()));
//关联相关操作的信号槽
//收到数据,触发readyRead
connect(socket,&QTcpSocket::readyRead,[this,socket]{
//没有可读的数据就返回
if(socket->bytesAvailable()<=0)
return;
//注意收发两端文本要使用对应的编解码
QByteArray data = socket->readAll();
QStandardItem *item;
quint16 comb;
// 棉结数值
comb = (uchar)data[10]<<8 | (uchar)data[11];
item = new QStandardItem(QString::number(comb));
model->setItem(0, 1, item);
// 短粗1
comb = (uchar)data[12]<<8 | (uchar)data[13];
item = new QStandardItem(QString::number(comb));
model->setItem(1, 1, item);
// 短粗2
comb = (uchar)data[14]<<8 | (uchar)data[15];
item = new QStandardItem(QString::number(comb));
model->setItem(2, 1, item);
// 长粗
comb = (uchar)data[16]<<8 | (uchar)data[17];
item = new QStandardItem(QString::number(comb));
model->setItem(3, 1, item);
// 细节1
comb = (uchar)data[18]<<8 | (uchar)data[19];
item = new QStandardItem(QString::number(comb));
model->setItem(0, 3, item);
// 细节2
comb = (uchar)data[20]<<8 | (uchar)data[21];
item = new QStandardItem(QString::number(comb));
model->setItem(1, 3, item);
// 特殊纱疵
comb = (uchar)data[22]<<8 | (uchar)data[23];
item = new QStandardItem(QString::number(comb));
model->setItem(2, 3, item);
// 纱支变异
comb = (uchar)data[24]<<8 | (uchar)data[25];
item = new QStandardItem(QString::number(comb));
model->setItem(3, 3, item);
// 纱疵总数
comb = (uchar)data[26]<<8 | (uchar)data[27];
item = new QStandardItem(QString::number(comb));
model->setItem(0, 5, item);
// 十万米纱疵数
comb = (uchar)data[28]<<8 | (uchar)data[29];
item = new QStandardItem(QString::number(comb*0.1,'f',1));
model->setItem(1, 5, item);
// 产量
quint16 comb2;
quint32 comb12;
comb = (uchar)data[30]<<8 | (uchar)data[31];
comb2 = (uchar)data[32]<<8 | (uchar)data[33];
comb12 = comb*65536 + comb2;
item = new QStandardItem(QString::number(comb12*0.0001,'f',4));
model->setItem(2, 5, item);
// 效率
comb = (uchar)data[34]<<8 | (uchar)data[35];
item = new QStandardItem(QString::number(comb*0.01,'f',2));
model->setItem(3, 5, item);
// 驱动单元连接个数
comb = (uchar)data[36]<<8 | (uchar)data[37];
ui->driveEdit->setText(QString::number(comb));
// 处理单元连接个数
comb = (uchar)data[38]<<8 | (uchar)data[39];
ui->processEdit->setText(QString::number(comb));
/*
const QString recv_text=QString::fromUtf8(socket->readAll()); // 读取客户端发送的内容
ui->textRecv->clear();
ui->textRecv->append(QString("[%1:%2]") // 在textEdit写入客户端ip和port
.arg(socket->peerAddress().toString())
.arg(socket->peerPort()));
*/
// ui->textRecv->append(recv_text); // 在textEdit吸入客户端发送内容
});
//错误信息
connect(socket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
[this,socket](QAbstractSocket::SocketError)
{
// ui->textRecv->clear();
ui->textRecv->append(QString("[%1:%2] Soket Error:%3")
.arg(socket->peerAddress().toString())
.arg(socket->peerPort())
.arg(socket->errorString()));
});
//连接断开,销毁socket对象,这是为了开关server时socket正确释放
connect(socket,&QTcpSocket::disconnected,[this,socket]{
socket->deleteLater();
clientList.removeOne(socket);
ui->textRecv->setTextColor(QColor(200,0,0));
// ui->textRecv->clear();
ui->textRecv->append(QString("[%1:%2] Soket Disonnected")
.arg(socket->peerAddress().toString())
.arg(socket->peerPort()));
updateState();
});
}
updateState();
});
}
void Widget::closeServer()
{
//停止服务
server->close();
for(QTcpSocket * socket:clientList)
{
//断开与客户端的连接
socket->disconnectFromHost();
if(socket->state()!=QAbstractSocket::UnconnectedState){
socket->abort();
}
}
}
void Widget::updateState()
{
//将当前server地址和端口、客户端连接数写在标题栏
if(server->isListening())
{
setWindowTitle(QString("Server[%1:%2] connections:%3")
.arg(server->serverAddress().toString())
.arg(server->serverPort())
.arg(clientList.count()));
}
else
{
setWindowTitle("Server");
}
}
void Widget::setTableView()
{
// 创建模型 - 5行6列
model = new QStandardItemModel(4,6);
// 设置表头
model->setHorizontalHeaderLabels({"类型","个数", "类型","个数","统计","Value"});
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); // tableView只读
ui->tableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(150,150,150);}"); // tableView设置head颜色
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); // tableView自适应
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 填充数据
QStandardItem *item;
item = new QStandardItem(QString("棉结"));
model->setItem(0, 0, item);
item = new QStandardItem(QString("短粗1"));
model->setItem(1, 0, item);
item = new QStandardItem(QString("短粗2"));
model->setItem(2, 0, item);
item = new QStandardItem(QString("长粗"));
model->setItem(3, 0, item);
item = new QStandardItem(QString("细节1"));
model->setItem(0, 2, item);
item = new QStandardItem(QString("细节2"));
model->setItem(1, 2, item);
item = new QStandardItem(QString("特殊纱疵"));
model->setItem(2, 2, item);
item = new QStandardItem(QString("纱支变异"));
model->setItem(3, 2, item);
item = new QStandardItem(QString("纱疵总数"));
model->setItem(0, 4, item);
item = new QStandardItem(QString("纱疵数[十万米]"));
model->setItem(1, 4, item);
item = new QStandardItem(QString("产量[千克]"));
model->setItem(2, 4, item);
item = new QStandardItem(QString("效率(%)"));
model->setItem(3, 4, item);
ui->tableView->verticalHeader()->setVisible(false);
ui->tableView->setModel(model);
}
客户端用NetAssist.exe工具即可,设置如下:
1)选择协议类型:Tcp Client
2)远程主机地址:服务器地址(我的本机地址:192.168.0.187)
3)远程主机端口:服务器端口号(我的本机端口号:8080)