使用libcurl异步发送http请求

2023-05-16

在工作中需要完成一个工具,该工具主要的用途就是向指定的服务器和端口发送http请求,为了提高性能,采用多线程的方式进行,同时采用libcurl的异步形式。代码如下,在其中添加一些注释来记录写代码中遇到的问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <pthread.h>
#include <signal.h>

#include <curl/curl.h>

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

char globalQueryContext[100000][2048];//不要在函数中声明如此大的变量,否则容易在进入函数时发生core
string globalLogFile     = "access.log";
string globalHost        = "i3114.se.shyc2.qihoo.net";
string globalPort        = "6351";
string globalQueryFile   = "query.txt";
volatile bool globalStop = false;
static int  globalThreadNumber  = 3;

void sig_handle(int)
{
    globalStop = true;
}

struct thread_arg
{
    int  sequence;
    int  successRequest;
    int  totalQueryNumber;
    thread_arg():sequence(-1), successRequest(0), totalQueryNumber(0){}
};

size_t write_response(void *contents, size_t size, size_t nmemb, void *stream )
{
    string data((const char*) contents, (size_t) size * nmemb);
    *((stringstream*) stream) << data << endl;
    //cout << stream << endl;
    return size * nmemb;
}

char *getCompletedQuery(char* const completedQuery, const char *queryContext)
{
    if(completedQuery == NULL || queryContext == NULL)
        return NULL;

    completedQuery[0] = '\0';//注意初始化数组
    strcat(completedQuery, "http://");
    strcat(completedQuery, globalHost.c_str());//注意string 与 char*的区别以及相互转化
    strcat(completedQuery, ":");
    strcat(completedQuery, globalPort.c_str());
    strcat(completedQuery, "/mod_qsrch/warmup?kw=");
    strcat(completedQuery, queryContext);

    cout <<  string(completedQuery) << ":" << strlen(completedQuery) << endl;
    return completedQuery;
}

void setCurlEasy(CURL *curl, const char *completedQuery, stringstream& response)
{
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); 
    curl_easy_setopt(curl, CURLOPT_URL, completedQuery);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    return;
}

bool dealWithCurlCode(CURLcode code)
{
    if(code == CURLE_OK)
    {
        cout << "OK" << endl;
        return true;
    }
    else 
    {
        cout << string(curl_easy_strerror(code)) << endl;
        return false;
    }
}

bool asyncSetCurlEasy(const char queryContext[][2048], 
                      char completedQuerys[][2048],
                      stringstream responses[], 
                      int queryBeginPosition,
                      size_t queryNumber,
                      CURLM *curlm
                      )
{
    if(completedQuerys == NULL || responses == NULL || curlm == NULL)
        return false;

    for(size_t i = 0; !globalStop &&i < queryNumber; ++i)
    {
        CURL *curl = curl_easy_init(); 
        if(curl)
        {
            getCompletedQuery(completedQuerys[i], queryContext[queryBeginPosition + i]);        
            setCurlEasy(curl, completedQuerys[i], responses[i]);         
            curl_multi_add_handle(curlm, curl); 
        }
        else
            return false;
    }

    return true;
}

int asyncDealWithCurlCode(CURLM *curlm)
{
    if(curlm == NULL) 
        return false;

    int leftMsg = 0;
    int sucessCurl = 0;
    CURLMsg* msg = NULL;

    while(!globalStop && (msg = curl_multi_info_read(curlm, &leftMsg)) != NULL)
    {
        if(msg->msg == CURLMSG_DONE) 
        {
            sucessCurl++;
            int httpStatusCode = 0; 
            curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &httpStatusCode);
            char *effectiveUrl = NULL;
            curl_easy_getinfo(msg->easy_handle, CURLINFO_EFFECTIVE_URL, &effectiveUrl); 
            cout << "url: " << effectiveUrl << " status:  " << httpStatusCode << "  " 
                << curl_easy_strerror(msg->data.result)  << endl;         
            curl_multi_remove_handle(curlm, msg->easy_handle);
            curl_easy_cleanup(msg->easy_handle);
        }
        else  
            return sucessCurl;
    }

    return sucessCurl;
}

int asyncSendRequestAndGetResponse(const char queryContexts[][2048], 
                                   int queryBeginPosition, 
                                   size_t queryNumber)
{
    char completedQuerys[queryNumber][2048]; 
    stringstream responses[queryNumber]; 
    CURLM *curlm = curl_multi_init();
    //TODO 对curlm进行判断
    if(!asyncSetCurlEasy(queryContexts, completedQuerys, responses, queryBeginPosition, queryNumber, curlm))
    {
        cout << "asyncSetCurlEasy error"<< endl; 
        return 0;
    }

    int runningCurls = 0;
    do{
        curl_multi_wait(curlm, NULL, 0, 2000, NULL);  
        curl_multi_perform(curlm, &runningCurls);
    }while(runningCurls > 0 && !globalStop);

    int sucessRequest = asyncDealWithCurlCode(curlm);  
    curl_multi_cleanup(curlm); 

    return sucessRequest;
}

bool sendRequestAndGetResponse(const char* queryContext)
{
    CURL *curl = curl_easy_init();

    if(curl)
    {
        char completedQuery[2048] = {0};
        stringstream response;
        getCompletedQuery(completedQuery, queryContext);        
        setCurlEasy(curl, completedQuery, response); 

        CURLcode code = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        return dealWithCurlCode(code);
    }
    else
        return false;
}

int getQueryContext(const string& path, char queryContexts[][2048], int queryNumber)
{
    FILE* fd = fopen(path.c_str(), "r");
    if(!fd)
    {
        cout << "open file " << path << " failed!"<< endl;
        return 0;
    }

    int index = 0;
    //queryContexts[index] = {};
    while(!globalStop && index < queryNumber && fgets(queryContexts[index], 2048, fd) != NULL)
    {
        queryContexts[index][strlen(queryContexts[index]) - 1] = '\0';
        cout << "query" << index << ":" << queryContexts[index] << endl; 
        //queryContexts[++index] = "";
        index++;
    }

    fclose(fd);
    return index;
}

void *doWarmBySendQueryFormFile(void *arg)
{
    struct thread_arg *queryMsg = (thread_arg*)arg; 
    int averageQueryNumber = queryMsg->totalQueryNumber / globalThreadNumber;
    int queryBeginPosition = queryMsg->sequence * averageQueryNumber;
    if(queryMsg->sequence == globalThreadNumber - 1) 
        averageQueryNumber = queryMsg->totalQueryNumber - averageQueryNumber * queryMsg->sequence;
    cout << "thread " << queryMsg->sequence << " query begin position is " << queryBeginPosition << " query number is " << averageQueryNumber << endl;
    queryMsg->successRequest = asyncSendRequestAndGetResponse(globalQueryContext, queryBeginPosition, averageQueryNumber);

    return NULL;
}

void usage(const char* pname)
{
    cout << pname << 
           "-p port "
           "-h host "
           "-q input_file "
           "-l log_file "
           "-t thread_number" << endl;
}

void *time_worker(void *arg)
{
    pthread_detach(pthread_self());
    struct timespec delay;
    delay.tv_sec = 10 * 60;
    delay.tv_nsec = 0;

    sigset_t mask;
    sigfillset(&mask);
    sigfillset(&mask, SIGALRM);
    pthread_sigmask(SIG_BLOCK, &mask, NULL);//线程屏蔽信号

    nanosleep(%delay, NULL);
    globalStop = true;
    cout << "time to exit" << endl;

    return NULL;
}

bool parseParameters(int argc, char *argv[])
{
    int c;
    while((c = getopt(argc, argv, "h:p:q:l:t:")) != -1)
    {
        switch(c)
        {
            case 'p': 
                {
                    string port(optarg);
                    globalPort = port;
                }
                break;
            case 'h':
                {
                    string host(optarg);
                    globalHost = host;
                }
                break;
            case 'q':
                {
                    string queryFile(optarg);
                    globalQueryFile = queryFile;
                }
                break;
            case 'l':
                {
                    string logFile(optarg); 
                    globalLogFile = logFile;
                }
                break;
            case 't':
                globalThreadNumber = atoi(optarg); 
                break;
            default:
                usage(argv[0]);
                return false;
        } 
    }

    return true;
}

int main(int argc, char* argv[])
{
    CURLcode code = curl_global_init(CURL_GLOBAL_ALL); 
    if(code != CURLE_OK)
    {
        cout << "curl_global_init error" << endl; 
        exit(-1);
    }

    if(!parseParameters(argc, argv))
    {
        cout << "parse parameters error" << endl;
        exit(-1);
    }

    signal(SIGINT, sig_handle);//设置信号的处理函数
    signal(SIGTERM, sig_handle);

    int queryNumber = getQueryContext(globalQueryFile, globalQueryContext, 100000); 
    if(!queryNumber)
    {
        cout << "read query from file failed" << endl;
        exit(-1);
    }

    pthread_t *tids = new pthread_t[globalThreadNumber];
    if(tids == NULL)
    {
        cout << "new pthread failed" << endl;
        exit(-1);
    }

    thread_arg *thr_args = new thread_arg[globalThreadNumber];
    if(thr_args == NULL)
    {
        cout << "new pthread arg  failed"<< endl;
        exit(-1);
    }

    for(int i = 0; i < globalThreadNumber; ++i)
    {
        thr_args[i].totalQueryNumber = queryNumber;
        thr_args[i].sequence = i;
        if(pthread_create(&tids[i], NULL, doWarmBySendQueryFormFile, &thr_args[i]) != 0)    
        {
            cout << "create curl thread" << i << " error" << endl; 
            exit(-1);
        } 
    }

    thread_t twid;
    if(pthread_create(&twid, NULL, time_worker, NULL) != 0)
    {
        cout << "create time worker thread error"<< endl; 
        exit(-1);
    }

    for(int i = 0; i < globalThreadNumber; ++i)
    {
        pthread_join(tids[i], NULL);//主线程等待子线程执行完毕后退出
    }

    delete[] tids;
    delete[] thr_args;
    curl_global_cleanup();
    return 0;
}


写完上述代码,发现其中还是有一些问题:

  1. 代码的书写规范有问题
  2. 没有对请求成功和失败结果的统计
  3. 函数行数太多
  4. 可以将上述方法封装成一个类
  5. 在异步的同时也可以控制并发

后来通过改进就变成了下面的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <pthread.h>
#include <signal.h>

#include <curl/curl.h>

#include <iostream>
#include <vector>
#include <string>
#include <fstream>

using namespace std;

vector<string> globalCompletedQuerys;

string globalHost               = "localhost";
string globalPort               = "6351";
string globalQueryFile          = "query.txt";
volatile bool globalStop        = false;

static int  globalThreadNumber  = 3;
static int  globalConcurrence   = 1;
static long globalQueryNumber   = 1;

void sigHandle(int)
{
    globalStop = true;
    cout << "stop "<< endl;
}

class statisticsRequest
{
    private:
        int failedRequest;
        int successRequest;
    public:
        statisticsRequest()
        {
            failedRequest = 0;
            successRequest = 0; 
        }
        void increaseFailedRequest(){ failedRequest++; }
        void increaseSucessRequest(){ successRequest++; }
        int getFailedRequest(){ return failedRequest; }
        int getSuccessRequest(){ return successRequest; }
};

struct threadArg
{
    int  sequence;
    int  concurrence;
    int  totalQueryNumber;
    statisticsRequest * statsReq;
    threadArg():sequence(-1), concurrence(1), totalQueryNumber(0), statsReq(NULL){}
};

size_t writeResponse(void *contents, size_t size, size_t nmemb, void *stream)
{
    string data((const char *)contents, (size_t)size * nmemb);
    statisticsRequest *request = (statisticsRequest *)stream;

    if(data.substr(0, 9) == "rsp_ec: 0")
    {
        request->increaseSucessRequest();
    }
    else if(data.substr(0, 6) == "rsp_ec" && data.substr(0, 9) != "rsp_ec: 0")
    {
        request->increaseFailedRequest();
    }

    return size * nmemb;
}

void getCompletedQuery(const string& queryContext, string& completedQuery)
{

    completedQuery.clear();//注意初始化数组
    completedQuery += "http://";
    completedQuery +=  globalHost;
    completedQuery += ":";
    completedQuery += globalPort;
    completedQuery += "/mod_qsrch/warmup?kw=";
    completedQuery +=  queryContext;

    return;
}

void setCurlEasy(CURL *curl, const string &completedQuery, statisticsRequest *response)
{
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); 
    curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
    curl_easy_setopt(curl, CURLOPT_URL, completedQuery.c_str());
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, writeResponse);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)response);

    return;
}

bool asyncSetCurlEasy(const vector<string>& completedQuerys, 
                      int queryBeginPosition,
                      size_t queryNumber,
                      CURLM *curlm,
                      statisticsRequest* statsReq)
{
    if(completedQuerys.empty() || statsReq == NULL || curlm == NULL)
        return false;

    for(size_t i = 0; !globalStop && i < queryNumber; ++i)
    {
        CURL *curl = curl_easy_init(); 
        if(curl)
        {
            setCurlEasy(curl, completedQuerys[queryBeginPosition + i], statsReq);
            curl_multi_add_handle(curlm, curl); 
        }
        else
            return false;
    }

    return true;
}

void asyncDealWithCurlCode(CURLM *curlm)
{
    if(curlm == NULL) 
        return;

    int leftMsg = 0;
    CURLMsg* msg = NULL;

    while(!globalStop && (msg = curl_multi_info_read(curlm, &leftMsg)) != NULL)
    {
        if(msg->msg == CURLMSG_DONE) 
        {
            int httpStatusCode = 0; 
            curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &httpStatusCode);

            char *effectiveUrl = NULL;
            curl_easy_getinfo(msg->easy_handle, CURLINFO_EFFECTIVE_URL, &effectiveUrl); 
            //cout << "url: " << effectiveUrl << " status:  " << httpStatusCode << "  " << curl_easy_strerror(msg->data.result)  << endl;         
        }
    }

    return;
}

void splitQuery(const vector<string> &queryContexts, vector<string> &completedQuerys)
{
    for(vector<string>::const_iterator it = queryContexts.begin(); it !=queryContexts.end(); ++it)
    {
        completedQuerys.push_back("");
        getCompletedQuery(*it,completedQuerys.back());  
    }

}

void asyncSendRequestAndGetResponse(const vector<string>& completedQuerys, 
                                   int sequence,
                                   int queryBeginPosition, 
                                   int concur,
                                   int queryNumber,
                                   statisticsRequest *statsReq)
{
    CURLM *curlm = curl_multi_init();
    if(curlm == NULL) 
    {
        cout << "init curl multi failed" << endl; 
        return;
    }

    if(concur > queryNumber)
        concur = queryNumber;

    int runningCurls = 0, queryStart = queryBeginPosition; 
    int queryEndPosition = queryBeginPosition + queryNumber;
    int sendedRequest = queryBeginPosition - queryStart;
    int receivedResponse = statsReq->getFailedRequest() + statsReq->getSuccessRequest();

    asyncSetCurlEasy(completedQuerys, queryBeginPosition, concur, curlm, statsReq);
    queryBeginPosition += concur;

    do{
        curl_multi_perform(curlm, &runningCurls);
        asyncDealWithCurlCode(curlm); 

        sendedRequest = queryBeginPosition - queryStart;
        receivedResponse = statsReq->getFailedRequest() + statsReq->getSuccessRequest();

        if(queryBeginPosition != queryEndPosition && sendedRequest - receivedResponse < concur)
        {
            int curlNumberToAdd = concur - (sendedRequest - receivedResponse);

            if(queryBeginPosition + curlNumberToAdd < queryEndPosition)
            {
                asyncSetCurlEasy(completedQuerys, queryBeginPosition, curlNumberToAdd, curlm, statsReq);
                queryBeginPosition += curlNumberToAdd;
            }
            else if(queryBeginPosition < queryEndPosition)
            {
                asyncSetCurlEasy(completedQuerys, queryBeginPosition, queryEndPosition - queryBeginPosition, curlm, statsReq);
                queryBeginPosition += queryEndPosition - queryBeginPosition;
            }
        }

        curl_multi_wait(curlm, NULL, 0, 200000, NULL);  

        //cout << pthread_self() << " receivedResponse: " << receivedResponse << "queryNumber :" << queryNumber << endl;  
    }while(receivedResponse < queryNumber && !globalStop);

    curl_multi_cleanup(curlm); 

    return;
}

int getQueryContext(const string& path, vector<string>& queryContexts, int num)
{
    int index = 0;
    string line;
    ifstream in(path.c_str());
    while(!globalStop && index < num && (in >> line)){
        if(line.empty())
            continue;
        queryContexts.push_back(line);
        index++;
    }
    in.close();

    return index;
}

void *doWarmBySendQueryFormFile(void *arg)
{
    struct threadArg *queryMsg = (threadArg*)arg; 
    int averageQueryNumber = queryMsg->totalQueryNumber / globalThreadNumber;
    int queryBeginPosition = queryMsg->sequence * averageQueryNumber;
    if(queryMsg->sequence == globalThreadNumber - 1) 
        averageQueryNumber = queryMsg->totalQueryNumber - averageQueryNumber * queryMsg->sequence;
    //cout << "thread " << queryMsg->sequence << " query begin position is " << queryBeginPosition << " query number is " << averageQueryNumber << "concurrence is " << queryMsg->concurrence << endl;
     asyncSendRequestAndGetResponse(globalCompletedQuerys, queryMsg->sequence, queryBeginPosition, queryMsg->concurrence,  averageQueryNumber, queryMsg->statsReq);

    return NULL;
}

void usage(const char* pname)
{
    cout << pname << 
           "-p port "
           "-h host "
           "-i input_file "
           "-t thread_number" << endl;
}

void *timeWorker(void *arg)
{
    pthread_detach(pthread_self());
    struct timespec delay;
    delay.tv_sec = 10 * 60;
    delay.tv_nsec = 0;

    sigset_t mask;
    sigfillset(&mask);
    sigdelset(&mask, SIGALRM);
    pthread_sigmask(SIG_BLOCK, &mask, NULL);

    nanosleep(&delay, NULL);
    globalStop = true;
    cout << "time to exit" << endl;

    return NULL;
}

bool parseParameters(int argc, char *argv[])
{
    int c;
    while((c = getopt(argc, argv, "h:p:i:t:n:c:")) != -1)
    {
        switch(c)
        {
            case 'p': 
                {
                    string port(optarg);
                    globalPort = port;
                }
                break;
            case 'h':
                {
                    string host(optarg);
                    globalHost = host;
                }
                break;
            case 'i':
                {
                    string queryFile(optarg);
                    globalQueryFile = queryFile;
                }
                break;
            case 't':
                globalThreadNumber = atoi(optarg); 
                break;
            case 'n':
                globalQueryNumber = atoi(optarg);
                break;
            case 'c':
                globalConcurrence = atoi(optarg);
                break;
            default:
                usage(argv[0]);
                return false;
        } 
    }

    return true;
}

void getStatisticsRequest(statisticsRequest statsReqs[], int *totalSuccessRequest, int *totalFailedRequest)
{
    for(int i = 0; i < globalThreadNumber; i++)
    {
        *totalSuccessRequest += statsReqs[i].getSuccessRequest();
        *totalFailedRequest  += statsReqs[i].getFailedRequest(); 
    }
}

int main(int argc, char* argv[])
{
    CURLcode code = curl_global_init(CURL_GLOBAL_ALL); 
    if(code != CURLE_OK)
    {
        cout << "curl_global_init error" << endl; 
        exit(-1);
    }

    if(!parseParameters(argc, argv))
    {
        cout << "parse parameters error" << endl;
        exit(-1);
    }

    signal(SIGINT, sigHandle);
    signal(SIGTERM, sigHandle);

    vector<string> queryContexts;
    int queryNumber = getQueryContext(globalQueryFile, queryContexts, globalQueryNumber); 
    if(!queryNumber)
    {
        cout << "read query from file failed" << endl;
        exit(-1);
    }
    splitQuery(queryContexts, globalCompletedQuerys);

    pthread_t *tids = new pthread_t[globalThreadNumber];
    if(tids == NULL)
    {
        cout << "new pthread failed" << endl;
        exit(-1);
    }

    threadArg *thr_args = new threadArg[globalThreadNumber]();
    if(thr_args == NULL)
    {
        cout << "new pthread arg  failed"<< endl;
        exit(-1);
    }

    statisticsRequest *statsReqs = new statisticsRequest[globalThreadNumber]();
    if(statsReqs == NULL) 
    {
        cout << "new statsReqs arg  failed"<< endl;
        exit(-1);
    }

    for(int i = 0; i < globalThreadNumber; ++i)
    {
        thr_args[i].sequence = i;
        thr_args[i].statsReq = &statsReqs[i];
        thr_args[i].concurrence = globalConcurrence;
        thr_args[i].totalQueryNumber = globalQueryNumber;

        if(pthread_create(&tids[i], NULL, doWarmBySendQueryFormFile, &thr_args[i]) != 0)    
        {
            cout << "create curl thread" << i << " error" << endl; 
            exit(-1);
        } 
    }

    pthread_t twid;
    if(pthread_create(&twid, NULL, timeWorker, NULL) != 0)
    {
        cout << "create time worker thread error"<< endl; 
        exit(-1);
    }

    for(int i = 0; i < globalThreadNumber; ++i)
    {
        pthread_join(tids[i], NULL);
    }

    int totalSuccessRequest = 0;
    int totalFailedRequest  = 0;
    getStatisticsRequest(statsReqs, &totalSuccessRequest, &totalFailedRequest);
    cout << "total request : " << totalSuccessRequest + totalFailedRequest << endl
         << "totalSuccessRequest is " << totalSuccessRequest << endl
         << "totalFailedRequest is " << totalFailedRequest << endl;

    delete[] statsReqs; 
    delete[] thr_args;
    delete[] tids;

    curl_global_cleanup();
    return 0;
}

但是上述代码并没有对前面提出的问题进行特别的改进,只是改进了控制并发的问题,而且并不是特别完美,最完美的方式是在其中使用nanosleep函数来控制时间,所以还需要进行改进。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用libcurl异步发送http请求 的相关文章

  • idea中maven报错Cannot reconnect

    问题所在 xff1a Maven工程 错误处在pom文件 xff1a 最重要的是学会了怎样在Idea出现错误时 xff0c 发现导致错误的具体原因 xff0c 通过 Helper gt Show Log in Explorer xff0c
  • 用一份JAVA工程师的求职简历来说说求职简历怎么写

    这是一篇我比较想看到的简历指导的文章 但是我比较反对简历造假 我觉得会什么写什么把 可以先看看我的这篇文章 xff1a 刚实习结束 xff0c 就要做面试官 xff0c 谈谈我的想法 自己做过面试官以后 xff0c 自己也总结了一下 xff
  • LiveGBS/LiveNVR等实现安防监控视频Web无插件直播时如何叠加水印文字

    H5直播点播播放器 下载集成入口 xff1a https www liveqing com docs download LivePlayer html 使用说明 xff1a https www liveqing com docs manua
  • OpenMV4开发笔记3-串口通信

    OpenMV4引出了串口3和串口1 xff0c 首先以串口3的收发为例 span class token keyword import span time span class token keyword from span pyb spa
  • FreeRTOS与UCOSIII任务状态对比

    FreeRTOS任务状态 1 运行态 正在运行的任务 xff0c 正在使用处理器的任务 单核处理器中任何时候都有且只有一个任务处于运行态 2 就绪态 已经准备就绪 xff08 非阻塞或挂起 xff09 xff0c 可以立即运行但还没有运行的
  • 白话TCP/IP协议栈

    前言 最近在复习总结计算机基础知识 xff0c 包括操作系统 数据结构 计算机网络等程序员必备的知识 xff0c 这属于程序员的内功 把内功修炼好了 xff0c 外功只是一种形式 xff0c 如果你内功深厚 xff0c 那么无论是用龙抓手还
  • 飞机绕地球问题

    每个飞机只有一个油箱 xff0c 飞机之间可以相互加油 xff08 注意是相互 xff0c 没有加油机 xff09 xff0c 一箱油可以供一架飞机绕地球飞半圈 问 xff1a 为了使至少一架飞机绕地球一圈回到起飞 时候的飞机场 xff0c
  • 【ROS】Gazebo仿真平台安装及问题解决

    Gazebo安装 这里的ROS版本是Melodic xff0c 如果是其他版本的ROS可以修改下面命令的melodic为指定版本 sudo apt get install ros melodic gazebo ros pkgs ros me
  • 激光slam经典开源算法及论文整理

    开源算法 loamLeGO LOAMlio mappingLIO SAMCartographergmappinghector slam 考虑到有些朋友们的网络下载论文可能有问题 xff0c 把论文整理到百度网盘 xff0c 可自行下载 xf
  • Body系下空间平面如何转到World系下

    Body系下空间平面如何转到World系下 题目解法 题目 已知 传感器坐标系 xff08 Body系 xff09 下有一平面P方程为Ax 43 By 43 Cz 43 D 61 0 xff0c 简写为 n
  • cmake: symbol lookup error: cmake: undefined symbol: archive_write_add_filter_zstd 两种解决方法

    centOS8 x86 64 或 aarch64 系统下 yum或dnf 默认安装的 cmake 3 18 2 11 el8版本 xff0c 安装后无法使用 xff0c 出现 xff1a cmake symbol lookup error
  • ROS学习--轻松使用tf

    tf是ROS中建立坐标系 xff0c 并且使用各个坐标间转换关系的一个很好的工具 xff0c 对于非导航专业的同学 xff0c 常常苦恼与各种旋转矩阵的变换 xff0c 自己经常被搞的头大 xff0c 最近由于课题实验的需要 xff0c 尝
  • ROS 下navigation/robot_pose_ekf编译报错

    想要使用navigation下的robot pose ekf做IMU与视觉的融合于是找到了这个包 xff1a https github com ros planning navigation tree indigo devel 但是编译报错
  • 【学习日记】ROS下IMU使用困惑

    最近准备在机器人上测试使用IMU代替机器人的里程计 xff0c 以提高底层的控制周期 由于底层通信的原因 xff0c 使用里程计的话最多能到50HZ xff0c 因为我们的机器人对运动性能要求较高 xff0c 所以无法满足我们的要求 xff
  • ROS学习--如何结合launch文件使用参数服务器

    ROS xff08 Robot Operating System xff09 接触了将近两年了 xff0c 最常用的也就是发布话题与订阅话题 xff0c 前一段时间刚刚把Rviz与 tf搞明白一些 xff0c 都能够多掌握一些东西 xff0
  • 【ROS工具学习】之topic_tools/throttle,改变节点发布频率

    最近老师想做一个实验 xff0c 机器人上搭在Hokuyo单线激光雷达 xff0c Velodyne16线激光雷达 xff0c Kinect2 xff0c Bumblebee xb3等传感器 xff0c 这些传感器做一些实验 xff0c 因
  • 【ROS工具学习】之message_filters:消息同步

    最近实验室老师在做一个多传感器数据采集实验 xff0c 涉及到了消息同步 所以就学习了ROS官网下的消息同步工具message filters http wiki ros org message filters 消息同步有两种方式 xff0
  • ROS中map与costmap的topic数据格式定义

    map与costmap都是以nav msgs OccupancyGrid类型发布其topic 其中整张地图的障碍物信息存放在data数据成员中 xff0c data是一个int8类型的vector向量 xff0c 即一维数组 假设一张pgm
  • Ubuntu 16.04 Qt clang-format 插件安装使用教程

    Ubuntu 16 04 Qt clang format 插件安装使用教程 Qt安装下载安装修改qt环境变量 LLVM安装安装clang format配置qt打开工程文件配置clang format Qt安装 最新的qt5 12支持保存代码
  • HTTP请求首部——Authorization

    前几天的任务需要用到Authorization认证 xff0c 任务比较急 xff0c 就照着给的例子写好了 xff0c 现在任务结束了 xff0c 还是来了解一下这个Authorization Authorization 是一个HTTP安

随机推荐

  • 如何真正理解用户标签体系?

    对用户标签的理解不够透彻 xff1f 用户标签体系创建的方法论总是三头两绪 xff1f 具体业务场景中 xff0c 经常找不到数据分析的思路 xff1f 本文根据神策数据业务咨询师钟秉哲以 构建用户标签体系 xff0c 助力企业精细化运营
  • ubuntu设置tightvncserver自动启动

    vi etc init d vnc bin bash PATH 61 34 PATH usr bin 34 export USER 61 34 root 34 DISPLAY 61 34 1 34 DEPTH 61 34 24 34 GEO
  • 毕业设计小车搭建(1)测试思岚A1雷达数据

    采用的思岚A1型号的雷达 ubuntu系统上采集雷达数据并rviz显示 主要是根据官网给的教程步骤一步一步走下来的 思岚激光雷达 首先下载对应的官方功能包GitHub Slamtec rplidar ros 功能包创建结束后注意环境变量写入
  • 如何关闭docker容器里的进程

    如何关闭docker容器里的进程 1 使用docker exec 容器名 ps ef命令查看进程信息 示例 xff1a 创建名为 34 redis 34 的容器 xff0c 并在容器内部和宿主机中查看容器中的进程信息 xff1a 2 然后进
  • 浅谈嵌入式与互联网(详细)

    纲要 一 什么叫嵌入式 xff0c 以及与人工智能的关系 xff1f 二 嵌入式岗位 三 浅谈嵌入式开发优缺点 四 与互联网 CS相关的 xff0c 如平台服务器 xff0c 前端 APP 软件 对比 五 能力要求和薪资 参考知乎 以下均采
  • 那一年读过的技术经典书

    转载请注明 xff1a http blog csdn net xinzhangyanxiang article details 10199757 大学刚毕业 xff0c 总结起来读过的书并不算多 xff0c 而且主要集中在大四的时期读的 x
  • 关于Ubuntu的串口链接上但接收不了数据问题

    作为开始小白的我 xff0c 一开始链接串口以为按装了CuteCom就能使用 xff0c 不知道使用串口前是需要打开权限的 xff0c 所以我在CuteCom的时候链接上但收不了数据 xff0c 后来才知道打开权限 首先第一步 1 打开你的
  • 进程的调用

    每个进程都有一个非负整数的唯一ID xff0c 用pid t结构表示其ID xff0c 其中ID为0的是调度进程 xff0c 常被称为交换进程 是内核的一部分为系统进程 xff0c ID为1的是init进程 xff0c 他是一个普通用户进程
  • Oracle VM VirtualBox UUID already exists 问题解决

    当我们在VirtualBox下装完系统 xff0c 想拷贝一份备用的时候 xff0c 导入备份的虚拟磁盘的会提示VirtualBox UUID already exists问题 xff0c 其实这个问题在网络上早就有各种不同的解决方案了 x
  • Mac下将文件复制到移动硬盘

    现象分析 xff1a 如果你在使用Mac系统时 xff0c 发现Mac系统和移动硬盘之间无法拷贝数据 xff0c 很有可能你的移动硬盘是NTFS格式的 xff0c 因为目前苹果系统的硬盘格式暂时不兼容这样的格式拷贝 xff0c 只能从NTF
  • Maven打包时报Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.2:war解决方案

    问题现象 xff1a 用Maven打包时 xff0c 报Failed to execute goal org apache maven plugins maven war plugin 2 2 war错误 原因分析 xff1a 打包时在We
  • mac系统中怎么把显示在桌面上的磁盘图标取消掉?

    问题现象 xff1a 安装一些软件时 xff0c 桌面上总会出现外置磁盘图标如图1 下面就简单介绍下怎样取消这种外置图标 图1 解决方案 xff1a finder xff0d 偏好设置 xff0d 通用 xff0d 外置设置 取消前面对话框
  • tensorflow深度学习实战笔记(三):使用tensorflow lite把训练好的模型移植到手机端,编译成apk文件

    目录 一 准备工作 1 1模型训练 1 2模型固化和pb转tflite 1 3下载tensorflow源码 1 4安装android studio 二 在Android studio中进行开发 2 1修改app的build gradle文件
  • nvidia和cuda对应以及cuda和cuDNN对应版本

  • 修改spring Boot启动时的默认图案Banner

    一 修改Banner spring Boot启动的时候会有一个默认的启动图案 如下图 39 39 39 39 96 39 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
  • Java线程池自学手册Executor的使用

    准备做一个系列文章 xff0c 将零散的知识整理起来分享给大家 xff0c 希望给大家的工作和学习带来帮助 目录 1 Executor 2 ExecutorService 3 Executors 4 ThreadPoolExecutor 5
  • 常用开发资源整理(更新日:2017/4/26)

    说明 xff1a 为了方便 xff0c 今后将工作中用到一些常用的资源链接进行整理 xff0c 初衷是想发些各版本的冷资源 xff0c 免得在需要的时候花大量时间寻找 一 开发语言 1 Spring各版本压缩包下载 http repo sp
  • 博客地址迁移www.xiangquba.cn

    大家好 xff0c 非常感谢大家一直以来对我的关注 xff0c 博客有一年时间没有更新了 xff0c 其实我并没有停止分享 xff0c 只是这一年时间并没有在csdn上更新 xff0c 原因是因为起初csdn有一个自己给csdn发送短信绑定
  • FreeRTOS 任务函数里面的死循环

    任务函数是一个无限循环且不带返回值的函数 任务必须是死循环 xff0c 否则任务将经过 LR 返回 xff0c 如果 LR 指向了非法内存就会产生HardFault Handler xff0c 而 FreeRTOS 指向一个死循环 xff0
  • 使用libcurl异步发送http请求

    在工作中需要完成一个工具 xff0c 该工具主要的用途就是向指定的服务器和端口发送http请求 xff0c 为了提高性能 xff0c 采用多线程的方式进行 xff0c 同时采用libcurl的异步形式 代码如下 xff0c 在其中添加一些注