Android C++ Socket请求XMl,TinyXml解析文件,JNI返回数据给JAVA层

     这个需求的环境是Android FWK层之前用HttpUrlConnection请求了系统配置Xml文件,现在需要Native化,然后需要用C++ Socket封装Http协议请求XML数据,然后将XML解析成Model然后通过JNI返回给上层的流程


     简单看一下上层调用接口就知道下面要干什么了

   

 mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        //getHttpInfo("");
                        String URL = "http://rom.1919game.net:8081/NibiruRom/app/CheckVRKeyMapAction?keymapTime=1502444523662&channel=VR0829&romVersion=3&version=1";
                        String PATH = "/sdcard/shao2.xml";
                        int value = requestXmlInfo(URL,PATH);
                        if(value == 200){
                            HashMap<String, Config> map = getConfigMapValue(PATH);
                            map.size();
                        }else {

                        }
                    }
                }.start();
            }
        });

native函数

    public native int requestXmlInfo(String URL,String filePath);

    public native HashMap<String, Config> getConfigMapValue(String filePath);


第一个接口requestXmlIInfo()实现网络请求然后将数据写到XML文件

第二个接口getConfigMapValue()实现将C++的Map映射到Java层的Map


 下面看一下JNI层调用实现理解更好的理解C++层代码

extern "C"
JNIEXPORT jint JNICALL Java_nibiruvr_nibiru_test_com_httpcpp_MainActivity_requestXmlInfo
        (JNIEnv *env, jobject obj, jstring url, jstring filePath) {

    std::string URL(env->GetStringUTFChars(url, 0));
    std::string FILE_PATH(env->GetStringUTFChars(filePath, 0));
    Header head;
    head.setHost(head.GetHostAddrExclutePortFromUrl(URL.c_str()));
    head.setPath(head.GetParamFromUrl(URL.c_str()));
    head.setPort(head.GetPortFromUrl(URL.c_str()));
    head.setMethod("GET");
    head.setAccept("text/html,application/xhtml+xml,application/xml;q=0.9,*//*;q=0.8");
    head.setUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0");
    head.setAccept_language("fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3");
    head.setConnexion("keep-alive");

    int value;
    Http http(head, &value);
    if (value == 0) {
        return -1;
    } else {
        std::string page = http.getPage(&head);
        if (page.empty()) {
            return -1;
        }
        int result = FileIo::writeContentToFile(FILE_PATH, page);
        head.removeVariable();
        head.removeCookie();
        return result;
    }

}


extern "C"
JNIEXPORT jobject JNICALL Java_nibiruvr_nibiru_test_com_httpcpp_MainActivity_getConfigMapValue
        (JNIEnv *env, jobject obj,jstring filePath) {

    std::string FILE_PATH(env->GetStringUTFChars(filePath, 0));
    NibiruXmlParser testXmlParser;
    std::map<std::string, Config> nativeMap = testXmlParser.parseConfigXml(FILE_PATH);
    jobject javaHashMap;
    if (nativeMap.size() > 0) {
        //创建java层的hashmap
        jclass class_Java_HashMap = env->FindClass("java/util/HashMap");
        jmethodID java_hashMap_Init = env->GetMethodID(class_Java_HashMap, "<init>", "()V");
        javaHashMap = env->NewObject(class_Java_HashMap, java_hashMap_Init, "");
        jmethodID HashMap_put = env->GetMethodID(class_Java_HashMap, "put",
                                                 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");

        jclass java_Config_Class = env->FindClass("nibiruvr/nibiru/test/com/httpcpp/Config");
        jmethodID initMethodID = env->GetMethodID(java_Config_Class, "<init>", "()V");
        jfieldID field_name = env->GetFieldID(java_Config_Class, "packageName",
                                              "Ljava/lang/String;");
        jfieldID field_enable = env->GetFieldID(java_Config_Class, "configEnable",
                                                "Ljava/lang/String;");
        jfieldID field_scale = env->GetFieldID(java_Config_Class, "scale", "Ljava/lang/String;");
        jfieldID field_stretch = env->GetFieldID(java_Config_Class, "stretch",
                                                 "Ljava/lang/String;");
        jfieldID field_offsetx = env->GetFieldID(java_Config_Class, "offsetx",
                                                 "Ljava/lang/String;");
        jfieldID field_offsety = env->GetFieldID(java_Config_Class, "offsety",
                                                 "Ljava/lang/String;");
        jfieldID field_overlayx = env->GetFieldID(java_Config_Class, "overlayx",
                                                  "Ljava/lang/String;");
        jfieldID field_overlayy = env->GetFieldID(java_Config_Class, "overlayy",
                                                  "Ljava/lang/String;");
        jfieldID field_mask = env->GetFieldID(java_Config_Class, "showmask", "Ljava/lang/String;");
        jfieldID field_category = env->GetFieldID(java_Config_Class, "category", "I");
        jfieldID field_hwc = env->GetFieldID(java_Config_Class, "dishwc", "Ljava/lang/String;");
        jfieldID field_touch = env->GetFieldID(java_Config_Class, "touch", "Ljava/lang/String;");
        jfieldID field_sdk = env->GetFieldID(java_Config_Class, "sdk", "Ljava/lang/String;");

        //进行迭代
        for (std::map<std::string, Config>::iterator it = nativeMap.begin();
             it != nativeMap.end(); it++) {
            std::string configPackageName = it->first;
            Config nativeConfig = it->second;
            jstring packageName;

            //创建Java层对象
            jobject javaConfig = env->NewObject(java_Config_Class, initMethodID);
            if (configPackageName.c_str() != NULL && configPackageName.length() > 0) {
                packageName = (env)->NewStringUTF(configPackageName.c_str());
                (env)->SetObjectField(javaConfig, field_name, packageName);
            }

            jstring fieldEnale = (env)->NewStringUTF(nativeConfig.enable.c_str());
            jstring fieldScale = (env)->NewStringUTF(nativeConfig.scale.c_str());
            jstring fieldStrech = (env)->NewStringUTF(nativeConfig.stretch.c_str());
            jstring fieldOffsetx = (env)->NewStringUTF(nativeConfig.offsetx.c_str());
            jstring fieldOffsety = (env)->NewStringUTF(nativeConfig.offsety.c_str());
            jstring fieldOverlayx = (env)->NewStringUTF(nativeConfig.overlayx.c_str());
            jstring fieldOverlayy = (env)->NewStringUTF(nativeConfig.overlayy.c_str());
            jstring fieldShowmax = (env)->NewStringUTF(nativeConfig.showmask.c_str());
            jstring fieldDishwc = (env)->NewStringUTF(nativeConfig.dishwc.c_str());
            jstring fieldTouch = (env)->NewStringUTF(nativeConfig.touch.c_str());
            jstring fieldSdk = (env)->NewStringUTF(nativeConfig.sdk.c_str());


            (env)->SetObjectField(javaConfig, field_enable, fieldEnale);
            (env)->SetObjectField(javaConfig, field_scale, fieldScale);
            (env)->SetObjectField(javaConfig, field_stretch, fieldStrech);
            (env)->SetObjectField(javaConfig, field_offsetx, fieldOffsetx);
            (env)->SetObjectField(javaConfig, field_offsety, fieldOffsety);
            (env)->SetObjectField(javaConfig, field_overlayx, fieldOverlayx);
            (env)->SetObjectField(javaConfig, field_overlayy, fieldOverlayy);
            (env)->SetObjectField(javaConfig, field_mask, fieldShowmax);
            (env)->SetIntField(javaConfig, field_category, (jint) (nativeConfig.category));
            (env)->SetObjectField(javaConfig, field_hwc, fieldDishwc);
            (env)->SetObjectField(javaConfig, field_touch, fieldTouch);
            (env)->SetObjectField(javaConfig, field_sdk, fieldSdk);

            env->CallObjectMethod(javaHashMap, HashMap_put, packageName, javaConfig);
            env->DeleteLocalRef(javaConfig);
            env->DeleteLocalRef(packageName);

            env->DeleteLocalRef(fieldEnale);
            env->DeleteLocalRef(fieldScale);
            env->DeleteLocalRef(fieldStrech);
            env->DeleteLocalRef(fieldOffsetx);
            env->DeleteLocalRef(fieldOffsety);
            env->DeleteLocalRef(fieldOverlayx);
            env->DeleteLocalRef(fieldOverlayy);
            env->DeleteLocalRef(fieldShowmax);
            env->DeleteLocalRef(fieldDishwc);
            env->DeleteLocalRef(fieldTouch);
            env->DeleteLocalRef(fieldSdk);
        }
    }
    return javaHashMap;
}


注意env->DeleteLocalref()在这是为了解决JNI 512引用的限制 需要手动释放加快GC


到这我们就要开始写C++ HTTP的网络请求和TinyXml2的解析代码了,先感谢GitHUb上贡献的代码解决了Socket请求不知道数据流什么时候结束的问题

https://github.com/lucasBertola/HTTP-CLIENT


现在看一下核心代码其实就是拼HTTP协议

#include "NibiruHttp.h"
#include "rechercheInformation.h"
#include <sstream>

//TODO DEV HTTPS

#define BUFLEN 20480

Http::Http(Header head, int *result) {
    *result = CreateSocket(head);
    *result = ConnectSocket();
}

int Http::CreateSocket(Header head) {

    struct hostent *hostinfo = NULL;
    const char *hostname = head.getHostChar(true);
    hostinfo = gethostbyname(hostname);
    if (hostinfo == NULL) {
        fprintf(stderr, "Unknown host %s\n", hostname);
        return 0;
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    sin.sin_addr = *(IN_ADDR *) hostinfo->h_addr;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(head.getPort());
    return 1;

}

int Http::ConnectSocket() {
    if (connect(sock, (SOCKADDR *) &sin, sizeof(sin)) == SOCKET_ERROR) {
        //std::cout << "Error in the connection..." << std::endl;
        return 0;
    }
    return 1;
}

//reponce-> message renvoyer par le server dont on veut prendre les cookies.
void Http::getCookie(Header *head, std::string reponce) {

    RechercheInfo::searchCutLeft(&reponce, "Set-Cookie: ", true);
    std::string tampon = reponce;

    while (tampon.size() != 0) {
        std::string tampon2 = tampon;
        RechercheInfo::searchCutRight(&tampon2, "\r\n", false);
        RechercheInfo::searchCutRight(&tampon, "=", true);
        RechercheInfo::searchCutLeft(&tampon2, "=", true);

        if (tampon2.find(";") <= tampon2.size())
            RechercheInfo::searchCutRight(&tampon2, ";", true);
        else
            RechercheInfo::searchCutRight(&tampon2, "\r\n", true);

        head->addCookie(tampon, tampon2);
        RechercheInfo::searchCutLeft(&reponce, "Set-Cookie: ", true);
        tampon = reponce;
    }
}

std::string Http::getPage(Header *head) {

    sendPaquet(*head);

    std::string reponce = recvPaquet();

    getCookie(head, reponce);
    head->setReferer("http://" + head->getHostString(true) + head->getPathString());
    redirection(head, &reponce);

    return reponce;
}

void Http::redirection(Header *head, std::string *reponce) {
    if (reponce->find("\r\nlocation: ") != std::string::npos || reponce->find("\r\nLocation: ") !=
                                                                std::string::npos)//TODO changer imagine que sur le content y a un \r\nlocation
    {

        if (reponce->find("\r\nlocation: ") != std::string::npos)
            RechercheInfo::searchCutLeft(reponce, "\r\nlocation: ", true);


        if (reponce->find("\r\nLocation: ") != std::string::npos)
            RechercheInfo::searchCutLeft(reponce, "\r\nLocation: ", true);

        RechercheInfo::searchCutRight(reponce, "\r\n", true);

        if (reponce->find("http://") == std::string::npos)
            head->setPath(*reponce);
        else {
            std::string host = "";
            std::string path = "/";

            RechercheInfo::searchCutLeft(reponce, "http://", true);

            if (reponce->find("/") == std::string::npos)
                host = *reponce;
            else {
                path = *reponce;
                host = *reponce;

                RechercheInfo::searchCutLeft(&path, "/", false);
                RechercheInfo::searchCutRight(&host, "/", true);
            }

            head->setHost("http://" + host);
            head->setPath(path);
        }
        head->setMethod("GET");

        head->removeVariable();

        (*reponce) = getPage(head);
    }
}

void Http::sendPaquet(Header head) {
    //http protocol uses \r\n as a new line, and the last line must always be empty.
    std::string requete = head.getMethod() + " ";

    if (!head.getPathString().compare("")) {
        requete += "/ ";
    } else requete += head.getPathString();

    requete += " HTTP/1.1\r\n";

    if (head.getHostString().compare(""))
        requete += "Host: " + head.getHostString() + "\r\n";

    if (head.getUserAgent().compare(""))
        requete += "User-Agent: " + head.getUserAgent() + "\r\n";

    if (head.getAccept().compare(""))
        requete += "Accept: " + head.getAccept() + "\r\n";

    if (head.getAccept_language().compare(""))
        requete += "Accept-Language: " + head.getAccept_language() + "\r\n";

    if (head.getAccept_encoding().compare(""))
        requete += "Accept-Encoding: " + head.getAccept_encoding() + "\r\n";

    if (head.getRefererString().compare(""))
        requete += "Referer: " + head.getRefererString() + "\r\n";

    if (head.getCookie().compare(""))
        requete += "Cookie: " + head.getCookie() + "\r\n";
    requete += "Cookie: " + head.getCookie() + "\r\n";

    if (head.getConnexion().compare(""))
        requete += "Connection: " + head.getConnexion() + "\r\n";

    if (head.getIfnotMatch().compare(""))
        requete += "If-None-Match: " + head.getIfnotMatch() + "\r\n";

    if (!head.getVariable().compare("")) {
        requete += "\r\n";
    } else {
        std::ostringstream oss;
        oss << head.getVariable().size();
        requete +=
                "Content-Type: application/x-www-form-urlencoded\r\nContent-Length: " + oss.str() +
                "\r\n\r\n" + head.getVariable() + "\r\n\r\n";
    }

    char *bufferOutput = new char[requete.length() + 1];
    strcpy(bufferOutput, requete.c_str());

    unsigned int nbEnvoyer = 0;
    int erreur = -1;
    while (nbEnvoyer < strlen(bufferOutput)) {
        erreur = send(sock, bufferOutput + nbEnvoyer, strlen(bufferOutput) - nbEnvoyer, 0);
        nbEnvoyer += erreur;
        if (erreur == 0 || erreur == -1) {
            std::cout
                    << "Erreur dans l'envois de la requette...La connexion a t elle pas été fermé?. "
                    << erreur << std::endl;//TODO GERER UNE RECONNEXION
        }
    }

    delete[] bufferOutput;
}

int Http::recvTimeOut(unsigned int sock, int millisecond, std::string *chaine) {
    struct timeval timeout;
    timeout.tv_sec = millisecond;
    timeout.tv_usec = 0;

    fd_set readfs;
    FD_ZERO(&readfs);
    FD_SET(sock, &readfs);
    int nb = select(sock + 1, &readfs, NULL, NULL, &timeout);

    if (nb == 0) {
        return 0;
    } else if (nb == -1) {
        //CreateSocket(head);
        ConnectSocket();
        return 0;
    } else {
        char recvbuf[BUFLEN];
        int recvbuflen = BUFLEN;

        int result = recv(sock, recvbuf, recvbuflen, 0);
        for (int i = 0; i < result; i++)
            (*chaine) += recvbuf[i];

        return result;
    }
}

int Http::hexaTodecimal(std::string hex) {
    int x;
    std::istringstream iss(hex);
    iss >> std::hex >> x;
    return x;
}

unsigned int Http::tailleHead(std::string page) {
    int taille = page.length();
    RechercheInfo::searchCutRight(&page, "\r\n\r\n", false);

    if (page.length() == 0)
        return taille;

    return page.length();
}

std::string Http::recvPaquet() {
    //stock the number of bytes received
    unsigned int nbRecu = 0;
    //the number of bytes of the chunk.
    unsigned int chunked = 0;
    bool isChunked = false;
    bool haveContentLength = false;
    //the number of bytes of the content.
    unsigned int contentLenght = 0;
    //Stock the reponse
    std::string input = "";
    bool isRunning = true;
    unsigned int tampon;
    while (isRunning) {
        tampon = recvTimeOut(sock, 2000, &input);
        nbRecu += tampon;
        if (tampon == 0) {
            break;
        }
        if (!isChunked && input.find("Transfer-Encoding: chunked") != std::string::npos &&
            input.find("Transfer-Encoding: chunked") < tailleHead(input)) {
            std::string tampon = input;
            tampon.replace(nbRecu, tampon.size(), "");
            RechercheInfo::searchCutLeft(&tampon, "\r\n\r\n", true);
            RechercheInfo::searchCutRight(&tampon, "\r", true);
            if (tampon.size() > 0) {
                chunked = hexaTodecimal(tampon) + input.find(tampon) + tampon.size() + 4;
                isChunked = true;
                input.replace(input.find(tampon), tampon.size() + 2, "");
                nbRecu -= (tampon.length() + 2);
                chunked -= (tampon.length() + 2);
            }
        }

        while (isChunked && chunked < nbRecu) {
            std::string tampon = input;
            tampon.replace(0, chunked, "");
            RechercheInfo::searchCutRight(&tampon, "\r", true);
            //POUR GERER LE cas ou le input s'arrete pil sur la valeur hexa
            if (tampon.length() == 0)
                break;
            int nombre = hexaTodecimal(tampon);
            //if the size of the new chunk is 0 we stop.
            if (nombre == 0) {
                isRunning = false;
                break;
            } else //else we upgrade the chunk
            {
                int EmplacementChunked = 0;
                if (input.find(tampon) != std::string::npos)
                    EmplacementChunked = input.find(tampon);

                if (nbRecu < (EmplacementChunked + tampon.length() + 2)) break;

                input.replace(EmplacementChunked - 2, tampon.length() + 4, "X");

                nbRecu -= (tampon.length() + 3);
                chunked += nombre + 1;// en faite ce serait plus : -2 +2 + nombre
            }
        }

        if (!haveContentLength) {
            std::string tampon = input;
            //on garde uniquement le header
            tampon.replace(tailleHead(input), tampon.size() - tailleHead(input), "");

            RechercheInfo::searchCutLeft(&tampon, "Content-Length:", true);
            RechercheInfo::searchCutRight(&tampon, "\r", true);
            if (tampon.size() > 0) {
                std::istringstream iss(tampon);
                iss >> contentLenght;
                haveContentLength = true;
            }
        }
        if (haveContentLength && (contentLenght + tailleHead(input)) <= nbRecu) {
            break;
        }
    }
    return input;
}

Http::~Http() {
    closesocket(sock);
}



现在开始解析XML了 用的是开源的TinyXml2

std::map<std::string, Config> NibiruXmlParser::parseConfigXml(const std::string filePath) {
    XMLDocument doc;
    doc.LoadFile(filePath.c_str());
    XMLElement *rootElement = doc.RootElement();
    XMLElement *packElement = rootElement->FirstChildElement("package");
    std::map<std::string, Config> vrConfigMap;
    vrConfigMap.clear();
    while (packElement) {
        //获得packageName
        Config modelConfig;
        XMLElement *packageChild = packElement->FirstChildElement();
        const XMLAttribute *attributeOfSurface = packElement->FirstAttribute();
        modelConfig.packageName = attributeOfSurface->Value();
        while (packageChild) {
            const std::string childName = packageChild->Name();
            std::string value;
            if (packageChild->GetText() == NULL) {
                value = "";
            } else {
                value = packageChild->GetText();
            }
            if (childName.compare("enable") == 0) {
                modelConfig.enable = value;
            } else if (childName.compare("scale") == 0) {
                modelConfig.scale = value;
            } else if (childName.compare("stretch") == 0) {
                modelConfig.stretch = value;
            } else if (childName.compare("offsetx") == 0) {
                modelConfig.offsetx = value;
            } else if (childName.compare("offsety") == 0) {
                modelConfig.offsety = value;
            } else if (childName.compare("overlayx") == 0) {
                modelConfig.overlayx = value;
            } else if (childName.compare("overlayy") == 0) {
                modelConfig.overlayy = value;
            } else if (childName.compare("mask") == 0) {
                modelConfig.showmask = value;
            } else if (childName.compare("category") == 0) {
                modelConfig.category = atoi(value.c_str());
            } else if (childName.compare("hwc") == 0) {
                modelConfig.dishwc = value;
            } else if (childName.compare("touch") == 0) {
                modelConfig.touch = value;
            } else if (childName.compare("sdk_distort") == 0) {
                modelConfig.sdk = value;
            }
            packageChild = packageChild->NextSiblingElement();
        }
        vrConfigMap.insert(std::pair<std::string, Config>(modelConfig.packageName, modelConfig));
        packElement = packElement->NextSiblingElement();
    }
    return vrConfigMap;
};


到这C++--》JNI--》JAVA就完成需求了

下面就是我Demo实现的GitHub地址 欢迎Star

https://github.com/ShaoZhongqi2359/JNI_CPP_HTTP_XMLParser




    



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值