1: 弾性構造
データのサイズに応じて柔軟にスペースを申請できます。
原則:構造体の最後のメンバーは int data [] で、int 型の空の配列です。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct PDU
{
int a;
int b;
int c;
int d[]; //妙用 写成指针的形式
}PDU;
int main(int argc, char *argv[])
{
printf("%ld\n", sizeof(PDU));
PDU *pdu = (PDU*)malloc(sizeof(PDU)+100*sizeof(int)); //空间大小灵活申请
pdu->a = 90;
pdu->b = 89;
pdu->c = 88;
memcpy(pdu->d, "you jump i jump", 16);
printf("a=%d,b=%d,c=%d,%s\n", pdu->a, pdu->b, pdu->c, (char*)(pdu->d));
printf("Hello World!\n");
return 0;
}
2: 通信プロトコルの設計
/* protocol.h */
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
typedef unsigned int uint; //无符号整型
struct PDU //协议
{
uint uiPDULen; //总的协议数据单元大小
uint uiMsgType; //消息类型
char caData[64];
uint uiMsgLen; //实际消息长度
int caMsg[]; //实际消息 通过空的数组地址,访问动态申请的空间
};
PDU *mkPDU(uint uiMssgLen); //创建一个协议数据单元
#endif // PROTOCOL_H
/* protocol.cpp */
#include "protocol.h"
PDU *mkPDU(uint uiMsgLen) //创建一个协议数据单元
{
uint uiPDULen = sizeof(PDU)+uiMsgLen;
PDU *pdu = (PDU*)malloc(uiPDULen);
if(NULL == pdu)
{
exit(EXIT_FAILURE);
}
memset(pdu, 0, uiPDULen);
pdu->uiPDULen = uiPDULen;
pdu->uiMsgLen = uiMsgLen;
return pdu;
}
3: データ送受信テスト
クライアント
「送信」ボタンをクリックしてスロット機能をご利用ください
/* tcpclient.h */
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <QWidget>
#include <QFile> //操作文件 所用到的 头文件
#include <QTcpSocket>
#include "protocol.h" //包含协议
namespace Ui {
class TcpClient;
}
class TcpClient : public QWidget
{
Q_OBJECT
public:
explicit TcpClient(QWidget *parent = 0);
~TcpClient();
// 加载配置文件
void loadConfig(); //*** Alt + Enter 选第一个 添加函数定义 ***
public slots: //添加槽函数(信号的处理函数)
void showConnect(); //成功连接服务器会发出connected信号 以此判断是否连接成功
private slots:
void on_send_pb_clicked();
private:
Ui::TcpClient *ui;
QString m_strIp; //字符型 存放IP
quint16 m_usPort; //无符号短整型 存放端口号
QTcpSocket m_tcpSocket; //连接服务器,与其进行数据交互
};
#endif // TCPCLIENT_H
/* tcpclient.cpp */
#include "tcpclient.h"
#include "ui_tcpclient.h"
#include <QByteArray>
#include <QDebug>
#include <QMessageBox> //用来显示信息
#include <QHostAddress>
TcpClient::TcpClient(QWidget *parent) :
QWidget(parent),
ui(new Ui::TcpClient)
{
ui->setupUi(this);
loadConfig(); //加载配置文件
connect(&m_tcpSocket, SIGNAL(connected()), this, SLOT(showConnect()));//将信号 与其处理函数 关联起来
m_tcpSocket.connectToHost(QHostAddress(m_strIp),m_usPort); //连接服务器
}
TcpClient::~TcpClient()
{
delete ui;
}
void TcpClient::loadConfig() //加载配置文件
{
QFile file(":/client.config");
if(file.open(QIODevice::ReadOnly))//按住ctrl 再点击open 可阅读源码 加上作用域QIODevice
{
QByteArray baData = file.readAll(); //字节类型
QString strData = baData.toStdString().c_str(); //转换为 字符串 char*
//qDebug() << strData; //"127.0.0.1\r\n8888\r\n"
file.close();
strData.replace("\r\n", " "); //将\r\n替换为空格 解析出IP端口
//qDebug() << strData;
QStringList strList = strData.split(" "); //按照空格切分
// for(int i=0; i<strList.size(); i++)
// {
// qDebug() << "--->" << strList[i];
// }
//转换IP和端口 ip: "127.0.0.1" port: 8888
m_strIp = strList.at(0);
m_usPort = strList.at(1).toUShort();
//qDebug() << "ip:"<< m_strIp <<" port:" << m_usPort;
}
else
{
QMessageBox::critical(this, "open config", "open config failed");
}
}
void TcpClient::showConnect()
{
QMessageBox::information(this, "连接服务器", "连接服务器成功");
}
/* ***********************本文代码 此处开始****************************** */
void TcpClient::on_send_pb_clicked() //客户端 发送数据
{
QString strMsg = ui->lineEdit->text(); //获取输入框信息
if (!strMsg.isEmpty()) //非空
{
PDU *pdu = mkPDU(strMsg.size()+1); //申请数据空间
pdu->uiMsgType = 8888; //假定数据类型为 8888
//将要发送的数据copy到 pdu->caMsg
memcpy(pdu->caMsg, strMsg.toStdString().c_str(), strMsg.size());
qDebug() << (char*)(pdu->caMsg);
m_tcpSocket.write((char*)pdu, pdu->uiPDULen); //tcpsocket发送数据
free(pdu);
pdu = NULL;
}
else //空
{
QMessageBox::warning(this, "信息发送", "发送的信息不能为空");
}
}
サーバ
サーバーをクライアントに関連付けます。
サーバーはクライアント接続を受信すると、自動的にincomingConnection (qintptrソケットDescriptor) に移動し、 自動的に tcpsocket を生成し、記述子を返します。ソケットを生成し、記述子を保存すると、データを送受信できるようになります。
void MyTcpServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << "new client connected" ;
MyTcpSocket *pTcpSocket = new MyTcpSocket; //产生一个socket
pTcpSocket->setSocketDescriptor(socketDescriptor); //将描述符保存
m_tcpSocketList.append(pTcpSocket); //放进链表
}
mytcpsocket を自分でカプセル化する
/* mytcpsocket.h */
#ifndef MYTCPSOCKET_H
#define MYTCPSOCKET_H
#include <QTcpSocket> //
#include "protocol.h"
class MyTcpSocket : public QTcpSocket
{
Q_OBJECT //添加 继承基类 以支持信号槽
public:
MyTcpSocket();
public slots: //自己定义槽函数
void recvMsg();
};
#endif // MYTCPSOCKET_H
/* mytcpsocket.cpp */
#include "mytcpsocket.h"
#include <QDebug>
MyTcpSocket::MyTcpSocket() //自己封装 以方便判断 多个客户端连接产生的多个socket
{
//本身发出的信号 由本身的槽函数接收
//实现 各自socket产生的数据 各自处理
connect(this, SIGNAL(readyRead()), this, SLOT(recvMsg()));
}
void MyTcpSocket::recvMsg() //当MyTcpSocket有数据过来,进行接收
{
//打印当前发送的数据有多少 数据可能会混乱
//因此tcpserver也要按照 自定义协议进行接收
qDebug() << this->bytesAvailable();
uint uiPDULen = 0;
this->read((char*)&uiPDULen, sizeof(uint)); //先收4个字节 信息总的大小
uint uiMsgLen = uiPDULen-sizeof(PDU); //计算实际的消息长度
PDU *pdu = mkPDU(uiMsgLen);
this->read((char*)pdu+sizeof(uint), uiPDULen-sizeof(uint)); //偏移位置 接收剩余的数据
qDebug() << pdu->uiMsgType << (char*)(pdu->caMsg);
}
サーバープログラム
/* mytcpserver.h */
#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H
#include <QTcpServer>
#include <QList>
#include "mytcpsocket.h"
class MyTcpServer : public QTcpServer
{ //支持信号槽 条件1:QTcpServer继承基类QObject MyTcpServer又继承了QTcpServer
Q_OBJECT //支持信号槽 条件2:写上 宏Q_OBJECT
public:
MyTcpServer();
static MyTcpServer &getInstance(); //单例模式
void incomingConnection(qintptr socketDescriptor);
private:
QList<MyTcpSocket*> m_tcpSocketList; //定义链表 保存所有的socket
};
#endif // MYTCPSERVER_H
/* mytcpserver.cpp */
#include "mytcpserver.h"
#include <QDebug>
MyTcpServer::MyTcpServer()
{
}
MyTcpServer &MyTcpServer::getInstance()
{
static MyTcpServer instance;
return instance;
}
void MyTcpServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << "new client connected" ;
MyTcpSocket *pTcpSocket = new MyTcpSocket;
pTcpSocket->setSocketDescriptor(socketDescriptor);
m_tcpSocketList.append(pTcpSocket); //放进链表
}