(1) 首先看产生错误的源码:

/* get it! */res = curl_easy_perform(curl_handle);long http_code = 0;curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);/* cleanup curl stuff */curl_easy_cleanup(curl_handle);if (res != CURLE_OK || http_code != 200) {cout << uri << ", res = " << res << ", http_code = " << http_code << endl;}return (res == CURLE_OK && http_code == 200);

错误日志如下:

http://10.237.92.30:8746/thumbnail/jpeg/l820/AppStore/b262b95f-95b8-4e0e-b4e0-edc3b76e3c81,
res = 7, http_code = 0
http://10.237.92.30:8746/thumbnail/jpeg/l820/AppStore/a4c37951-d8b5-40ff-af27-4efcd1a58e71,
res = 7, http_code = 0
http://10.237.92.30:8746/thumbnail/jpeg/l820/AppStore/abab08ff-75e1-40da-a113-053789e93686,
res = 7, http_code = 0

查看curllib的错误代码,如下,错误代码为CURLE_COULDNT_CONNECT

CURLE_OK = 0,CURLE_UNSUPPORTED_PROTOCOL,    /* 1 */CURLE_FAILED_INIT,             /* 2 */CURLE_URL_MALFORMAT,           /* 3 */CURLE_NOT_BUILT_IN,            /* 4 - [was obsoleted in August 2007 for7.17.0, reused in April 2011 for 7.21.5] */CURLE_COULDNT_RESOLVE_PROXY,   /* 5 */CURLE_COULDNT_RESOLVE_HOST,    /* 6 */CURLE_COULDNT_CONNECT,         /* 7 */CURLE_FTP_WEIRD_SERVER_REPLY,  /* 8 */CURLE_REMOTE_ACCESS_DENIED,    /* 9 a service was denied by the server

(2) 分析curl_easy_perform返回错误的原因

最直接的办法采用gdb跟踪客户端的运行情况,发现客户端在connect的时候返回错误,在源文件curl-7.28.1/lib/connect.c的singleipconnect函数中,于是加入日志在connect之后打印errno,代码如下:

if(!isconnected && (conn->socktype == SOCK_STREAM)) {rc = connect(sockfd, &addr.sa_addr, addr.addrlen);if(-1 == rc) {error = SOCKERRNO;printf("connect failed with errno = %d", errno);}conn->connecttime = Curl_tvnow();if(conn->num_addr > 1)Curl_expire(data, conn->timeoutms_per_addr);

再次运行测试程序,得到如下输出:

connect failed with errno = 99 http://127.0.0.1:8902/thumbnail/jpeg/l820/AppStore/f8913ca1-
ae5f-4fcc-abc5-cbe9ada1a67d, ret_code: 0, res: 7
connect failed with errno = 99 http://127.0.0.1:8902/thumbnail/jpeg/l820/AppStore/3726a1e2-
057e-402d-b347-61c5a5136cd9, ret_code: 0, res: 7
connect failed with errno = 99 http://127.0.0.1:8902/thumbnail/jpeg/l820/AppStore/c19bad67-
6b7d-4dc6-a17a-f74ea525c32a, ret_code: 0, res: 7
connect failed with errno = 99 http://127.0.0.1:8902/thumbnail/jpeg/l820/AppStore/5d778568-
d873-46a7-9651-ad8ac3810bf4, ret_code: 0, res: 7

可以看到errno = 99,在内核的include/asm-generic/errno.h文件中可以查看errno = 99的解释为” Cannot assign requested address”。

#define EAFNOSUPPORT    97  /* Address family not supported by protocol */
#define EADDRINUSE  98  /* Address already in use */
#define EADDRNOTAVAIL   99  /* Cannot assign requested address */
#define ENETDOWN    100 /* Network is down */

(3) errno = 99的原因;

至于connect系统调用为什么返回失败,就只能看系统调用的实现了。

a) connect系统调用

connect系统调用在net/socket.c中实现,Sys_connect系统调用的调用栈如下:

Sys_connect--->sock->ops->connect                   // inet_stream_connectsk->sk_prot->connect               // tcp_v4_connect

tcp_v4_connect的作用主要是完成TCP连接三次握手中的第一个握手,即向服务端发送SYNC = 1和一个32位的序号的连接请求包。要发送SYNC请求包,按照TCP/IP协议,就必须有源IP地址和端口,源IP地址的选择和路由相关,需要查询路由表,在ip_route_connect中实现,源端口的选择在__inet_hash_connect中实现,而且如果找不到一个可用的端口,这个函数会返回-EADDRNOTAVAIL,因此基本上可以确定是这个函数返回错误导致connect失败;

b) __inet_hash_connect

这个函数的主要作用是选择一个可用的端口,其主要的实现步骤如下:

i. 调用inet_get_local_port_range(&low, &high);获取可用的端口链表;

  1. 调用read_seqbegin(&sysctl_local_ports.lock);得到顺序锁;
  2. 得到可用端口的low和high:

*low = sysctl_local_ports.range[0];

*high = sysctl_local_ports.range[1];

ii. 对于每一个端口,进行下面的步骤:

  1. 在inet_hashinfo *hinfo中查找这个端口inet_hashinfo用于保存已经使用的端口信息,每个使用的端口在这个hash表中有一个entry;
  2. 对端口做hash得到链表头(使用链表解决hash冲突)
  3. 遍历链表中的每一个entry:

a) 判断是否与这个要使用的端口相同,如果相同转到步骤b,如果不相同则遍历下一个entry

b) 找到这个端口,调用check_established(__inet_check_established)判断这个端口是否可以重用(TIME_WAIT状态下的端口并且net.ipv4.tcp_tw_recycle = 1是端口可以重用)

  1. 如果在链表中没有找到这个端口,表示端口没有被使用,调用inet_bind_bucket_create在hash表中插入一个entry;

iii. 如果到最后都没有找到一个可用的端口就返回EADDRNOTAVAIL;

从这个函数的实现可以看出,主要是由于可用的端口被占满了,所以找不到一个可用的端口,导致连接失败。运行netstat可以发现确实存在很多TIME_WAIT状态的socket,这些socket将可用端口占满了。

[root@test miuistorage-dev]# netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state)
print key,"\t",state[key]}'
TIME_WAIT        26837
ESTABLISHED      30

(4) 解决办法:

要解决端口被TIME_WAIT状态的socket占满的问题,可以有以下的解决办法:

a) 修改可用端口范围

查看当前的端口范围:

root@guojun8-desktop:/linux-2.6.34# sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768    61000

修改端口范围:

root@guojun8-desktop:linux-2.6.34# sysctl net.ipv4.ip_local_port_range="32768    62000"
net.ipv4.ip_local_port_range = 32768    62000

这种办法可能不能解决根本问题,因为如果使用短连接,即使增加可用端口还是会被占满的。

b) 设置net.ipv4.tcp_tw_recycle = 1

这个参数表示系统的TIME-WAIT sockets是否可以快速回收

root@guojun8-desktop:linux-2.6.34# sysctl net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_tw_recycle = 1

c) 设置net.ipv4.tcp_tw_recycle = 1

这个参数表示是否可以重用TIME_WAIT状态的端口;

root@guojun8-desktop:linux-2.6.34# [root@test thumbnail]# sysctl net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_reuse = 1

(5) 更深入的探讨:sysctl做了什么

可以用strace跟踪一下sysctl的系统调用:

root@guojun8-desktop:linux-2.6.34# strace sysctl net.ipv4.tcp_tw_recycle=1
execve("/sbin/sysctl", ["sysctl", "net.ipv4.tcp_tw_recycle=1"], [/* 20 vars */]) = 0
brk(0)                                  = 0x952f000
…..
open("/proc/sys/net/ipv4/tcp_tw_recycle", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb788e000
write(3, "1\n", 2)                      = 2
close(3)                                = 0
munmap(0xb788e000, 4096)                = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 8), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb788e000
write(1, "net.ipv4.tcp_tw_recycle = 1\n", 28net.ipv4.tcp_tw_recycle = 1
) = 28
exit_group(0)                           = ?

可以看到这个程序打开/proc/sys/net/ipv4/tcp_tw_recycle并向文件中写入1,但是这个设置时怎样其作用的呢?在内核中对/proc/sys目录下的文件的i_fop做了特殊的处理,在proc_sys_make_inode 中设置:inode->i_fop = &proc_sys_file_operationsproc_sys_file_operations的定义如下:

static const struct file_operations proc_sys_file_operations = {
.read   = proc_sys_read,
.write    = proc_sys_write,
};

proc_sys_write中会修改对应的文件,并且修改内存中的内容,不同的文件有不同的proc_handler,如tcp_tw_recycle对应的处理函数是proc_dointvec,这个函数会修改下面的变量:

tcp_death_row.sysctl_tw_recycle

这个变量在内核中表示TIME_WIAT状态的socket是否可以被快速回收。

CURL: CURLE_COULDNT_CONNECT问题探究相关推荐

  1. curl CURLE_COULDNT_CONNECT

    #curl CURLE_COULDNT_CONNECT(7) 错误 工作中遇到软件无法登录的情况,仅一台测试机复现,打印日志发现 curl 返回CURLE_COULDNT_CONNECT错误.上网查了 ...

  2. curl 返回CURLE_COULDNT_RESOLVE_HOST、CURLE_COULDNT_CONNECT

    最近用cocos2dx开发游戏,win32上好好的没啥问题,但是打包到android上登录老是提示curl错误,老是提示CURLE_COULDNT_RESOLVE_HOST(远程主机找不到), 想想难 ...

  3. node.js初步探究

    node.js 探究之路 1.mac下安装node.js (1).升级系统到最新 (2).升级xcode xcode-select -p //检查是否安装xcode,如果出现路径,说明已安装xcode ...

  4. curl下载失败返回0_curl返回常见错误码

    CURLE_OK(0) 成功. CURLE_UNSUPPORTED_PROTOCOL(1) 你的URL传递给libcurl的使用协议,这libcurl的不支持.支持可能是你没有使用一个编译时的选项,它 ...

  5. PHP中cURL错误号对照[转]

    PHP cURL curl_errno 在php程序编写中,使用curl函数库的几率还是挺高的,如curl_init().curl_setopt().curl_exec().curl_errno()等 ...

  6. CURL 错误码 中文翻译

    这几天用CURL做下载系统,经常会遇到一些问题,很多的错误还是和CURL的option有关.现在把这些错误码贴过来,方便查看一下. 错误代码列表 CURLE_UNSUPPORTED_PROTOCOL ...

  7. curl返回常见错误码

    关注公众号 风色年代(itfantasycc) 300G微服务资料等你拿! curl返回常见错误码 - 阿波伦 - 博客园 CURLE_OK(0) 成功. CURLE_UNSUPPORTED_PROT ...

  8. C++编程(二):CURL错误码及含义

    目前项目中使用Android app登录后登再登录,接口响应报错,通过加打印得到curl的错误码为27,因此查询网上资料了解了一个curl错误码以及对应的含义. 本篇文章中的内容转载自:https:/ ...

  9. Curl常见错误返回码

    转自:http://blog.csdn.net/cwj649956781/article/details/8086337 CURLE_OK(0) 所有罚款.继续像往常一样. CURLE_UNSUPPO ...

最新文章

  1. Python之装饰器入门
  2. cents上运行wget报错:unable to resolve host address
  3. Know more about RAC GES STATISTICS
  4. 大图详解负载神器 LVS、Nginx及HAProxy工作原理
  5. Android 反编译apk文件(转)
  6. 奇妙的安全旅行之国密算法
  7. linux英伟达显卡偶尔加载失败,Ubuntu 8.10环境下出现NVIDIA显卡无法正常工作的解决方法...
  8. 阿里巴巴发布第四财季财报 菜鸟驿站包裹量增长100%
  9. 掌管大局的IoC Service Provider
  10. 电脑无法安装SecoClient
  11. 博客空间自动互踩刷人气
  12. 计算理论重点——Theory of Computation
  13. Android知识点深究
  14. FlashBuilder找不到所需要的AdobeFlashPlayer调试器版本的解
  15. 2022年2月语音合成(TTS)和语音识别(ASR)论文月报
  16. 绿米Aqara、飞利浦等设备,如何与智汀使用同一个APP实现跨品牌互联?
  17. 4480: [Jsoi2013]快乐的jyy
  18. dad my_【玩转英文绘本】My Dad!《我爸爸》
  19. 怎么写好一篇接口文档
  20. 【php】PHP语言进阶

热门文章

  1. CSS之标签选择器、ID选择器、类选择器
  2. 先行一步,7大技术创新和突破,阿里云把 Serverless 领域的这些难题都给解了
  3. 计算机组成原理笔算乘法改进,计算机组成原理(唐朔飞)运算方法 PPT.ppt
  4. 华为路由器:ISIS基本原理与配置(含实验)
  5. Git-统计代码行数
  6. 关于Linux软件工程师的招聘要求
  7. markdown写作教程总结
  8. Pytorch - Illegal instruction
  9. IBM X3650 服务器更换内存的过程记录
  10. 夺命聘礼【三】- 原创中篇小说