使用libcurl遇到的问题

随手记录

问题1:对某个url发送http get之后,数据量太大,会导致数据返回多次,如何知道已经完全返回呢?

答:首先,如果是easy接口的话,因为是同步接口,所以curl_easy_perform之后,就是接收完数据了,这时,可以用下述代码获取收到的数据长度。

double vl_Info = 0;
vl_ErrCode = curl_easy_getinfo(pl_Curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &vl_Info);

如果是用multi接口的话。愚蠢地以为,也是首先接收头部数据,获取其content length字段的值,知道包长度,然后每次有数据返回时判断获取到的数据总大小是否等于这个长度值,提醒一下,虽然不做动态网页,如果是动态网页则是一直获取,直到包长度为0才为结束。

实际操作中,multi接口需要不断调用curl_multi_perform,之后通过CURLMsg结构中的返回值判断是否操作完成。


问题2:我使用multi接口时,每次想并发get数据前,调用curl_multi_fdset,第五个参数max_fd总是返回-1,无法select。

答:原来mail list有提到这个问题,调用curl_multi_fdset之后应该调用curl_multi_timeout,获取一个等待时间,如果可以select的话就按这个时间去select,否则重新调用curl_multi_perform。

int MultiSelect(void)
{
    int ret = 0;

    struct timeval timeout_tv;
    fd_set  fd_read;
    fd_set  fd_write;
    fd_set  fd_except;
    int     max_fd = -1;

    // 注意这里一定要清空fdset,curl_multi_fdset不会执行fdset的清空操作  //
    FD_ZERO(&fd_read);
    FD_ZERO(&fd_write);
    FD_ZERO(&fd_except);

    // 设置select超时时间  //
    timeout_tv.tv_sec = 0;
    timeout_tv.tv_usec = 500 * 1000;

    long curl_timeo = -1;
    ret = curl_multi_timeout(m_pMultiHandle, &curl_timeo); // curl_timeo 单位是毫秒
    X_ASSERT(ret == 0);
    S_TRACE("Timeout: %d\n", curl_timeo);
    if (curl_timeo >= 0) {
        timeout_tv.tv_sec = curl_timeo / 1000;
        if (timeout_tv.tv_sec > 1)
            timeout_tv.tv_sec = 1;
        else
            timeout_tv.tv_usec = (curl_timeo % 1000) * 1000;
    }

    // 获取multi curl需要监听的文件描述符集合 fd_set //
    ret = curl_multi_fdset(m_pMultiHandle, &fd_read, &fd_write, &fd_except, &max_fd);
    X_ASSERT(ret == 0);

    /**
    * When max_fd returns with -1,
    * you need to wait a while and then proceed and call curl_multi_perform anyway.
    * How long to wait? I would suggest 100 milliseconds at least,
    * but you may want to test it out in your own particular conditions to find a suitable value.
    */
    if (-1 == max_fd) {
        S_TRACE("NO FD?\n");
        //MC_X_SYS_US_SLEEP(curl_timeo * 1000);
        Sleep(curl_timeo);
        return -2;
    }

    /**
    * 执行监听,当文件描述符状态发生改变的时候返回
    * 返回0,程序调用curl_multi_perform通知curl执行相应操作
    * 返回-1,表示select错误
    * 注意:即使select超时也需要返回0,具体可以去官网看文档说明
    */
    S_TRACE("Try Select\n");
    int ret_code = ::select(max_fd + 1, &fd_read, &fd_write, &fd_except, &timeout_tv);
    switch (ret_code) {
    case -1:
        /* select error */
        ret = -1;
        break;
    case 0:
        /* select timeout */
    default:
        /* one or more of curl's file descriptors say there's data to read or write*/
        ret = 0;
        break;
    }

    return ret;
}


// PERFORM逻辑如下:
        int vl_RunningHandles;
	while (CURLM_CALL_MULTI_PERFORM
		== curl_multi_perform(m_pMultiHandle, &vl_RunningHandles)) {
		S_TRACE("Running Handles: %d\n", vl_RunningHandles);
	}

	while (vl_RunningHandles) {
		int vl_Ret = MultiSelect();

		if (-2 == vl_Ret) {
			// curl_multi_fdset返回没有fd可以select
			while (CURLM_CALL_MULTI_PERFORM 
				== curl_multi_perform(m_pMultiHandle, &vl_RunningHandles)) {
				S_TRACE("Running Handles: %d\n", vl_RunningHandles);
			}
		}
		else if (-1 == vl_Ret) {
			S_TRACE("select error\n");
			break;
		}
		else {
			// select监听到事件,调用curl_multi_perform通知curl执行相应的操作 //
			while (CURLM_CALL_MULTI_PERFORM
				== curl_multi_perform(m_pMultiHandle, &vl_RunningHandles)) {
				S_TRACE("select: Running Handles: %d\n", vl_RunningHandles);
			}
		}

		S_TRACE("select: Running Handles: %d\n", vl_RunningHandles);
	}

问题3:把select换成libcurl的新接口,更爽快?

int myc_http_client::MultiWait(void)
{
	int ret = 0;

	struct timeval timeout_tv;

	// 设置select超时时间  //
	timeout_tv.tv_sec = 0;
	timeout_tv.tv_usec = 500 * 1000;

	long curl_timeo = -1;
	ret = curl_multi_timeout(m_pMultiHandle, &curl_timeo); // curl_timeo 单位是毫秒
	X_ASSERT(ret == 0);
	//S_TRACE("Timeout: %d\n", curl_timeo);
	if (curl_timeo >= 0) {
		timeout_tv.tv_sec = curl_timeo / 1000;
		if (timeout_tv.tv_sec > 1)
			timeout_tv.tv_sec = 1;
		else
			timeout_tv.tv_usec = (curl_timeo % 1000) * 1000;
	}

	ret = curl_multi_wait(m_pMultiHandle, NULL, 0, curl_timeo, NULL);
	X_ASSERT(ret == 0);

	return ret;
}

上述的等待接口被我换成这样,事实就是直接不会出现上述的问题2,而且libcurl也建议用新接口。

问题4:不断向同一个url发请求数据后,请求会失败,该如何处理?

答:查看返回代码是超时问题,只需重传,如果是CURLE_COULDNT_CONNECT,则需要重连了。现在并发大概每秒向两百多个不同url,发送两百个get请求,代码已经趋于稳定,以后可能要使用socket_action接口。




你可能感兴趣的:(网络编程)