Linux下MyIpAdd库的使用
一、MyIpAdd库简介MyIpAdd库是一个纯C库可以用它获取我们的公网IP地址。其github地址为https://github.com/felmur/MyIpAdd二、下载编译MyIpAdd源码从 https://github.com/felmur/MyIpAdd 下载MyIpAdd源码解压进入源码目录可以看到其目录结构如下编译cmake . -DCMAKE_INSTALL_PREFIX$(pwd)/install make make install然后源码目录下会生成MyIpAdd的库文件libMyIpAdd.so三、MyIpAdd的使用例子以C为例编写一个使用示例。main.cpp内容如下#include iostream #include myipadd.h using namespace std; int main() { MyIpAdd * m new MyIpAdd(); cout My public ip address is: m-ip endl; return 0; }编译g main.cpp -o main -g -IXXX/MyIpAdd-master -LXXX/MyIpAdd-master -lMyIpAdd运行效果如下。可以看到本机的公网ip被成功打印出来了该ip和通过百度查询到的公网ip一致四、MyIpAdd源码里的bugMyIpAdd源码里myipadd.cpp的构造函数 MyIpAdd::MyIpAdd()的代码为MyIpAdd::MyIpAdd() { buf static_castchar *(malloc(1024)); sk socket(AF_INET, SOCK_STREAM, 0); if (sk0) { if (DEBUG) cout Socket error endl; exit(-1); } if (DEBUG) cout Socket creation OK endl; host gethostbyname(addr.c_str()); if (host nullptr ) { if (DEBUG) cout Bad host addr endl; exit(-3); } addrin.sin_family AF_INET; addrin.sin_port htons(80); addrin.sin_addr.s_addr *(reinterpret_castunsigned int*(host-h_addr)); if (connect(sk, reinterpret_caststruct sockaddr *(addrin), sizeof(addrin))0){ if (DEBUG) cout Connection Failed endl; exit(-2); } if (DEBUG) cout Connection OK endl; string msg GET / HTTP/1.1\r\nHost: addr \r\nConnection: close\r\n\r\n; send(sk, msg.c_str(), msg.length(), 0); ssize_t ret 0; string html; while((ret read( sk , buf, sizeof(buf)))0) { *(bufret) 0; html buf; } liststring cont; split(html,cont,\n); size_t r 0; for(auto line : cont) { if (line.length()20) { if ((r line.find(Current IP Address:))!string::npos) { string a line.substr(r20); a a.substr(0,a.find()); if (DEBUG) cout a endl; ip a; } } } close(sk); }可以看到里面调用了exit函数host gethostbyname(addr.c_str()); if (host nullptr ) { if (DEBUG) cout Bad host addr endl; exit(-3); }也就是说当解析默认域名checkip.dyndns.com 失败时MyIpAdd 构造函数会直接 exit(-3)从而把整个调用MyIpAdd函数的进程退出。MyIpAdd 构造函数会把DNS/socket/connect失败当成致命错误直接退出整个进程。五、修复MyIpAdd源码里的退出bug修改 MyIpAdd 源码把MyIpAdd::MyIpAdd() 里的 exit(...) 改成“失败但不退出进程”的形式然后重新编译 libMyIpAdd.so就能解决调用MyIpAdd的进程退出问题。比如修改为if (host nullptr) { ip ; return; }或者更完整一点if (host nullptr) { if (buf) free(buf); if (sk 0) close(sk); ip ; return; }修复后整个MyIpAdd::MyIpAdd()函数改为MyIpAdd::MyIpAdd() { sk socket(AF_INET, SOCK_STREAM, 0); if (sk 0) { if (DEBUG) cout Socket error endl; return; } if (DEBUG) cout Socket creation OK endl; host gethostbyname(addr.c_str()); if (host nullptr || host-h_addr nullptr) { if (DEBUG) cout Bad host addr endl; close(sk); sk -1; return; } addrin.sin_family AF_INET; addrin.sin_port htons(80); addrin.sin_addr.s_addr *(reinterpret_castunsigned int*(host-h_addr)); if (connect(sk, reinterpret_caststruct sockaddr *(addrin), sizeof(addrin))0){ if (DEBUG) cout Connection Failed endl; close(sk); sk -1; return; } if (DEBUG) cout Connection OK endl; string msg GET / HTTP/1.1\r\nHost: addr \r\nConnection: close\r\n\r\n; if (send(sk, msg.c_str(), msg.length(), 0) 0) { if (DEBUG) cout Send Failed endl; close(sk); sk -1; return; } ssize_t ret 0; string html; char read_buf[1024]; while((ret read(sk, read_buf, sizeof(read_buf))) 0) { html.append(read_buf, ret); } close(sk); sk -1; liststring cont; split(html,cont,\n); size_t r 0; for(auto line : cont) { if (line.length()20) { if ((r line.find(Current IP Address:))!string::npos) { string a line.substr(r20); a a.substr(0,a.find()); if (DEBUG) cout a endl; ip a; } } } }六、反思我在某个项目中为了获取公网ip使用了MyIpAdd::MyIpAdd库。但是它的源码内部有异常退出的bug导致我的程序一直偶发挂掉。所以这给我的启发是1.尽量用多人用的开源库小众的开源库没什么人用往往有很多bug2.一定要有能阅读开源库代码的能力这样万一开源库出现问题的时候你才能修改这些bug。