有一段调用libcurl去访问POST接口的代码
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
/* complete within 20 seconds */
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 20L);
res = curl_easy_perform(curl_handle);
在win32窗口程序下使用是正常的。但是把同样的代码挪到控制台程序就不正常了。打开libcurl的日志,发现
- STATE: DO => DO_DONE handle 0xe73570; line 1695 (connection #0)
- STATE: DO_DONE => WAITPERFORM handle 0xe73570; line 1822 (connection #0)
- STATE: WAITPERFORM => PERFORM handle 0xe73570; line 1837 (connection #0)
- HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 100 Continue
在这段日志之后程序就卡死不动了。在调试状态下把程序break掉,发现卡在libcurl的代码 lib/transfer.c Curl_fillreadbuffer()
nread = (int)data->state.fread_func(data->req.upload_fromhere, 1,
buffersize, data->state.in);
一开始没细看,以为这是在从网络层读取数据,数据读不出来以为是服务器的问题,在这里饶了很大的弯路也没定位出来。后来又认真看了下代码,这个data->state.fread_func是指向c库里面的一段代码,说明就是系统的api了。认真阅读了libcurl的源码,fread_func的赋值发送在
/* Curl_init_CONNECT() gets called each time the handle switches to CONNECT
which means this gets called once for each subsequent redirect etc */
void Curl_init_CONNECT(struct Curl_easy *data)
{
data->state.fread_func = data->set.fread_func_set;
data->state.in = data->set.in_set;
}
而这些fread_func_set、in_set的值,在另外一个文件 lib/url.c Curl_init_userdefined()中被设置为默认值
set->in_set = stdin; /* default input from stdin */
set->err = stderr; /* default stderr to stderr */
/* use fwrite as default function to store output */
set->fwrite_func = (curl_write_callback)fwrite;
/* use fread as default function to read input */
set->fread_func_set = (curl_read_callback)fread;
换而言之,卡住的这段代码,实际上就是通过fread从stdin读取输入。对于win32窗体程序,从stdin读取数据会立即返回0,因此不会卡住。而对于控制台程序,stdin就要等待键盘输入了,而且libcurl里面读取的缓冲区又特别长,有16384字节,因此会卡住一直等键盘输入填满缓冲区。
归根结底,其实是libcurl调用POST访问接口用得不正确。因为没有指明POST什么数据,导致libcurl会从stdin读取输入。需要改成
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, "");
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
/* complete within 20 seconds */
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 20L);
res = curl_easy_perform(curl_handle);
网友评论