早上九点,口实服务器日志里跳出一串红色报错。口实我负责的口实库存系统调用第三方物流API时,返回了HTTP 401。口实查了半小时,口实发现是口实签名算法里少拼了一个冒号。这种事在C语言调用Web API时太常见了——你以为在写业务代码,口实实际是口实在跟协议细节搏斗。
先看最基础的口实GET请求。用libcurl库,口实三行核心代码就能跑通:
curl_easy_setopt(curl,口实 CURLOPT_URL, "http://api.example.com/items"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); res = curl_easy_perform(curl); 麻烦在于响应处理。C没有现成的口实JSON解析器,得自己拼字符串或者引入cJSON库。口实我习惯用cJSON,口实它能把返回的口实JSON树直接映射成结构体。但要注意内存释放——cJSON_Parse之后必须调cJSON_Delete,否则跑上几天就会内存泄漏。这个坑我踩过,线上服务崩溃时才知道痛。
POST请求就更有意思了。你要提交表单数据或者JSON体,必须手动构造Content-Type头。比如调用微信支付接口,要传XML格式的参数,还得在头部声明Content-Type: text/xml。很多新手忽略这点,服务端返回415错误,还以为是接口地址写错了。
实际项目里,最磨人的是鉴权。我调某云厂商的对象存储接口时,需要生成签名串。规则是:把HTTP方法、Content-MD5、Content-Type、日期、CanonicalizedResource拼起来,再用HMAC-SHA1加密。C语言里没有现成的加密函数,得引OpenSSL库。代码写起来像这样:
HMAC(EVP_sha1(), secret_key, strlen(secret_key), (unsigned char*)string_to_sign, strlen(string_to_sign), digest, &digest_len); 然后还得自己Base64编码。每次调试签名问题,我都要把请求头打印出来,跟服务端文档对比。有次发现是日期格式不对——服务端要求GMT格式,我传了本地时间。差8小时,签名验证永远失败。
超时处理也是必修课。第三方接口偶尔会卡住,默认的libcurl超时时间是0,意味着无限等待。必须显式设置:
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); 但超时后重试策略得自己设计。我试过简单粗暴地循环重试三次,结果把对方服务器打挂了。后来改成指数退避:第一次等1秒,第二次等2秒,第三次等4秒。还要根据HTTP状态码区分——500错误可以重试,400错误重试也没用,是参数问题。
最让我头疼的是异步场景。C语言没有内建的异步机制,调用Web API时如果不想阻塞主线程,得自己开线程。但线程里不能直接操作UI或者共享内存,要加互斥锁。我写过一个下载器,开了四个线程同时拉取数据,结果频繁出现段错误。调试了两天,发现是全局缓冲区被多个线程同时写入。后来每个线程分配独立内存,用消息队列传给主线程,问题才解决。
还有个细节容易被忽略——字符编码。大部分Web API返回UTF-8编码,但Windows下C语言默认用的是本地代码页。如果直接printf输出中文,控制台会显示乱码。我一般用MultiByteToWideChar转换,或者干脆在代码里统一用UTF-8存储,只在输出时转换。
说回开头那个401错误。最后定位是签名串里的冒号应该是英文的,我代码里写成了中文全角。就这么一个字符,浪费了我一上午。C语言调用Web API就是这样——没有框架帮你兜底,每个字节都得自己负责。但也正因如此,一旦跑通了,你对HTTP协议的理解会深入骨髓。那种感觉,比用Python调requests库爽十倍。
