windows xp下redis客户端无法连接服务端(MicrosoftArchive/redis逻辑bug)

PR:https://github.com/MicrosoftArchive/redis/pull/598


错误描述与处理办法

用redis-cli连接服务端时,错误提示好像是Could not connect to Redis at xxx:poll(2),事实上使用微软的redis on windows编译的Win32_Interop库在window版本低于6.0时都不能连接上redis server。

先说结论吧,造成bug的原因是低版本时调用select函数前多进行了一次rfd(redis的文件描述符)与socket的映射,解决方案是

  1. 把Win32_FDAPI.cpp的1005~1019行删除(FDAPI_select函数的三个if代码块);
  2. 674行的fds[i].fd == INVALID_SOCKET改为pollCopy[i].fd == INVALID_SOCKET
  3. 677行的if (pollCopy[i].fd >= FD_SETSIZE)改为if (fds[i].fd >= FD_SETSIZE),或者直接删除677~680行;
  4. 重新编译Win32_Interop,hiredis和RedisCli即可。

顺便一提我在使用xp工具集编译时出现了一个问题,WS2tcpip.h的第48~51行的预处理并没有起作用,无法包含winapifamily.h,于是我把它直接改为了#include "win32_winapifamily.h"。


问题分析

先来看一下相关部分的源码:


FDAPI_poll函数
FDAPI_poll函数,注意667到680行的合法性检查.png
原FDAPI_select函数
  • 在635行,FDAPI_poll函数进行了一次RFD到socket的映射,此时pollCopy[n].fd已经是socket句柄并在682~684行复制到fd_set中,当WindowsVersion低于6.0时作为WSAPoll的替代,在688行FDAPI_poll调用了FDAPI_select;
  • 但在FDAPI_select中,调用select之前又进行了一次多余的映射(1005~1019行),试图通过RFDMap来从RFD映射到socket,但由于此时的RFD已经是socket了,因为RFDMap中并不存在这些key,结果就是所有的socket都是INVALID_SOCKET,导致无法成功连接到redis服务器。
  • 在677行,原redis代码用pollCopy[i].fd来判断socket数量是否超过64的逻辑也是错误的,因为经过一次映射后pollCopy[i].fd代表的是socket,而socket函数返回的是OS文件描述符,超过64很正常,应当用fds[i].fd(也就是rfd)来判断。另外这部分的check我认为是多余的,也可以直接删除,因为在667~670行已经判断过了。
    在redis中rfd和OS类似,0~2也是被占用的,新的fd从3开始,因此实际上的合法socket数量不超过62。
  • 吐槽:我觉得select部分和poll部分肯定不是同一个人写的,所以写select的人真的很不认真,把fds和pollCopy的涵义完全搞反了并且还没有进行测试。或者写poll的人也许应该把映射部分放在分支内进行?就是多了一部分重复代码。

p.s. 这个PR是不可能merge了,提个PR做提醒也好,另外这个人好像有在维护这个坑,链接在这里,但是没有修复这个bug(2018.05.14),我也懒得再提一次PR了:https://github.com/tporadowski/redis

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容