0

我正在尝试使用 libcurl 实现 Ntrip 客户端,因为我需要 HTTPS (TLS) 支持。Ntrip 客户端使用 HTTP(S)/GET,但有点不同,它可以定期向服务器发送 GPS/NMEA 信息,以便服务器可以将相关信息流式传输回来。所有这些都是为了提高效率而使用相同的 HTTP 会话。

我目前遇到的问题是,当我在当前设置中使用“curl_easy_send”时,libcurl 不喜欢它。如果我想使用“curl_easy_send”,我会失去 curl 的所有 HTTP 功能。似乎是全有或全无。请让我知道我必须将这些自定义选项添加到 HTTP/GET。谢谢

这是我到目前为止所拥有的:

#include <getopt.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <curl/curl.h>

#define VERSION         "1.0.0"
#define NTRIP_VER_STR   "Ntrip-Version: Ntrip/2.0"
#define USER_AGENT_STR  "User-Agent: NTRIP NtripClient/0.1"
#define PROTO_HTTP      "http://"
#define PROTO_HTTPS     "https://"
#define MAX_DATA_SIZE   1000

#ifdef DEBUG
  #define IS_VERBOSE 1
#else
  #define IS_VERBOSE 0
#endif


typedef struct
{
    const char *server;
    const char *mountpoint;
    const char *port;
    const char *user;
    const char *password;
    const char *nmea;
    bool use_tls;
} Args;


static void help()
{
    fprintf(stderr, "Version " VERSION "\n"
            "Usage: ntripclient [OPTIONS] ...\n"
            " -s  the server name or address\n"
            " -m  the requested data set or sourcetable filtering criteria\n"
            " -r  the server port number (default 2101)\n"
            " -u  the user name\n"
            " -p  the login password\n"
            " -n  NMEA string for sending to server\n"
            " -T  Use TLS (HTTPS)\n");
    exit(1);
}

static int parse_args(int argc, char **argv, Args *args)
{
    int opt = 0;
    memset(args, 0, sizeof(Args));

    do
    {
        switch((opt = getopt(argc, argv, "s:m:r:u:p:n:T")))
        {
        case 's': args->server = optarg;       break;
        case 'm': args->mountpoint = optarg;   break;
        case 'r': args->port = optarg;         break;
        case 'u': args->user = optarg;         break;
        case 'p': args->password = optarg;     break;
        case 'n': args->nmea = optarg;         break;
        case 'T': args->use_tls = true;        break;
        case  -1: /* done */                   break;
        default : help();                      break;
        }
    } while (opt != -1);

    // Make sure port is a number
    if (atoi(args->port) == 0)
        help();

    return 0;
}

static int init()
{
    setbuf(stdout, 0);
    setbuf(stdin, 0);
    setbuf(stderr, 0);

    // setup reading from stdin to non-blocking
    int flags = fcntl(0, F_GETFL);
    int ret = fcntl(0, F_SETFL, flags | O_NONBLOCK);
    return ret;
}

static bool valid_header(char *buffer, char *expect, int peek_len)
{
    if (strncmp(buffer, expect, peek_len) == 0)
    {
        return strncmp(buffer, expect, strlen(expect)) == 0;
    }
    return true;  // ignore
}

static size_t cb_header(char *buffer, size_t size, size_t count, void *userdata)
{
    (void)userdata;  // not used

    if (valid_header(buffer, "HTTP/1.1 200 OK\r\n",          5) &&
        valid_header(buffer, "Content-Type: gnss/data\r\n",  13) &&
        valid_header(buffer, "Ntrip-Version: Ntrip/2.0\r\n", 14))
    {
        return size * count;
    }
    return 0; // report error
}

static size_t cb_body_data(void *ptr, size_t size, size_t count, void *userdata)
{
    CURL *curl = (CURL*)userdata;
    size_t len = size * count;

    // Write data to standard out
    fwrite(ptr, len, 1, stdout);

    // TODO: send GGA data at 1Hz
    //size_t bytes_sent;
    //curl_easy_send(curl, "testing\r\n", 9, &bytes_sent);
    return len;
}

static int run(const Args *args)
{
    int ret = -1;
    CURL *curl;

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl)
    {
        CURLcode res;

        // create url string
        char url[1024];
        const char *proto = args->use_tls ? PROTO_HTTPS : PROTO_HTTP;
        snprintf(url, sizeof(url), "%s%s/%s", proto, args->server, args->mountpoint);
        url[sizeof(url)-1] = 0;

        // Set CURL options
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_PORT, atoi(args->port));
        curl_easy_setopt(curl, CURLOPT_USERNAME, args->user);
        curl_easy_setopt(curl, CURLOPT_PASSWORD, args->password);
        curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
        curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, cb_header);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb_body_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)curl);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, IS_VERBOSE);

        if (args->use_tls)
        {
            curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_3);
        }

        // Set NTRIP header fields
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, NTRIP_VER_STR);
        headers = curl_slist_append(headers, USER_AGENT_STR);

        if (args->nmea != NULL)
        {
            char nmea[500];
            snprintf(nmea, sizeof(nmea), "Ntrip-GGA: %s", args->nmea);
            nmea[sizeof(nmea)-1] = 0;
            headers = curl_slist_append(headers, nmea);
        }

        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        // Run CURL
        res = curl_easy_perform(curl);
        if (res != CURLE_OK)
        {
            fprintf(stderr, "failed: %s\n", curl_easy_strerror(res));
        }
        else
        {
            ret = 0;
        }

        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
    return ret;
}

int main(int argc, char **argv)
{
    Args args;
    int ret = parse_args(argc, argv, &args);

    if (ret == 0)
        ret = init();

    if (ret == 0)
        ret = run(&args);

    return ret;
}
4

0 回答 0