編輯:關於Android編程
在測試我們開發的一個 APK(使用了 libevent-2.1.3-alpha 作為網絡庫) 時發現一個奇怪的問題,域名解析有時報錯 Non-recoverable name resolution failure 。在公司偶爾報錯,後來程序改動了一下,出錯時重試幾次,問題沒再出現,以為好了。昨天換了個網絡環境,結果報錯幾率變得非常大。
互聯網搜索到這個錯誤的一個處理辦法,說在使用 getnameinfo() 函數時需要顯式指定其第二個參數 salen 為 sizeof(struct sockaddr_in) 或者 sizeof(struct sockaddr_in6) ,說是 Solaris 和 Android 上的 getnameinfo() 實現不會查看 saddr 中的 sin_family 來計算出真正 salen 。我嘗試了一下,沒有解決問題,後來想想, libevent 根本就沒有使用系統的域名解析函數,完全是自己實現的,於是只好自己跟代碼了。
由於遠程調試的環境沒有搭建起來,只能不斷地添加日志,反復查看,非常耗時。最後還真給我找到了問題所在。
libevent 的 dns 解析實現就在 evdns.c 這個文件中,不過如果不懂得 DNS 協議,代碼看起來可能比較難懂,我重溫了 DNS 協議,然後開始跟代碼。
libevent 在處理 DNS 解析時,針對域名引入了一個隨機大小寫的概念,在 evdns_base_new() 中把 global_randomize_case 默認設置為 1 ,然後在讀取域名服務器配置文件時根據裡面的 options 來修改。安卓上沒有 resolv.conf ,這些選項就沒有修正的機會,於是最終 global_randomize_case 還是 1。
在 libevent 構造 DNS 請求( request_new() 函數)時,會根據 global_randomize_case 來決定是否對發起 dns 請求時傳入的域名進行大小寫隨機轉換,代碼如下:
if (base->global_randomize_case) { unsigned i; char randbits[(sizeof(namebuf)+7)/8]; strlcpy(namebuf, name, sizeof(namebuf)); evutil_secure_rng_get_bytes(randbits, (name_len+7)/8); for (i = 0; i < name_len; ++i) { if (EVUTIL_ISALPHA_(namebuf[i])) { if ((randbits[i >> 3] & (1<<(i & 7)))) namebuf[i] |= 0x20; else namebuf[i] &= ~0x20; } } name = namebuf; }
然後在處理 DNS 服務器返回的結果時,從 DNS 請求列表中根據 trans_id 找到對應的 request ,拿 DNS 結果中解析出來的名字和 request 中的名字比較,如果不一致,就認為出錯了。詳情參考 reply_parse() 函數,其中 TESTNAME 宏實現名字比對,原始代碼如下:
#define TEST_NAME \ do { tmp_name[0] = '\0'; \ cmp_name[0] = '\0'; \ k = j; \ if (name_parse(packet, length, &j, tmp_name, \ sizeof(tmp_name))<0) \ goto err; \ if (name_parse(req->request, req->request_len, &k, \ cmp_name, sizeof(cmp_name))<0) \ goto err; \ if (base->global_randomize_case) { \ if (strcmp(tmp_name, cmp_name) == 0) \ name_matches = 1; \ } else { \ if (evutil_ascii_strcasecmp(tmp_name, cmp_name) == 0) \ name_matches = 1; \ } \ } while (0)
#define TEST_NAME \ do { tmp_name[0] = '\0'; \ cmp_name[0] = '\0'; \ k = j; \ if (name_parse(packet, length, &j, tmp_name, \ sizeof(tmp_name))<0) \ goto err; \ if (name_parse(req->request, req->request_len, &k, \ cmp_name, sizeof(cmp_name))<0) \ goto err; \ if (base->global_randomize_case) { \ if (evutil_ascii_strcasecmp(tmp_name, cmp_name) == 0) \ name_matches = 1; \ } else { \ if (strcmp(tmp_name, cmp_name) == 0) \ name_matches = 1; \ } \ } while (0)
這個問題我花費了4個多小時來跟,最後總算解決了,心終於放下了(之前出錯重試的修補方式總讓人不踏實)。
RecyclerView 是 android-support-v7-21 版本中新增的一個 Widgets, 還有一個 CardView 會在下次介紹使用。官方介紹 Re
QQ5.0側滑效果實現方案有很多方式,今天我們使用ViewDragHelper來實現一下。先上效果圖: ①自定義控件SlidingMenu繼承FrameLayout,放在
日常生活中我們隨處可見對話框,上面有很多提示信息,更加方便提示用戶進行不同的操作。一、對話框的兩個特點和一些常見的對話框1.當彈出對話框是會結束UI線程(即主線程);2.
一、通信技術1、1G:模擬制式 只能進行語音通話。2、2G:GSM, CDMA 收發短信和郵件。3、2.5G :GPRS, EDGE,訪問wap網絡數據.(圖片, 壁紙,