ネットワークディスクプロジェクト - 通信プロトコルの設計

通信プロトコルの設計

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); //放进链表
}

おすすめ

転載: blog.csdn.net/weixin_43200943/article/details/130428506