• wss


    最重要的是,windows的路径问题

    /*
     * libwebsockets-test-client - libwebsockets test implementation
     *
     * Copyright (C) 2011-2017 Andy Green <andy@warmcat.com>
     *
     * This file is made available under the Creative Commons CC0 1.0
     * Universal Public Domain Dedication.
     *
     * The person who associated a work with this deed has dedicated
     * the work to the public domain by waiving all of his or her rights
     * to the work worldwide under copyright law, including all related
     * and neighboring rights, to the extent allowed by law. You can copy,
     * modify, distribute and perform the work, even for commercial purposes,
     * all without asking permission.
     *
     * The test apps are intended to be adapted for use in your code, which
     * may be proprietary.  So unlike the library itself, they are licensed
     * Public Domain.
     */
    
    #include "lws_config.h"
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <getopt.h>
    #include <string.h>
    #include <signal.h>
    
    #ifdef _WIN32
    #define random rand
    #include "gettimeofday.h"
    #else
    #include <syslog.h>
    #include <sys/time.h>
    #include <unistd.h>
    #endif
    
    #include <libwebsockets.h>
    
    struct lws_poly_gen {
        uint32_t cyc[2];
    };
    
    #define block_size (3 * 4096)
    
    static int deny_deflate, longlived, mirror_lifetime, test_post, once;
    static struct lws *wsi_dumb, *wsi_mirror;
    static struct lws *wsi_multi[3];
    static volatile int force_exit;
    static unsigned int opts, rl_multi[3];
    static int flag_no_mirror_traffic, justmirror, flag_echo;
    static uint32_t count_blocks = 1024, txb, rxb, rx_count, errs;
    static struct lws_poly_gen tx = { { 0xabcde, 0x23456789 } },
                   rx = { { 0xabcde, 0x23456789 } }
    ;
    
    #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
    char crl_path[1024] = "";
    #endif
    
    /*
     * This demo shows how to connect multiple websockets simultaneously to a
     * websocket server (there is no restriction on their having to be the same
     * server just it simplifies the demo).
     *
     *  dumb-increment-protocol:  we connect to the server and print the number
     *                we are given
     *
     *  lws-mirror-protocol: draws random circles, which are mirrored on to every
     *                client (see them being drawn in every browser
     *                session also using the test server)
     */
    
    enum demo_protocols {
    
        PROTOCOL_DUMB_INCREMENT,
        PROTOCOL_LWS_MIRROR,
    
        /* always last */
        DEMO_PROTOCOL_COUNT
    };
    
    static uint8_t
    lws_poly_rand(struct lws_poly_gen *p)
    {
        p->cyc[0] = (p->cyc[0] & 1) ? (p->cyc[0] >> 1) ^ 0xb4bcd35c :
                          p->cyc[0] >> 1;
        p->cyc[0] = (p->cyc[0] & 1) ? (p->cyc[0] >> 1) ^ 0xb4bcd35c :
                          p->cyc[0] >> 1;
        p->cyc[1] = (p->cyc[1] & 1) ? (p->cyc[1] >> 1) ^ 0x7a5bc2e3 :
                          p->cyc[1] >> 1;
    
        return p->cyc[0] ^ p->cyc[1];
    }
    
    /*
    static void show_http_content(const char *p, size_t l)
    {
        if (lwsl_visible(LLL_INFO)) {
            while (l--)
                if (*p < 0x7f)
                    putchar(*p++);
                else
                    putchar('.');
        }
    }
    */
    
    /*
     * dumb_increment protocol
     *
     * since this also happens to be protocols[0], some callbacks that are not
     * bound to a specific protocol also turn up here.
     */
    
    
    
    /* lws-mirror_protocol */
    
    int gCount = 0;
    
    static int
    callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
                void *user, void *in, size_t len)
    {
        unsigned char buf[LWS_PRE + block_size], *p;
        unsigned int rands[4];
        int l = 0;
        int n;
    
        switch (reason) {
        case LWS_CALLBACK_CLIENT_ESTABLISHED:
    
            lwsl_notice("mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
    
            if (flag_echo) {
                rxb = txb = 0;
                rx.cyc[0] = tx.cyc[0] = 0xabcde;
                rx.cyc[1] = tx.cyc[1] = 0x23456789;
    
                lws_callback_on_writable(wsi);
    
                break;
            }
    
            lws_get_random(lws_get_context(wsi), rands, sizeof(rands[0]));
            mirror_lifetime = 16384 + (rands[0] & 65535);
            /* useful to test single connection stability */
            if (longlived)
                mirror_lifetime += 500000;
    
            lwsl_notice("opened mirror connection with "
                  "%d lifetime\n", mirror_lifetime);
    
            /*
             * mirror_lifetime is decremented each send, when it reaches
             * zero the connection is closed in the send callback.
             * When the close callback comes, wsi_mirror is set to NULL
             * so a new connection will be opened
             *
             * start the ball rolling,
             * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
             */
            if (!flag_no_mirror_traffic)
                lws_callback_on_writable(wsi);
            break;
    
        case LWS_CALLBACK_CLIENT_CLOSED:
            lwsl_notice("mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d, "
                    "rxb %d, rx_count %d\n", mirror_lifetime, rxb,
                    rx_count);
            wsi_mirror = NULL;
            if (flag_echo || once)
                force_exit = 1;
            break;
    
        case LWS_CALLBACK_CLIENT_WRITEABLE:
            lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE\n");
            if (flag_no_mirror_traffic)
                return 0;
    /*
            if (flag_echo) {
                for (n = 0; n < (int)block_size; n++)
                    buf[LWS_PRE + n] = lws_poly_rand(&tx);
    
                n = lws_write(wsi, &buf[LWS_PRE], block_size,
                          opts | LWS_WRITE_TEXT);
                if (n < 0) {
                    lwsl_err("Error sending\n");
                    return -1;
                }
    
                txb++;
                if (txb != count_blocks)
                    lws_callback_on_writable(wsi);
                else {
                    lwsl_notice("send completed: %d x %d\n",
                            count_blocks, block_size);
                }
                break;
            }
    
            for (n = 0; n < 1; n++) {
                lws_get_random(lws_get_context(wsi), rands,
                           sizeof(rands));
                l += sprintf((char *)&buf[LWS_PRE + l],
                        "c #%06X %u %u %u;",
                        rands[0] & 0xffffff,    
                        rands[1] & 511,    
                        rands[2] & 255,        
                        (rands[3] & 31) + 1);    
            }
    */
            char str[1024] ={0};
            //strcpy(str,"{\"jsonrpc\" : \"2.0\",\"id\" : \"327\",\"method\" : \"Init\",\"params\" : {\"protocolVersion\" : \"1.0\",\"mac\" : \"8c:68:c8:d4:30:b8\",");
            //strcat(str,"\"version\" : \"V2.0.1T1\",\"boot\": \"V2.0.1T1\",\"configuration\": \"V2.0.1T1\",\"type\" : \"E8820V2-SHDX\",");
            //strcat(str,"\"serialNumber\" : \"HN51N6KHBA02378\",\"odm\":\"1\",\"areaCode\":\"\"}}");
            strcpy(str,"{\"params\": {\"configuration\": \"V1.0.0.0B5-0000\", \"type\": \"ZXHN E3630-0000\", \"version\": \"V1.0.0.2B4.0000_O\", \"protocolVersion\": \"1.0\", \"reversion\": \"V1.0.0.2B4.0000\", \"odm\": \"5\", \"mac\": \"e0:19:54:7f:ff:91\", \"serialNumber\": \"HN5EN84KAC00056\", \"areaCode\": \"\"}, \"jsonrpc\": \"2.0\", \"id\": \"2\", \"method\": \"Init\"}");
            //strcpy(str,"{\"params\": {\"configuration\": \"V1.0.0.0B5-0000\", \"type\": \"ZXHN E3630-0000\", \"version\": \"V1.0.0.2B4.0000_O\", \"protocolVersion\": \"1.0\", \"reversion\": \"V1.0.0.2B4.0000\", \"odm\": \"5\", \"mac\": \"e0:19:54:7f:ff:91\", \"serialNumber\": \"HN5EN84KAC00056\", \"areaCode\": \"\"}, \"jsonrpc\": \"2.0\", \"id\": \"2\", \"method\": \"SearchAll\"}");
            //strcpy(str,"{\"params\": {\"type\": \"ZXHN E3630-0000\",\"version\": \"V1.0.0.2B6.0000\",},\"jsonrpc\": \"2.0\",\"id\": \"2\",\"method\": \"PullUpgrade\"}");
            printf(" ---%s--- \n",str);
    
            if(gCount++ > 3){
                break;
            }
    
            strcpy((char*)&buf[LWS_PRE],str);
    
            n = lws_write(wsi, &buf[LWS_PRE], (unsigned int)strlen(str),
                      opts | LWS_WRITE_TEXT);
            if (n < 0)
                return -1;
            if (n < l) {
                lwsl_err("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n");
                return -1;
            }
            if (!justmirror)
                mirror_lifetime--;
            if (!mirror_lifetime) {
                lwsl_notice("closing mirror session\n");
                return -1;
            }
            /* get notified as soon as we can write again */
            lws_callback_on_writable(wsi);
    
    #if !defined(_WIN32) && !defined(WIN32)
            usleep(50);
    #endif
            break;
    
        case LWS_CALLBACK_CLIENT_RECEIVE:
            printf("---LWS_CALLBACK_CLIENT_RECEIVE----%s---%ld-->\n",(char*)in,len);
            if (flag_echo) {
                p = (unsigned char *)in;
                for (n = 0; n < (int)len; n++)
                    if (*p++ != lws_poly_rand(&rx)) {
                        lwsl_err("mismatch at rxb %d offset %d\n", rxb + (n / block_size), n % block_size);
                        errs++;
                        force_exit = 1;
                        return -1;
                    }
                rx_count += (unsigned int)(unsigned long long)len;
                while (rx_count >= block_size) {
                    rx_count -= block_size;
                    rxb++;
                }
                if (rx_count == 0 && rxb == count_blocks) {
                    lwsl_notice("Everything received: errs %d\n",
                            errs);
                    force_exit = 1;
                    return -1;
                }
            }
            break;
        default:
            break;
        }
    
        return 0;
    }
    //wss://appstore-gw.ztehome.com.cn:443/tunnel?mac=a8:74:84:e8:3b:94&sn=HN5YN81M7616415&type=ZXHN%20E1600-1000A&version=V1.0.0.2B1.1000&reversion=V1.1.0.5B4.1000
    
    /*
    static int
    callback_test_raw_client(struct lws *wsi, enum lws_callback_reasons reason,
                 void *user, void *in, size_t len)
    {
        switch (reason) {
        case LWS_CALLBACK_RAW_ADOPT:
            lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
            break;
    
        case LWS_CALLBACK_RAW_RX:
            lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
            puts(in);
            break;
    
        case LWS_CALLBACK_RAW_CLOSE:
            lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
            break;
    
        case LWS_CALLBACK_RAW_WRITEABLE:
            lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
            break;
    
        default:
            break;
        }
    
        return 0;
    }
    */
    /* list of supported protocols and callbacks */
    
    static const struct lws_protocols protocols[] = {
        /*
        {
            "dumb-increment-protocol",
            callback_dumb_increment,
            0,
            20,
        },*/
        {
            "lws-mirror-protocol",
            callback_lws_mirror,
            0,
            4096,
        }, 
        /*
        {
            "lws-test-raw-client",
            callback_test_raw_client,
            0,
            128
        },*/
        { NULL, NULL, 0, 0 } /* end */
    };
    
    static const struct lws_extension exts[] = {
        {
            "permessage-deflate",
            lws_extension_callback_pm_deflate,
            "permessage-deflate; client_no_context_takeover"
        },
        {
            "deflate-frame",
            lws_extension_callback_pm_deflate,
            "deflate_frame"
        },
        { NULL, NULL, NULL /* terminator */ }
    };
    
    
    
    void sighandler(int sig)
    {
        force_exit = 1;
    }
    
    static struct option options[] = {
        { "help",    no_argument,        NULL, 'h' },
        { "debug",      required_argument,      NULL, 'd' },
        { "port",    required_argument,    NULL, 'p' },
        { "ssl",    no_argument,        NULL, 's' },
        { "strict-ssl",    no_argument,        NULL, 'S' },
        { "version",    required_argument,    NULL, 'v' },
        { "undeflated",    no_argument,        NULL, 'u' },
        { "echo",    no_argument,        NULL, 'e' },
        { "multi-test",    no_argument,        NULL, 'm' },
        { "nomirror",    no_argument,        NULL, 'n' },
        { "justmirror",    no_argument,        NULL, 'j' },
        { "longlived",    no_argument,        NULL, 'l' },
        { "post",    no_argument,        NULL, 'o' },
        { "once",    no_argument,        NULL, 'O' },
        { "pingpong-secs", required_argument,    NULL, 'P' },
        { "ssl-cert",  required_argument,    NULL, 'C' },
        { "ssl-key",  required_argument,    NULL, 'K' },
        { "ssl-ca",  required_argument,        NULL, 'A' },
    #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
        { "ssl-crl",  required_argument,        NULL, 'R' },
    #endif
        { NULL, 0, 0, 0 }
    };
    
    static int ratelimit_connects(unsigned int *last, unsigned int secs)
    {
        struct timeval tv;
    
        gettimeofday(&tv, NULL);
    
        if (tv.tv_sec - (*last) < secs)
            return 0;
    
        *last = tv.tv_sec;
    
        return 1;
    }
    
    int main(int argc, char **argv)
    {
        int n = 0, m, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
        unsigned int rl_dumb = 0, rl_mirror = 0, do_ws = 1, pp_secs = 0,
                 do_multi = 0;
        struct lws_context_creation_info info;
        struct lws_client_connect_info i;
        struct lws_context *context;
        const char *prot, *p;
        char path[300];
        char cert_path[1024] = "";
        char key_path[1024] = "";
        char ca_path[1024] = "";
        unsigned long last = lws_now_secs();
    
        
    
        memset(&info, 0, sizeof info);
    
        lwsl_notice("libwebsockets test client - license LGPL2.1+SLE\n");
        lwsl_notice("(C) Copyright 2010-2018 Andy Green <andy@warmcat.com>\n");
    
        if (argc < 2)
            goto usage;
    
        while (n >= 0) {
            n = getopt_long(argc, argv, "Sjnuv:hsp:d:lC:K:A:P:moeO", options,
                    NULL);
            if (n < 0)
                continue;
            switch (n) {
            case 'd':
                lws_set_log_level(atoi(optarg), NULL);
                break;
            case 's': /* lax SSL, allow selfsigned, skip checking hostname */
                use_ssl = LCCSCF_USE_SSL |
                      LCCSCF_ALLOW_SELFSIGNED |
                      LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
                break;
            case 'S': /* Strict SSL, no selfsigned, check server hostname */
                use_ssl = LCCSCF_USE_SSL;
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'e':
                flag_echo = 1;
                break;
            case 'P':
                pp_secs = atoi(optarg);
                lwsl_notice("Setting pingpong interval to %d\n", pp_secs);
                break;
            case 'j':
                justmirror = 1;
                break;
            case 'l':
                longlived = 1;
                break;
            case 'v':
                ietf_version = atoi(optarg);
                break;
            case 'u':
                deny_deflate = 1;
                break;
            case 'm':
                do_multi = 1;
                break;
            case 'o':
                test_post = 1;
                break;
            case 'O':
                once = 1;
                break;
            case 'n':
                flag_no_mirror_traffic = 1;
                lwsl_notice("Disabled sending mirror data (for pingpong testing)\n");
                break;
            case 'C':
                lws_strncpy(cert_path, optarg, sizeof(cert_path));
                break;
            case 'K':
                lws_strncpy(key_path, optarg, sizeof(key_path));
                break;
            case 'A':
                lws_strncpy(ca_path, optarg, sizeof(ca_path));
                break;
    
    #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
            case 'R':
                lws_strncpy(crl_path, optarg, sizeof(crl_path));
                break;
    #endif
            case 'h':
                goto usage;
            }
        }
    
        if (optind >= argc)
            goto usage;
    
        signal(SIGINT, sighandler);
    
        memset(&i, 0, sizeof(i));
    
        i.port = port;
        if (lws_parse_uri(argv[optind], &prot, &i.address, &i.port, &p))
            goto usage;
    
        /* add back the leading / on path */
        if (p[0] != '/') {
            path[0] = '/';
            lws_strncpy(path + 1, p, sizeof(path) - 1);
            i.path = path;
        } else
            i.path = p;
    
        if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
            use_ssl = 0;
        if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
            if (!use_ssl)
                use_ssl = LCCSCF_USE_SSL;
    
        lwsl_debug("'%s' %p '%s' %p\n", i.address, i.address, i.path, i.path);
    
        /*
         * create the websockets context.  This tracks open connections and
         * knows how to route any traffic and which protocol version to use,
         * and if each connection is client or server side.
         *
         * For this client-only demo, we tell it to not listen on any port.
         */
        info.http_proxy_address = "proxy.XXX.com.cn";
        info.http_proxy_port = 80;
        info.port = CONTEXT_PORT_NO_LISTEN;
        info.protocols = protocols;
        info.gid = -1;
        info.uid = -1;
        info.ws_ping_pong_interval = pp_secs;
        info.extensions = exts;
    
    #if defined(LWS_WITH_TLS)
        info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
    #endif
    
        //wth0630
        use_ssl = LCCSCF_USE_SSL | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_EXPIRED| LCCSCF_ALLOW_SELFSIGNED;
        //
    
        if (use_ssl) {
            /*
             * If the server wants us to present a valid SSL client certificate
             * then we can set it up here.
             */
    
            if (cert_path[0])
                info.client_ssl_cert_filepath = cert_path;
            if (key_path[0])
                info.client_ssl_private_key_filepath = key_path;
    
            /*
             * A CA cert and CRL can be used to validate the cert send by the server
             */
            if (ca_path[0])
                info.client_ssl_ca_filepath = ca_path;
    
            printf("client_ssl_ca_filepath =%s \n",info.client_ssl_ca_filepath);
            
            info.client_ssl_cert_filepath =  "cloud.crt";
            info.client_ssl_private_key_filepath = "cloud.key.unsecure";
            info.client_ssl_ca_filepath ="ca.crt";
    
    
    #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
            else if (crl_path[0])
                lwsl_notice("WARNING, providing a CRL requires a CA cert!\n");
    #endif
        }
    
        if (use_ssl & LCCSCF_USE_SSL) {
            lwsl_notice(" Using SSL\n");
    #if defined(LWS_WITH_MBEDTLS)
            lwsl_notice("   (NOTE: mbedtls needs to be given the remote\n");
            lwsl_notice("    CA cert to trust (with -A) to validate it)\n");
    #endif
        }
        else
            lwsl_notice(" SSL disabled\n");
        if (use_ssl & LCCSCF_ALLOW_SELFSIGNED)
            lwsl_notice(" Selfsigned certs allowed\n");
        else
            lwsl_notice(" Cert must validate correctly (use -s to allow selfsigned)\n");
        if (use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)
            lwsl_notice(" Skipping peer cert hostname check\n");
        else
            lwsl_notice(" Requiring peer cert hostname matches\n");
    
        context = lws_create_context(&info);
        if (context == NULL) {
            fprintf(stderr, "Creating libwebsocket context failed\n");
            return 1;
        }
    
        i.context = context;
        i.ssl_connection = use_ssl;
        i.host = i.address;
        i.origin = i.address;
        i.ietf_version_or_minus_one = ietf_version;
    
        if (!strcmp(prot, "http") || !strcmp(prot, "https")) {
            lwsl_notice("using %s mode (non-ws)\n", prot);
            if (test_post) {
                i.method = "POST";
                lwsl_notice("POST mode\n");
            }
            else
                i.method = "GET";
            do_ws = 0;
        } else
            if (!strcmp(prot, "raw")) {
                i.method = "RAW";
                i.protocol = "lws-test-raw-client";
                lwsl_notice("using RAW mode connection\n");
                do_ws = 0;
            } else
                lwsl_notice("using %s mode (ws)\n", prot);
    
        /*
         * sit there servicing the websocket context to handle incoming
         * packets, and drawing random circles on the mirror protocol websocket
         *
         * nothing happens until the client websocket connection is
         * asynchronously established... calling lws_client_connect() only
         * instantiates the connection logically, lws_service() progresses it
         * asynchronously.
         */
    
        m = 0;
        while (!force_exit) {
    
            if (do_multi) {
                for (n = 0; n < (int)LWS_ARRAY_SIZE(wsi_multi); n++) {
                    if (!wsi_multi[n] && ratelimit_connects(&rl_multi[n], 2u)) {
                        lwsl_notice("dumb %d: connecting\n", n);
                        i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
                        i.pwsi = &wsi_multi[n];
                        lws_client_connect_via_info(&i);
                    }
                }
            } else {
    
                if (do_ws) {
                    if (!flag_echo && !justmirror && !wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
                        lwsl_notice("dumb: connecting\n");
                        i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
                        i.pwsi = &wsi_dumb;
                        lws_client_connect_via_info(&i);
                    }
    
                    if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
                        lwsl_notice("mirror: connecting\n");
                        i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
                        i.pwsi = &wsi_mirror;
                        wsi_mirror = lws_client_connect_via_info(&i);
                    }
                } else
                    if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
                        lwsl_notice("http: connecting\n");
                        i.pwsi = &wsi_dumb;
                        lws_client_connect_via_info(&i);
                    }
            }
    
            lws_service(context, 500);
    
            if (do_multi) {
                m++;
                if (m == 10) {
                    m = 0;
                    lwsl_notice("doing lws_callback_on_writable_all_protocol\n");
                    lws_callback_on_writable_all_protocol(context,
                           &protocols[PROTOCOL_DUMB_INCREMENT]);
                }
            }
    
            if (flag_echo && lws_now_secs() != last) {
                lwsl_notice("rxb %d, rx_count %d\n", rxb, rx_count);
                last = lws_now_secs();
            }
        }
    
        lwsl_err("Exiting\n");
        lws_context_destroy(context);
    
        return ret;
    
    usage:
        fprintf(stderr, "Usage: libwebsockets-test-client "
                    "<server address> [--port=<p>] "
                    "[--ssl] [-k] [-v <ver>] "
                    "[-d <log bitfield>] [-l]\n");
        return 1;
    }
  • 相关阅读:
    洛谷P2742 【模板】二维凸包
    计算几何笔记
    洛谷P1251 餐巾计划问题(最小费用最大流)
    洛谷P2762 太空飞行计划问题(最大权闭合图)
    洛谷P2764 最小路径覆盖问题(二分图)
    [置顶] Guava学习之ArrayListMultimap
    sphinx coreseek SetSortMode(SPH_SORT_ATTR_ASC, '') 对float 排序设置bug
    magento 修改 paypal order product name
    硬盘“坏了”怎么办
    能够兼容ViewPager的ScrollView
  • 原文地址:https://www.cnblogs.com/cnchengv/p/16433245.html
Copyright © 2020-2023  润新知