C/C++网络编程:文件传输系统(双协议单线程版)

C/C++网络编程:文件传输系统(双协议单线程版)

README

UDP和TCP双协议,UDP控制用户指令,TCP控制文件传输。
用户指令:get file(下载)、put file(上传)、list(列出服务器指定位置文件列表)。
服务端UDP、TCP占用两个端口,双线程同时启动。未使用多线程,所以客户端每次只能接入一个。
cJSON包需要自行下载编译。
同时支持windows、linux操作系统,只需要修改两个socket头文件中引用的库即可,windows用winsock2.h,linux用另外三个。

  • 源代码结构图

在这里插入图片描述

  • 源代码

udpsocket.hpp

#ifndef __UDPSOCKET_HPP
#define __UDPSOCKET_HPP

#include "utils.hpp"

#include <stdio.h>
#include <unistd.h>
#include <string.h>

// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>
#include <winsock2.h>

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

class udpSocket
{
private:
    int udpSock;
public:
    udpSocket();
    ~udpSocket();
    bool CreateUDPSocket();
    bool Bind2IPandPort(const char * ip, const uint16_t port);
    bool RecvPackageFrom(void * recvBuffer, void * ip, uint16_t * port);
    bool SendPackageTo(const void * sendBuffer, const char * ip, const uint16_t port);
    bool CloseUDPSocket();
};

#endif

udpsocket.cpp

#include "../header/udpSocket.hpp"

udpSocket::udpSocket():udpSock(-1)
{

    #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
        WSADATA wsaData;
        if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
            printf("WSAStartup error!\n");
    #else
        // Nothing todo...
    #endif

}

udpSocket::~udpSocket()
{
}

bool udpSocket::CreateUDPSocket()
{
    udpSock = socket(AF_INET, SOCK_DGRAM, 0);
    if(!CHECK_POINT(udpSock, "usocket"))
        return false;
    return true;
}

bool udpSocket::Bind2IPandPort(const char * ip, const uint16_t port)
{
    sockaddr_in usock_addr;
    usock_addr.sin_family = AF_INET;
    usock_addr.sin_addr.s_addr = inet_addr(ip);
    usock_addr.sin_port = htons(port);

    int bindCode = bind(udpSock, (sockaddr *)&usock_addr, sizeof(usock_addr));
    if(!CHECK_POINT(bindCode, "ubind"))
        return false;
    return true;
}

bool udpSocket::RecvPackageFrom(void * recvBuffer, void * ip = nullptr, uint16_t * port = nullptr)
{
    char tempRecvBuffer[UNIFORM_BUFFER_SIZE];
    memset(tempRecvBuffer, 0x00, sizeof(tempRecvBuffer));

    sockaddr_in net_usock_addr;
    socklen_t len = sizeof(net_usock_addr);

    int recvfromSize = recvfrom(udpSock, tempRecvBuffer, sizeof(tempRecvBuffer), 0, (sockaddr *)&net_usock_addr, &len);
    // printf("%d\n", recvfromSize);
    if(!CHECK_POINT(recvfromSize, "urecvfrom"))
        return false;
    
    memcpy(recvBuffer, tempRecvBuffer, recvfromSize);

    if(ip)
        memcpy(ip, inet_ntoa(net_usock_addr.sin_addr), 16);

    if(port)
        *port = ntohs(net_usock_addr.sin_port);

    return true;
}

bool udpSocket::SendPackageTo(const void * sendBuffer, const char * ip, const uint16_t port)
{
    sockaddr_in net_usock_addr;
    net_usock_addr.sin_family = AF_INET;
    net_usock_addr.sin_addr.s_addr = inet_addr(ip);
    net_usock_addr.sin_port = htons(port); 

    socklen_t len = sizeof(net_usock_addr);
    int sendtoSize = sendto(udpSock, (char *)sendBuffer, UNIFORM_BUFFER_SIZE, 0, (sockaddr *)&net_usock_addr, len);
    if(!CHECK_POINT(sendtoSize, "usendto"))
        return false;       
    return true;
}

bool udpSocket::CloseUDPSocket()
{
    close(udpSock);
    return true;
}

tcpsocket.hpp

#ifndef __TCPSOCKET_HPP
#define __TCPSOCKET_HPP

#include "utils.hpp"

#include <stdio.h>
#include <unistd.h>
#include <string.h>

// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>
#include <winsock2.h>

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

class tcpSocket
{
private:
    int tcpSock;
public:
    tcpSocket();
    ~tcpSocket();
    bool CreateTCPSocket();
    bool Bind2IPandPort(const char * ip, const uint16_t port);
    bool ListenSocket(const int& times);
    bool Accept(tcpSocket * peerTcpSock, char * ip, uint16_t * port);
    bool Connect(const char * ip, const uint16_t& port);
    bool RecvPackage(void * recvBuffer);
    bool SendPackage(const void * sendBuffer);
    bool CloseTCPSocket();
};

#endif

tcpsocket.cpp

#include "../header/tcpSocket.hpp"

tcpSocket::tcpSocket():tcpSock(-1)
{
    #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
        WSADATA wsaData;
        if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
            printf("WSAStartup error!\n");
    #else
        // Nothing todo...
    #endif
}

tcpSocket::~tcpSocket()
{
}

bool tcpSocket::CreateTCPSocket()
{
    tcpSock = socket(AF_INET, SOCK_STREAM, 0);
    if(!CHECK_POINT(tcpSock, "tsocket"))
        return false;
    return true;
}

bool tcpSocket::Bind2IPandPort(const char * ip, const uint16_t port)
{
    sockaddr_in tsock_addr;
    tsock_addr.sin_family = AF_INET;
    tsock_addr.sin_addr.s_addr = inet_addr(ip);
    tsock_addr.sin_port = htons(port);

    int bindCode = bind(tcpSock, (sockaddr *)&tsock_addr, sizeof(tsock_addr));
    if(!CHECK_POINT(bindCode, "tbind"))
        return false;
    return true;
}

bool tcpSocket::ListenSocket(const int& times)
{
    int listenCode = listen(tcpSock, times);
    if(!CHECK_POINT(listenCode, "tlisten"))
        return false;    
    return true;
}

bool tcpSocket::Accept(tcpSocket * peerTcpSock, char * ip = nullptr, uint16_t * port = nullptr)
{
    sockaddr_in net_tsock_addr;
    socklen_t len = sizeof(net_tsock_addr);

    int peertcpSock = accept(tcpSock, (sockaddr *)&net_tsock_addr, &len);
    if(!CHECK_POINT(peertcpSock, "taccept"))
        return false;    

    // memcpy(peerTcpSock, &peertcpSock, sizeof(int));
    peerTcpSock->tcpSock = peertcpSock;
    
    if(ip)
        memcpy(ip, inet_ntoa(net_tsock_addr.sin_addr), IP_SIZE);
    
    if(port)
        *port = ntohs(net_tsock_addr.sin_port);
    
    return true;
}

bool tcpSocket::Connect(const char * ip, const uint16_t& port)
{
    sockaddr_in tsock_addr;
    tsock_addr.sin_family = AF_INET;
    tsock_addr.sin_addr.s_addr = inet_addr(ip);
    tsock_addr.sin_port = htons(port);

    int connectCode = connect(tcpSock, (sockaddr *)&tsock_addr, sizeof(tsock_addr));
    if(!CHECK_POINT(connectCode, "tconnect"))
        return false;
    return true;
}

bool tcpSocket::RecvPackage(void * recvBuffer = nullptr)
{
    char tempRecvBuffer[UNIFORM_BUFFER_SIZE];
    memset(tempRecvBuffer, 0x00, sizeof(tempRecvBuffer));

    int recvsize = recv(tcpSock, tempRecvBuffer, sizeof(tempRecvBuffer), 0);
    if(!CHECK_POINT(recvsize, "trecv"))
        return false;
    
    if(!recvsize)
        return false;
        
    if (recvBuffer)
        memcpy(recvBuffer, tempRecvBuffer, sizeof(tempRecvBuffer));
    return true;
}

bool tcpSocket::SendPackage(const void * sendBuffer)
{
    int sendsize = send(tcpSock, (char *)sendBuffer, UNIFORM_BUFFER_SIZE, 0);
    if(!CHECK_POINT(sendsize, "tsend"))
        return false;
    return true;
}

bool tcpSocket::CloseTCPSocket()
{
    return (shutdown(tcpSock, 2) == 0);
}

utils.hpp

#ifndef __UTILS_HPP
#define __UTILS_HPP

#include <time.h>
#include <stdio.h>
#include <string.h>

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
    typedef unsigned short  uint16_t;
    typedef int socklen_t;
#endif

#define NOT_FOUND "not found"
#define EXIT_SYS "exit"
#define ACK "ACK"
#define OK "OK"
#define GET "get"
#define PUT "put"
#define LIST "list"
#define SERVER_ROOT_PATH "/root/Socket_Workspace/FILETransfer/filebuffer/"

#define IP_SIZE 32
#define LISTEN_TIMES 5
#define COMMAND_REQ_HEADER_MAXLENGTH 64
#define SEGMENT_NAME_MAXLENGTH 64
#define SEGMENT_DATA_MAXLENGTH 1300
#define UNIFORM_BUFFER_SIZE 1400

struct commandPackage
{
    char reqHeader[COMMAND_REQ_HEADER_MAXLENGTH] = {0};
    char reqContext[SEGMENT_NAME_MAXLENGTH] = {0};

    commandPackage()
    {}

    void setcommandPackage(const char * _reqHeader, const char * _reqContext)
    {
        strcpy(this->reqHeader, _reqHeader);
        strcpy(this->reqContext, _reqContext);
    }

    static commandPackage * getInstance()
    {
        static commandPackage commandpackage;
        return &commandpackage;
    }

    ~commandPackage()
    {
        reqHeader[COMMAND_REQ_HEADER_MAXLENGTH] = {0};
        reqContext[SEGMENT_NAME_MAXLENGTH] = {0};
    }
};

struct HEADER_GET_PUT_Package
{
    bool is_exist = false;
    long length = 0;
    char file_name[SEGMENT_NAME_MAXLENGTH] = {0};
    
    HEADER_GET_PUT_Package()
    {}

    void setHEADER_GET_PUT_Package(const bool& _is_exist, const long& _length, const char * _file_name)
    {
        this->is_exist = _is_exist;
        this->length = _length;
        strcpy(this->file_name, _file_name);
    }

    static HEADER_GET_PUT_Package * getInstance()
    {
        static HEADER_GET_PUT_Package header_gpp_Instance;
        return &header_gpp_Instance;
    }
    
    ~HEADER_GET_PUT_Package()
    {
        is_exist = false;
        length = 0;
        file_name[SEGMENT_NAME_MAXLENGTH] = {0};        
    }
};

static char * resolutionFileName_FromFilePath(char * filePath)
{
    char * fileName = new char[COMMAND_REQ_HEADER_MAXLENGTH];
    memset(fileName, 0x00, COMMAND_REQ_HEADER_MAXLENGTH);

    int index = 0;
    char * ptrFilePath = filePath;
    while((*ptrFilePath) != '\0')
    {
        #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
            ;
        #else
            if((*ptrFilePath) == '/' || (*ptrFilePath) == '\\')
            {
                memset(fileName, 0x00, COMMAND_REQ_HEADER_MAXLENGTH);
                index = 0;
            }
            else
            {
                fileName[index++] = *ptrFilePath;
            }
        #endif
        ptrFilePath++;
    }
    return fileName;
}

static char * IntToString(const int& ival)
{
    char * itos = new char[16];
    memset(itos, 0x00, 16);
    sprintf(itos, "%d", ival);
    return itos;
}

static char * LongIntToString(const long& ival)
{
    char * litos = new char[16];
    memset(litos, 0x00, 16);
    sprintf(litos, "%ld", ival);
    return litos;
}

static long obtainFileLength(FILE * fp_FILE)
{
    fseek(fp_FILE, 0L, SEEK_END);
    return ftell(fp_FILE);
}

static bool criticalOperator(const char * _first_operator, const char * _second_operator)
{
    return (strcmp(_first_operator, _second_operator) == 0);
}

static bool CHECK_POINT(const int number, const char * point)
{
    if(number < 0)
    {
        printf("%s error! exit code %d\n", point, number);
        return false;
    }
    return true;
}

static int progress(int prog, int lastpos)     
{
    int i = 0;
    for (i = 0; i < lastpos; i++)
    {
        printf("\b"); 
    }
    for (i = 0; i < prog; i++)
    {
        printf("#");  
    }
    printf(">>");
    for (i += 2; i < 104; i++) 
    {
        printf(" ");
    }
    i = i + printf("[%d%%]", prog);  
    fflush(stdout);
    return i; 
}

static char * getLocalTime()
{
    time_t timep;
    time(&timep);
    static char localTime[256];
    strftime(localTime, sizeof(localTime), "%Y-%m-%d %H:%M:%S", localtime(&timep));
    return localTime;
}

#endif

commandAnaly.hpp

#ifndef __COMMANDANALY_HPP
#define __COMMANDANALY_HPP

#include "utils.hpp"
#include <string.h>

typedef struct commandPackage commandPackage;

class commandAnaly
{
private:
    char * command;
public:
    commandAnaly(char * _command);
    ~commandAnaly();
    commandPackage * getcommandPackage();
};

#endif

commandAnaly.cpp

#include "../header/commandAnaly.hpp"

commandAnaly::commandAnaly(char * _command)
{
    this->command = new char[UNIFORM_BUFFER_SIZE];
    memset(this->command, 0x00, UNIFORM_BUFFER_SIZE);
    strcpy(this->command, _command);
}

commandAnaly::~commandAnaly()
{
    delete[] this->command;
}

commandPackage * commandAnaly::getcommandPackage()
{
    commandPackage * cmdPack = commandPackage::getInstance();

    char * ptrcommand = this->command;

    while(*ptrcommand != '\0')
    {
        if(*ptrcommand == ' ' && strlen(cmdPack->reqHeader) == 0)
        {
            ptrcommand++;
            continue;
        }
        if(*ptrcommand == ' ')
        {
            while(*ptrcommand != '\0')
            {
                if(*ptrcommand == ' ')
                {
                    ptrcommand++;
                    continue;
                }
                sprintf(cmdPack->reqContext, "%s%c", cmdPack->reqContext, *ptrcommand);
                ptrcommand++;
            }
            break;
        }
        sprintf(cmdPack->reqHeader, "%s%c", cmdPack->reqHeader, *ptrcommand);
        ptrcommand++;
    }

    return cmdPack;
}

dataPackage.hpp

#ifndef __dataPackage_HPP
#define __dataPackage_HPP

#include "utils.hpp"
#include <string.h>

class dataPackage
{
private:
    char PackageSegmentName[SEGMENT_NAME_MAXLENGTH];
    char PackageSegmentData[SEGMENT_DATA_MAXLENGTH];
    int PackageSegmentNumber;
    int PackageSegmentSize;
    bool PackageSegmentEnd;
public:
    dataPackage();
    dataPackage(const char * _PackageSegmentName, const char * _PackageSegmentData, 
        const int _PackageSegmentNumber, const int _PackageSegmentEnd, const int _PackageSegmentSize);
    ~dataPackage();
    char * getPackageSegmentName();
    char * getPackageSegmentData();
    void getPackageSegmentData(char *&pPackageSegmentData);
    int getPackageSegmentNumber();
    int getPackageSegmentSize();
    bool packageSegmentisEnd();
    bool setPackageSegmentName(const char * _PackageSegmentName);
    bool setPackageSegmentData(const char * _PackageSegmentData);
    bool setPackageSegmentNumber(const int _PackageSegmentNumber);
    bool setPackageSegmentEnd();
    bool operator==(dataPackage& udpack);
    void getdata(char * buf)
    {
        memcpy(buf, PackageSegmentData, PackageSegmentSize);
    }
};

#endif

dataPackage.cpp

#include "../header/dataPackage.hpp"

dataPackage::dataPackage()
{
    memset(this->PackageSegmentName, 0x00, SEGMENT_NAME_MAXLENGTH);
    memset(this->PackageSegmentData, 0x00, SEGMENT_DATA_MAXLENGTH);
    this->PackageSegmentNumber = -1;
    this->PackageSegmentEnd = false;
}

dataPackage::dataPackage(const char * _PackageSegmentName, const char * _PackageSegmentData, 
    const int _PackageSegmentNumber, const int _PackageSegmentEnd, const int _PackageSegmentSize)
{
    memset(this->PackageSegmentName, 0x00, SEGMENT_NAME_MAXLENGTH);
    memset(this->PackageSegmentData, 0x00, SEGMENT_DATA_MAXLENGTH);
    memcpy(this->PackageSegmentName, _PackageSegmentName, sizeof(this->PackageSegmentName));
    memcpy(this->PackageSegmentData, _PackageSegmentData, sizeof(this->PackageSegmentData));
    this->PackageSegmentNumber = _PackageSegmentNumber;
    this->PackageSegmentEnd = _PackageSegmentEnd;
    this->PackageSegmentSize = _PackageSegmentSize;
}

dataPackage::~dataPackage()
{
}

char * dataPackage::getPackageSegmentName()
{
    return this->PackageSegmentName;
}

char * dataPackage::getPackageSegmentData()
{
    return this->PackageSegmentData;
}

void dataPackage::getPackageSegmentData(char *&pPackageSegmentData)
{
    pPackageSegmentData = PackageSegmentData;
}

int dataPackage::getPackageSegmentNumber()
{
    return this->PackageSegmentNumber;
}

int dataPackage::getPackageSegmentSize()
{
    return this->PackageSegmentSize;
}

bool dataPackage::packageSegmentisEnd()
{
    return PackageSegmentEnd == true;
}

bool dataPackage::setPackageSegmentName(const char * _PackageSegmentName)
{
    memset(this->PackageSegmentName, 0x00, SEGMENT_NAME_MAXLENGTH);
    strcpy(this->PackageSegmentName, _PackageSegmentName);
    return true;
}

bool dataPackage::setPackageSegmentData(const char * _PackageSegmentData)
{
    memset(this->PackageSegmentData, 0x00, SEGMENT_DATA_MAXLENGTH);
    strcpy(this->PackageSegmentData, _PackageSegmentData);
    return true;
}

bool dataPackage::setPackageSegmentNumber(const int _PackageSegmentNumber)
{
    this->PackageSegmentNumber = _PackageSegmentNumber;
    return true;
}

bool dataPackage::setPackageSegmentEnd()
{
    this->PackageSegmentEnd = true;
    return true;
}

bool dataPackage::operator==(dataPackage& udpack)
{
    return (strcmp(this->PackageSegmentName, udpack.getPackageSegmentName()) == 0)
        && (strcmp(this->PackageSegmentData, udpack.getPackageSegmentData()) == 0)
        && (this->PackageSegmentNumber == udpack.getPackageSegmentNumber())
        && (this->PackageSegmentSize == udpack.getPackageSegmentSize())
        && (this->packageSegmentisEnd() == udpack.packageSegmentisEnd());
}

dirReader.hpp

#ifndef __DIRREADER_HPP
#define __DIRREADER_HPP

#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <vector>
#include "utils.hpp"
#include "../include/cJSON.h"

class dirReader
{
private:
    int numbers;
    char * dirPath;
    cJSON * fileContainer_cjson;
public:
    dirReader(char * _dirPath);
    ~dirReader();
    void readDirPath();
    cJSON * getfileContainer_cjson();
};

#endif

dirReader.cpp

#include "../header/dirReader.hpp"

dirReader::dirReader(char * _dirPath)
{
    this->numbers = 0;
    this->dirPath = new char[SEGMENT_NAME_MAXLENGTH * 2];
    this->fileContainer_cjson = cJSON_CreateObject();
    memset(this->dirPath, 0x00, SEGMENT_NAME_MAXLENGTH * 2);
    strcpy(this->dirPath, _dirPath);
}

dirReader::~dirReader()
{
}

void dirReader::readDirPath()
{
    struct dirent * ptrFile;
    DIR * ptrDirPath;

    if((ptrDirPath = opendir(this->dirPath)) == nullptr)
    {
        printf("dir open error!\n");
        return;
    }

    while((ptrFile = readdir(ptrDirPath)) != nullptr)
    {
        if(strcmp(ptrFile->d_name, ".") == 0 || strcmp(ptrFile->d_name, "..") == 0)
            continue;
        else
            cJSON_AddStringToObject(this->fileContainer_cjson, IntToString(++(this->numbers)), ptrFile->d_name);
    }
}

cJSON * dirReader::getfileContainer_cjson()
{
    return this->fileContainer_cjson;
}

fileTransferClient.hpp

#ifndef __FILETRANSFERCLIENT_HPP
#define __FILETRANSFERCLIENT_HPP

#include "tcpSocket.hpp"

class fileTransferClient
{
private:
    tcpSocket tcpClientSocket;
    char ip[IP_SIZE];
    uint16_t port;
public:
    fileTransferClient(const char * _ip, const uint16_t& _port);
    ~fileTransferClient();
    bool ConnectToTcpServer();
    bool CloseConnect();
    bool Recv(void * recvBuffer);
    bool Send(const void * sendBuffer);
    bool fileTransferClientdoGET(char * getFileName);
    bool fileTransferClientdoPUT(char * putFileName);
};

#endif

fileTransferClient.cpp

#include "../header/fileTransferClient.hpp"
#include "../header/dataPackage.hpp"

fileTransferClient::fileTransferClient(const char * _ip, const uint16_t& _port):port(_port)
{
    tcpClientSocket.CreateTCPSocket();
    memset(this->ip, 0x00, sizeof(this->ip));
    strcpy(this->ip, _ip);
}

fileTransferClient::~fileTransferClient()
{
    tcpClientSocket.CloseTCPSocket();
}

bool fileTransferClient::ConnectToTcpServer()
{
    return tcpClientSocket.Connect(this->ip, this->port);
}

bool fileTransferClient::CloseConnect()
{
    return tcpClientSocket.CloseTCPSocket();
}

bool fileTransferClient::Recv(void * recvBuffer)
{
    commandPackage * commandpackage = (commandPackage *)recvBuffer;
    if(criticalOperator(commandpackage->reqHeader, GET))
        return fileTransferClientdoGET(commandpackage->reqContext);
    else if(criticalOperator(commandpackage->reqHeader, PUT))
        return fileTransferClientdoPUT(commandpackage->reqContext);
    else
        return false;
}

bool fileTransferClient::Send(const void * sendBuffer)
{
    return tcpClientSocket.SendPackage(sendBuffer);
}

bool fileTransferClient::fileTransferClientdoGET(char * getFileName)
{
    char clientRecvBuffer[UNIFORM_BUFFER_SIZE] = {0};
    tcpClientSocket.RecvPackage(clientRecvBuffer);

    if(criticalOperator(clientRecvBuffer, NOT_FOUND))
        return false;

    long fileTotalLength = 0;
    sscanf(clientRecvBuffer, "%ld", &fileTotalLength);
    memset(clientRecvBuffer, 0x00, sizeof(clientRecvBuffer));

    #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
        char filePath[SEGMENT_NAME_MAXLENGTH] = ".//filebuffer//";
    #else
        char filePath[SEGMENT_NAME_MAXLENGTH] = "/root/Socket_Workspace/FILETransfer/";
    #endif

    char * localhostFileName = strcat(filePath, getFileName);
        
    FILE * fpw;
    if((fpw = fopen(localhostFileName, "wb")) == nullptr)
    {
        printf("fopen error!\n");
        return false;
    }

    long totalRecvLength = 0;
    int lastpos = 0;

    while(true)
    {
        tcpClientSocket.RecvPackage(clientRecvBuffer);
        
        if(criticalOperator(clientRecvBuffer, OK))
            break;

        tcpClientSocket.SendPackage(ACK);

        dataPackage * udpack = (dataPackage *)clientRecvBuffer;

        int fileblockLength = -1;
        if((fileblockLength = fwrite(udpack->getPackageSegmentData(), sizeof(char), udpack->getPackageSegmentSize(), fpw)) < 0)
        {
            printf("fwrite error!\n");
            return false;
        }

        totalRecvLength += udpack->getPackageSegmentSize();
        lastpos = progress((int)(100 * 1.0 * totalRecvLength / fileTotalLength), lastpos);
        fflush(stdout);

        memset(clientRecvBuffer, 0x00, sizeof(clientRecvBuffer));
    }

    fclose(fpw);

    return true;
}

bool fileTransferClient::fileTransferClientdoPUT(char * putFileName)
{
    char tcpClientSendBuffer[UNIFORM_BUFFER_SIZE] = {0};
    
    FILE * fpr;
    if((fpr = fopen(putFileName, "rb")) == nullptr)
    {
        printf("fopen error!\n");
        return false;
    }

    long fileTotalLength = obtainFileLength(fpr);
    fseek(fpr, 0L, SEEK_SET);

    tcpClientSocket.SendPackage(LongIntToString(fileTotalLength));
    tcpClientSocket.RecvPackage(tcpClientSendBuffer);
    if(!criticalOperator(tcpClientSendBuffer, ACK))
        return false;

    memset(tcpClientSendBuffer, 0x00, sizeof(tcpClientSendBuffer));

    int fileblockLength = -1;
    int segmentNumber = 1;

    int readsize = SEGMENT_DATA_MAXLENGTH - 2;

    long totalSendLength = 0;
    long totalLength = fileTotalLength;
    int lastpos = 0;

    printf("Uploading......\n");

    while(fileTotalLength)
    {
        if(fileTotalLength < readsize)
        {
            fileblockLength = fread(tcpClientSendBuffer, sizeof(char), fileTotalLength, fpr);
            fileblockLength = fileTotalLength;
            fileTotalLength = 0;
        }
        else
        {
            fileblockLength = fread(tcpClientSendBuffer, sizeof(char), readsize, fpr);
            fileblockLength = readsize;
            fileTotalLength -= readsize;
        }

        char segmentName[UNIFORM_BUFFER_SIZE] = {0};
        sprintf(segmentName, "tcp/pack/%s/segment/%d/%d", resolutionFileName_FromFilePath(putFileName), segmentNumber, 0);

        dataPackage udpack(segmentName, tcpClientSendBuffer, segmentNumber, false, fileblockLength);
        tcpClientSocket.SendPackage(&udpack);

        char clientRecvACK[UNIFORM_BUFFER_SIZE] = {0};
        tcpClientSocket.RecvPackage(clientRecvACK);
        if(!criticalOperator(clientRecvACK, ACK))
            break;

        // totalSendLength += udpack.getPackageSegmentSize();
        // // printf("Send Peocess : %f/%\n", (100 * 1.0 * totalSendLength / totalLength));
        // lastpos = progress((int)(100 * 1.0 * totalSendLength / totalLength), lastpos);
        // fflush(stdout);
        // printf("%s, %d\n", segmentName, udpack.getPackageSegmentSize());

        memset(tcpClientSendBuffer, 0x00, sizeof(tcpClientSendBuffer));
        segmentNumber++;
    }
    
    fclose(fpr);

    memset(tcpClientSendBuffer, 0x00, sizeof(tcpClientSendBuffer));

    tcpClientSocket.SendPackage(OK);
    return true;
}

fileTransferServer.hpp

#ifndef __FILETRANSFERSERVER_HPP
#define __FILETRANSFERSERVER_HPP

#include "tcpSocket.hpp"
#include "../header/dataPackage.hpp"
#include "../header/commandAnaly.hpp"

class fileTransferServer
{
private:
    tcpSocket tcpServerSocket;
    char ip[IP_SIZE];
    uint16_t port;
public:
    fileTransferServer(const char * _ip, const uint16_t& _port);
    ~fileTransferServer();
    void fileTransferServerStartUp();
    void fileTransferServerdoGET(char * reqContext, tcpSocket& peerTcpSocket, const char * peerIP, const uint16_t& peerPORT);
    void fileTransferServerdoPUT(char * reqContext, tcpSocket& peerTcpSocket, const char * peerIP, const uint16_t& peerPORT);
};

#endif

fileTransferServer.cpp

#include "../header/fileTransferServer.hpp"

fileTransferServer::fileTransferServer(const char * _ip, const uint16_t& _port):port(_port)
{
    memset(this->ip, 0x00, sizeof(this->ip));
    strcpy(this->ip, _ip);
}

fileTransferServer::~fileTransferServer()
{
}

void fileTransferServer::fileTransferServerStartUp()
{
    tcpServerSocket.CreateTCPSocket();
    tcpServerSocket.Bind2IPandPort(this->ip, this->port);
    tcpServerSocket.ListenSocket(LISTEN_TIMES);

    printf("[%s] TCP File Transfer Server Start Up...\n", getLocalTime());

    while(true)
    {
        tcpSocket peerTcpSocket;
        char peerIP[IP_SIZE] = {0};
        uint16_t peerPORT = 0;

        if(!tcpServerSocket.Accept(&peerTcpSocket, peerIP, &peerPORT))
            continue;

        printf("[%s] HOST [%s:%d] connected...\n", getLocalTime(), peerIP, peerPORT);

        while(true)
        {
            char buf[UNIFORM_BUFFER_SIZE] = {0};

            if(!peerTcpSocket.RecvPackage(buf))
            {
                printf("[%s] HOST [%s:%d] disconnected...\n", getLocalTime(), peerIP, peerPORT);
                peerTcpSocket.CloseTCPSocket();
                break;
            }

            commandPackage * commandpackage = (commandPackage *)buf;

            if(criticalOperator(commandpackage->reqHeader, GET))
                fileTransferServerdoGET(commandpackage->reqContext, peerTcpSocket, peerIP, peerPORT);
            else if(criticalOperator(commandpackage->reqHeader, PUT))
                fileTransferServerdoPUT(commandpackage->reqContext, peerTcpSocket, peerIP, peerPORT);
            else
                continue;
            
            commandpackage->~commandPackage();
        }
    }
    
    tcpServerSocket.CloseTCPSocket();
}

void fileTransferServer::fileTransferServerdoGET(
    char * reqContext, tcpSocket& peerTcpSocket, const char * peerIP, const uint16_t& peerPORT)
{
    char tcpServerSendBuffer[UNIFORM_BUFFER_SIZE] = {0};
    char filePath[SEGMENT_NAME_MAXLENGTH * 2] = SERVER_ROOT_PATH;
    char * fileName = strcat(filePath, (char *)reqContext);

    FILE * fpr;
    if((fpr = fopen(fileName, "rb")) == nullptr)
        peerTcpSocket.SendPackage(NOT_FOUND);

    long fileTotalLength = obtainFileLength(fpr);
    fseek(fpr, 0L, SEEK_SET);

    peerTcpSocket.SendPackage(LongIntToString(fileTotalLength));

    memset(tcpServerSendBuffer, 0x00, sizeof(tcpServerSendBuffer));

    int fileblockLength = -1;
    int segmentNumber = 1;

    int readsize = SEGMENT_DATA_MAXLENGTH - 2;

    long totalSendLength = 0;
    // long totalLength = fileTotalLength;
    // int lastpos = 0;

    printf("[%s] HOST [%s:%d] Sending......\n", getLocalTime(), peerIP, peerPORT);

    while(fileTotalLength)
    {
        if(fileTotalLength < readsize)
        {
            fileblockLength = fread(tcpServerSendBuffer, sizeof(char), fileTotalLength, fpr);
            fileblockLength = fileTotalLength;
            fileTotalLength = 0;
        }
        else
        {
            fileblockLength = fread(tcpServerSendBuffer, sizeof(char), readsize, fpr);
            fileblockLength = readsize;
            fileTotalLength -= readsize;
        }

        char segmentName[UNIFORM_BUFFER_SIZE] = {0};
        sprintf(segmentName, "tcp/pack/%s/segment/%d/%d", reqContext, segmentNumber, 0);

        dataPackage udpack(segmentName, tcpServerSendBuffer, segmentNumber, false, fileblockLength);
        peerTcpSocket.SendPackage(&udpack);

        char clientRecvACK[UNIFORM_BUFFER_SIZE] = {0};
        peerTcpSocket.RecvPackage(clientRecvACK);
        if(!criticalOperator(clientRecvACK, ACK))
            break;

        // totalSendLength += udpack.getPackageSegmentSize();
        // // printf("Send Peocess : %f/%\n", (100 * 1.0 * totalSendLength / totalLength));
        // lastpos = progress((int)(100 * 1.0 * totalSendLength / totalLength), lastpos);
        // fflush(stdout);
        // // printf("%s, %d\n", segmentName, udpack.getPackageSegmentSize());

        memset(tcpServerSendBuffer, 0x00, sizeof(tcpServerSendBuffer));
        segmentNumber++;
    }
    
    fclose(fpr);

    memset(tcpServerSendBuffer, 0x00, sizeof(tcpServerSendBuffer));

    peerTcpSocket.SendPackage(OK);
    printf("[%s] HOST [%s:%d] Sended Successful......\n", getLocalTime(), peerIP, peerPORT);

}
void fileTransferServer::fileTransferServerdoPUT(
    char * reqContext, tcpSocket& peerTcpSocket, const char * peerIP, const uint16_t& peerPORT)
{
    char tcpServerRecvBuffer[UNIFORM_BUFFER_SIZE] = {0};
    char filePath[SEGMENT_NAME_MAXLENGTH] = SERVER_ROOT_PATH;
    char * reqfileName = resolutionFileName_FromFilePath(reqContext);
    char * fileName = strcat(filePath, reqfileName);

    peerTcpSocket.RecvPackage(tcpServerRecvBuffer);
    peerTcpSocket.SendPackage(ACK);

    long fileTotalLength = 0;
    sscanf(tcpServerRecvBuffer, "%ld", &fileTotalLength);
    memset(tcpServerRecvBuffer, 0x00, sizeof(tcpServerRecvBuffer));

    // #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
    //     char filePath[SEGMENT_NAME_MAXLENGTH] = ".//filebuffer//";
    // #else
    //     char filePath[SEGMENT_NAME_MAXLENGTH] = "../filebuffer/recv/";
    // #endif
        
    FILE * fpw;
    if((fpw = fopen(fileName, "wb")) == nullptr)
    {
        printf("fopen error!\n");
    }

    long totalRecvLength = 0;
    int lastpos = 0;

    while(true)
    {
        peerTcpSocket.RecvPackage(tcpServerRecvBuffer);
        
        if(criticalOperator(tcpServerRecvBuffer, OK))
            break;

        dataPackage * udpack = (dataPackage *)tcpServerRecvBuffer;

        int fileblockLength = -1;
        if((fileblockLength = fwrite(udpack->getPackageSegmentData(), sizeof(char), udpack->getPackageSegmentSize(), fpw)) <= 0)
        {
            printf("fwrite error!\n");
            break;
        }
        
        peerTcpSocket.SendPackage(ACK);

        // totalRecvLength += udpack->getPackageSegmentSize();
        // lastpos = progress((int)(100 * 1.0 * totalRecvLength / fileTotalLength), lastpos);
        // fflush(stdout);
        // printf("%s, %d\n", udpack->getPackageSegmentName(), udpack->getPackageSegmentSize());

        memset(tcpServerRecvBuffer, 0x00, sizeof(tcpServerRecvBuffer));
    }

    fclose(fpw);
    printf("[%s] HOST [%s:%d] Recved Successful......\n", getLocalTime(), peerIP, peerPORT);
}

messageTransferClient.hpp

#ifndef __messageTransferClient_HPP
#define __messageTransferClient_HPP

#include "udpSocket.hpp"
#include "dataPackage.hpp"
#include "dirReader.hpp"
#include "../include/cJSON.h"

#define __OLD_VERSION true

class messageTransferClient
{
private:
    char ip[IP_SIZE];
    uint16_t port;
    udpSocket udpClientSocket;
public:
    messageTransferClient(const char * _ip, const uint16_t _port);
    ~messageTransferClient();
    void Send(const void * sendBuffer);
    bool Recv(void * recvBuffer);
    bool messageTransferClientdoGET();
    bool messageTransferClientdoPUT();
    bool messageTransferClientdoLIST();
};

#endif

messageTransferClient.cpp

#include "../header/messageTransferClient.hpp"

messageTransferClient::messageTransferClient(const char * _ip, const uint16_t _port):port(_port)
{
    memset(ip, 0x00, sizeof(ip));
    strcpy(ip, _ip);

    udpClientSocket.CreateUDPSocket();
}

messageTransferClient::~messageTransferClient()
{
    udpClientSocket.CloseUDPSocket();
}

void messageTransferClient::Send(const void * sendBuffer)
{
    udpClientSocket.SendPackageTo(sendBuffer, this->ip, this->port);
}

bool messageTransferClient::Recv(void * recvBuffer)
{
    commandPackage * commandpackage = (commandPackage *)recvBuffer;
    if(criticalOperator(commandpackage->reqHeader, GET))
        return messageTransferClientdoGET();
    else if(criticalOperator(commandpackage->reqHeader, PUT))
    {
        FILE * fpr;
        if((fpr = fopen(commandpackage->reqContext, "rb")) == nullptr)
        {
            printf("Path error! Not found this file in your localhost!\n");
            return false;
        }
        fclose(fpr);
        return messageTransferClientdoPUT();
    }
    else if(criticalOperator(commandpackage->reqHeader, LIST))
        return messageTransferClientdoLIST();
    else
        return false;
}

bool messageTransferClient::messageTransferClientdoGET()
{
    HEADER_GET_PUT_Package * header_gpp = HEADER_GET_PUT_Package::getInstance();
    
    udpClientSocket.RecvPackageFrom(header_gpp, nullptr, nullptr);
    if(!header_gpp->is_exist)
    {
        printf("Not Found!\n");
        header_gpp->~HEADER_GET_PUT_Package();
        return false;
    }
    
    printf("file [%s] Total Lendth : [%ld] bits\n", header_gpp->file_name, header_gpp->length);
    header_gpp->~HEADER_GET_PUT_Package();
    return true;
}

bool messageTransferClient::messageTransferClientdoPUT()
{
    HEADER_GET_PUT_Package * header_gpp = HEADER_GET_PUT_Package::getInstance();

    udpClientSocket.RecvPackageFrom(header_gpp, nullptr, nullptr);
    if(!header_gpp->is_exist)
    {
        printf("Allowed upload!\n");
        header_gpp->~HEADER_GET_PUT_Package();
        return true;
    }

    printf("file [%s] also exists, Total Lendth : [%ld] bits, can't allowed upload!\n", header_gpp->file_name, header_gpp->length);
    header_gpp->~HEADER_GET_PUT_Package();
    return false;
}

bool messageTransferClient::messageTransferClientdoLIST()
{
    char * dirFileList = new char[UNIFORM_BUFFER_SIZE];
    memset(dirFileList, 0x00, UNIFORM_BUFFER_SIZE);

    udpClientSocket.RecvPackageFrom(dirFileList, nullptr, nullptr);

    cJSON * dirFileList_cJSON = cJSON_Parse(dirFileList);
    if(!dirFileList_cJSON)
    {
        printf("cJSON error!%s\n", cJSON_GetErrorPtr());
        return false;
    }

    cJSON * getDirFileListItem_cJSON;
    for(int itemIndex = 1; ; itemIndex++)
    {
        getDirFileListItem_cJSON = cJSON_GetObjectItem(dirFileList_cJSON, IntToString(itemIndex));
        
        if(!getDirFileListItem_cJSON)
            break;
        
        if(getDirFileListItem_cJSON->type == cJSON_String)
            printf("%s   ", getDirFileListItem_cJSON->valuestring);
    }
    printf("\n");

    return true;
}

messageTransferServer.hpp

#ifndef __messageTransferServer_HPP
#define __messageTransferServer_HPP

#include "udpSocket.hpp"
#include "dataPackage.hpp"
#include "dirReader.hpp"
#include "commandAnaly.hpp"
#include "../include/cJSON.h"

#define __OLD_VERSION false
#define __WRITE_TEST false

class messageTransferServer
{
private:
    udpSocket udpServerSocket;
public:
    messageTransferServer();
    ~messageTransferServer();
    void messageTransferServerStartUp(const char * ip, const uint16_t port);
    void messageTransferServerdoGET(void * reqContext, const char * recvIP, const uint16_t& recvPORT);
    void messageTransferServerdoPUT(void * reqContext, const char * recvIP, const uint16_t& recvPORT);
    void messageTransferServerdoLIST(const char * recvIP, const uint16_t& recvPORT);
};

#endif

messageTransferServer.cpp

#include "../header/messageTransferServer.hpp"

messageTransferServer::messageTransferServer()
{
    udpServerSocket.CreateUDPSocket();
}

messageTransferServer::~messageTransferServer()
{
    udpServerSocket.CloseUDPSocket();
}

void messageTransferServer::messageTransferServerStartUp(const char * ip, const uint16_t port)
{
    udpServerSocket.Bind2IPandPort(ip, port);
    printf("[%s] UDP File Transfer Server Start Up...\n", getLocalTime());

    while (true)
    {
        char recvIP[IP_SIZE] = {0};        
        uint16_t recvPORT = 0;

        char buf[UNIFORM_BUFFER_SIZE] = {0};

        if(!udpServerSocket.RecvPackageFrom(buf, recvIP, &recvPORT))
            continue;

        commandPackage * commandpackage = (commandPackage *)buf;

        if(criticalOperator(commandpackage->reqHeader, GET))
            messageTransferServerdoGET(commandpackage->reqContext, recvIP, recvPORT);   //get
        else if(criticalOperator(commandpackage->reqHeader, PUT))
            messageTransferServerdoPUT(commandpackage->reqContext, recvIP, recvPORT);   //put
        else if(criticalOperator(commandpackage->reqHeader, LIST))
            messageTransferServerdoLIST(recvIP, recvPORT);   //list           
        else
            continue;   //nothing todo

        commandpackage->~commandPackage();
    }
}

void messageTransferServer::messageTransferServerdoGET(void * reqContext, const char * recvIP, const uint16_t& recvPORT)
{
    char filePath[SEGMENT_NAME_MAXLENGTH * 2] = SERVER_ROOT_PATH;
    char * fileName = strcat(filePath, (char *)reqContext);

    HEADER_GET_PUT_Package * header_gpp = HEADER_GET_PUT_Package::getInstance();

    FILE * fpr;
    if((fpr = fopen(fileName, "rb")) == nullptr)
    {
        udpServerSocket.SendPackageTo(header_gpp, recvIP, recvPORT);
        printf("[%s] HOST [%s:%d] REQ : %s NOT FOUND!, RESP : %ld bits\n", getLocalTime(), recvIP, recvPORT, fileName, header_gpp->length);
        header_gpp->~HEADER_GET_PUT_Package();
        return;
    }

    long fileTotalLength = obtainFileLength(fpr);
    fclose(fpr);

    header_gpp->setHEADER_GET_PUT_Package(true, fileTotalLength, fileName);

    udpServerSocket.SendPackageTo(header_gpp, recvIP, recvPORT);
    printf("[%s] HOST [%s:%d] REQ : %s, RESP : %ld bits\n", getLocalTime(), recvIP, recvPORT, fileName, header_gpp->length);
    header_gpp->~HEADER_GET_PUT_Package();
}

void messageTransferServer::messageTransferServerdoPUT(void * reqContext, const char * recvIP, const uint16_t& recvPORT)
{
    char filePath[SEGMENT_NAME_MAXLENGTH * 2] = SERVER_ROOT_PATH;
    char * reqfileName = resolutionFileName_FromFilePath((char *)reqContext);
    char * fileName = strcat(filePath, reqfileName);

    HEADER_GET_PUT_Package * header_gpp = HEADER_GET_PUT_Package::getInstance();

    FILE * fpr;
    if((fpr = fopen(fileName, "rb")) == nullptr)
    {
        udpServerSocket.SendPackageTo(header_gpp, recvIP, recvPORT);
        printf("[%s] HOST [%s:%d] REQ : %s NOT EXISTS THE SAME FILE, ALLOWED UPLOAD!, RESP : %ld bits\n", getLocalTime(), recvIP, recvPORT, fileName, header_gpp->length);
        header_gpp->~HEADER_GET_PUT_Package();
        return;
    }

    long fileTotalLength = obtainFileLength(fpr);
    fclose(fpr);

    header_gpp->setHEADER_GET_PUT_Package(true, fileTotalLength, fileName);
    
    udpServerSocket.SendPackageTo(header_gpp, recvIP, recvPORT);
    printf("[%s] HOST [%s:%d] REQ : %s EXISTS THE SAME FILE, CAN'T ALLOWED UPLOAD!, RESP : %ld bits\n", getLocalTime(), recvIP, recvPORT, fileName, header_gpp->length);
    header_gpp->~HEADER_GET_PUT_Package();
}

void messageTransferServer::messageTransferServerdoLIST(const char * recvIP, const uint16_t& recvPORT)
{
    dirReader * dirFileReader = new dirReader((char *)SERVER_ROOT_PATH);
    dirFileReader->readDirPath();

    cJSON * dirFileContainer = dirFileReader->getfileContainer_cjson();

    char * sendbuf = cJSON_Print(dirFileContainer);
    udpServerSocket.SendPackageTo(sendbuf, recvIP, recvPORT);

    printf("[%s] HOST [%s:%d] REQ : %s, RESP : As follows\n", getLocalTime(), recvIP, recvPORT, LIST);
    printf("%s\n", sendbuf);
}

serverMain.cpp

#include "../header/messageTransferServer.hpp"
#include "../header/fileTransferServer.hpp"

#include <thread>

const char * ip = "127.0.0.1";
const uint16_t msg_port = 39000;
const uint16_t fil_port = 39001;

messageTransferServer mts;
fileTransferServer fts(ip, fil_port);

void thread_mts()
{
    mts.messageTransferServerStartUp(ip, msg_port);
}

void thread_fts()
{
    fts.fileTransferServerStartUp();
}

int main()
{
    std::thread th_mts(thread_mts);
    std::thread th_fts(thread_fts);
    th_fts.detach();
    th_mts.join();
    return 0;
}

clientMain.cpp

#include "../header/messageTransferClient.hpp"
#include "../header/fileTransferClient.hpp"
#include "../header/dataPackage.hpp"
#include "../header/commandAnaly.hpp"
#include <iostream>

const char * ip = "127.0.0.1";
const uint16_t msg_port = 39000;
const uint16_t fil_port = 39001;

void RunCommand()
{
    printf("Welcome File Transfer v2.0.0! [%s]\n\n", getLocalTime());
    printf("Usage:\n");
    printf(" > `get [filename]`    --download file from server.\n");
    printf(" > `put [filename]`    --upload file to server.\n");
    printf(" > `list`              --list server files.\n");
    printf(" > `exit`              --exit the sysytem.\n\n");    
}

bool recvKeyboardInput(char * _usrCommand)
{
    char usrCommand[UNIFORM_BUFFER_SIZE];
    memset(usrCommand, 0x00, sizeof(usrCommand));

    printf(" > ");
    std::cin.getline(usrCommand, sizeof(usrCommand));

    if(criticalOperator(usrCommand, EXIT_SYS))
        return false;

    strcpy(_usrCommand, usrCommand);
    return true;
}

void deal1WordCommand(commandPackage * commandpackage)
{
    messageTransferClient mtc(ip, msg_port);
    mtc.Send(commandpackage);
    mtc.Recv(commandpackage);
}

void deal2WordCommand(commandPackage * commandpackage)
{
    messageTransferClient mtc(ip, msg_port);
    mtc.Send(commandpackage);
    if(mtc.Recv(commandpackage))
    {
        fileTransferClient ftc(ip, fil_port);
        ftc.ConnectToTcpServer();
        ftc.Send(commandpackage);
        if(ftc.Recv(commandpackage))
            printf("Successful!\n");
        ftc.CloseConnect();
    }
}

void RunFILETransfer()
{
    RunCommand();

    while(true)
    {
        char usrCommand[UNIFORM_BUFFER_SIZE];
        memset(usrCommand, 0x00, sizeof(usrCommand));
        
        if(!recvKeyboardInput(usrCommand))
            break;
        
        commandAnaly commandanaly(usrCommand);
        commandPackage * commandpackage = commandanaly.getcommandPackage();

        if(criticalOperator(commandpackage->reqHeader, LIST))
            deal1WordCommand(commandpackage);
        else
            deal2WordCommand(commandpackage);

        commandpackage->~commandPackage();
    }
        
}

int main()
{
    RunFILETransfer();
    return 0;
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值