Qt中udp和tcp收发方式略有不同。
//定义报文结构体
//通用报文头格式,所有报文均包含该头部
struct VTM_CommonHeader{
unsigned int uType; //业务类型
unsigned int uSize;//报文大小
};
struct VTM_MSCALL_REQUEST{
VTM_CommonHeader head;
unsigned int userNumber;//用户号
char ip[32];//营业员ip地址
VTM_MSCALL_REQUEST()
{
memset(this,0,sizeof(VTM_MSCALL_REQUEST));
}
};
//组包
QString ip = "127.0.0.1";
DATASTRUCT::VTM_MSCALL_REQUEST tVTM_CALL_REQUEST;
tVTM_CALL_REQUEST.head.uType = 1001;
tVTM_CALL_REQUEST.head.uSize = sizeof (DATASTRUCT::VTM_MSCALL_REQUEST);
tVTM_CALL_REQUEST.userNumber = 10086;
memcpy(tVTM_CALL_REQUEST.ip,ip.toLatin1().data(),sizeof(tVTM_CALL_REQUEST.ip));
//tcp发送
QTcpSocket *m_pTcpSocket;
m_pTcpSocket = new QTcpSocket(this);
m_pTcpSocket->connectToHost(QHostAddress("127.0.0.1"),9000);
m_pTcpSocket->waitForConnected(3000);
m_pTcpSocket->write(QByteArray((char*)&tVTM_CALL_REQUEST,sizeof (DATASTRUCT::VTM_MSCALL_REQUEST)));//发送数据
m_pTcpSocket->waitForBytesWritten();
//udp发送
QUdpSocket *pSendUdp;
QString mdfIP = "127.0.0.1";//对端IP
pSendUdp = new QUdpSocket(this);
pSendUdp->writeDatagram(QByteArray((char*)&tVTM_CALL_REQUEST,sizeof (DATASTRUCT::VTM_MSCALL_REQUEST)),QHostAddress(mdfIP),9000);
pSendUdp->waitForBytesWritten();
QUdpSocket不能使用readAll,否则会报错QIODevice::read (QUdpSocket): device not open;
通过readyRead信号触发槽函数读取数据。
//tcp接收
QTcpSocket *m_pTcpSocket;
m_pTcpSocket = new QTcpSocket(this);
//如果是tcp客户端要connectToHost,如果是tcp服务端则要赋值与QTcpServer连接的QTcpSocket
QByteArray array = m_pTcpSocket->readAll();
//udp接收
QUdpSocket *pRevUdp;
pRevUdp = new QUdpSocket(this);
pRevUdp->bind(QHostAddress::Any,9000);
while (pRevUdp->hasPendingDatagrams())
{
QByteArray mBuffer;
qint64 len = pRevUdp->pendingDatagramSize();
QHostAddress sender;
quint16 senderPort;
qint64 succLen = pRevUdp->readDatagram(mBuffer.data(),len,&sender, &senderPort);
}
对接收到数据QByteArray转换为结构体变量
DATASTRUCT::VTM_MSCALL_REQUEST tVTM_MSCALL_REQUEST;
memset(&tVTM_MSCALL_REQUEST,0,sizeof (DATASTRUCT::VTM_MSCALL_REQUEST));
memcpy(&tVTM_MSCALL_REQUEST,array.data(),sizeof (DATASTRUCT::VTM_MSCALL_REQUEST));
报文16进制字串每两位加空格
QString Instance::HexToHuman(QString str)
{
QString strAfter;
int flag=0;
for(int index=0;index<str.size();index++)
{
strAfter.append(str.at(index));
flag++;
if(flag == 2)
{
flag=0;
strAfter.append(" ");
}
}
return strAfter;
}
有时我们会把报文写入日志或打印出来,下面介绍如何解析打印出来的原始报文:
QByteArray array
qDebug()<< array.toHex()
上面的报文结构体VTM_MSCALL_REQUEST长度是44个字节,打印出来的是16进制字串,每两位代表一个16进制字符,因此打印出来是88位,比如:
e90300002c000000662700003132372e302e302e31000000ff000000a1bf55da008100807056a70200000000
报文开头是unsigned int uType,4个字节,占8位,也就是e9030000,每两位代表1个字节,这里注意报文默认是高字节在前,低字节在后,转换后是000003e9,转换位十进制就是1001;
紧接着后面8位是2c000000,转换后是0000002c,转换十进制是44,unsigned int uSize;//报文大小;
再后面8位是66270000,转换后是00002766,转换十进制是10086,是unsigned int userNumber;//用户号;
最后64位是char ip[32],对应32个字节,这里char是字符类型,需要对照ASCII码表转换,最后64位:3132372e302e302e31000000ff000000a1bf55da008100807056a70200000000;
第一个字节31是16进制,转换位十进制是49,ASCII码表查询49代表1,以此类推31 32 37 2e 30 2e 30 2e 31,代表127.0.0.1,后面的数据是发送端未赋值的乱码,不用处理。