ping in Android

上網找了一下,一般對於ping的建議是直接利用java內建的InetAddress.isReachable()來做,實際上在ping內部網路的伺服器時,是沒問題的,但是如果要ping位於外部網路的伺服器時,就會失敗而回傳False。

Android內部的實作是在libcore/luni/src/main/java/java/net/InetAddress.java裡,這裡就很簡單的建立socket,然後試著連到指定位址的port 7,如果可以連,或者是伺服器明確地拒絕,就視為伺服器存在,可以連線。這就解釋了為什麼無法ping位於外部網路的伺服器,因為ISP為了安全或是其他考量,而不允許。我分別以python與java寫了與Android實作相似的程式去實驗,的確都不行。

[python]
import sys
import socket

if len(sys.argv)<2:
print( “Need at least 1 parameters.” )
print( “Usage: {0} host”.format( sys.argv[0] ) )
sys.exit(-1)

r = False
try:
s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
s.settimeout(5)
s.connect( (sys.argv[1], 7) )
r = True
except socket.error, ex:
if ex.errno==111:
r = True
else:
print( ex )

if r:
print( “{0} is reachable.”.format( sys.argv[1] ) )
else:
print( “{0} is NOT reachable.”.format( sys.argv[1] ) )
[/python]

[java]
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.IOException;

class Ping {
public static void main(String[] args) {
InetAddress in;

try {
in = InetAddress.getByName(args[0]);
boolean result = in.isReachable(5000);
if (result) {
System.out.println(“Response OK”);
}
else {
System.out.println(“Response fail”);
}
} catch (UnknownHostException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
[/java]

Java裡只能建立 stream(TCP) 或 dgram(UDP) 的socket,那麼只能用JNI,用C寫ping了,但經過實驗結果,發現會因為權限的關係而無法建立socket,原來要建立raw與IPPROTO_ICMP的socket,需要root權限。一般linux裡,非root使用者可以使用ping,是因為ping加上了setuid權限,才能使用。在Android裡,要不就是建立service,要不就是設法為ping加上setuid,否則是都無法使用的。

Get IP Address in Android

這篇Get the ip address of your device on Android Development其實就講的很清楚了,就是用NetworkInterface.getNetworkInterfaces()去取得所有網路介面,然後再用網路介面的getInetAddresses()去看該網路卡上的所有IP位址,然後把Loopback位址排除掉,就可以取得了。該開的權限有android.permission.INTERNET與android.permission.ACCESS_WIFI_STATE。

我工作上的需求,是要拿到3G那邊的IP,經過實驗,發現在WiFi開啟的情況下,只會拿到WiFi網路介面的IP位址,而非3G網路介面的。如果把WiFi網路介面關閉,就可以拿到3G網路介面的IP。我以為是程式邏輯的關係,可是在確認過NetworkInterface.getNetworkInterfaces()傳回的網路介面數量以後,發現都是取得兩個網路介面,而非我預期的三個。好,那我不使用NetworkInterface.getNetworkInterfaces()改用NetworkInterface.getForName()去取的話,在WiFi開啟的情況下,取得的結果會是null,也就是說該網路介面被關掉了。

直接去看底層,在 mydroid/libcore 下NetworkInterface的JNI函式,那邊是使用netlink去跟kernel問所有的網路介面,然後解析之後傳回。那麼底層應該是沒有問題,看來是中間層為了能取得較快的網路速度,而自動把3G網路介面給關閉了。看來也只能在程式執行前,先把WiFi給關閉,才能取得3G網路介面的IP位址了。

參考資料:

[ExcelVBA]刪掉所有sheet,只留下指定的sheet

上星期的某天花了一個小時在寫這個Script來幫我刪sheet,程式邏輯很簡單,很快就搞定了,只是另存新檔,打開以後,卻什麼東西都沒看到。存為 .csv 的話,的確是有資料,那就奇怪了,百思不得其解。一放就又放了一個星期,這次終於解開秘密了。另存新檔以後,不知道為什麼,打開以後視窗會被隱藏,所以什麼都看不到。只要取消隱藏視窗就可以看到了。程式的話,就是要這樣寫: Application.Windows(1).Visible=True,下面就是整個程式:

電影流水帳(2012/1/1~2012/1/8)

轉眼又是新的一年。

  • Cowboys & Aliens(IMDB, Wikipedia),中譯:星際飆客。中文翻譯還蠻能唬人的,算了。這是一部西部片,只是對抗的人變成了外星人。故事很簡單,男主角一開始是喪失記憶的,手上帶了一個莫名其妙的手環,然後他到了小鎮。小鎮旋即被外星人襲擊,一堆人被擄走要解剖用。然後男主角就跟小鎮上最硬的人組隊去救人,在途中,男主角慢慢想起以前的事情,也跟女主角等人慢慢熟起來。然後終於找到外星人的飛船,就決定先把外星人引出來打,男主角跟女主角趁機進去救人。外面真的幾乎是一場一面倒的戰役,那個時代的槍沒打到要害的話,基本上是打不死人的,就子彈卡在肌肉裡這樣,所以外星人根本是所向披靡。不過在電影的加持之下,還是幹掉了不少外星人。題外話,其實外星人的設定不是很合理,為什麼身體還會打開,把要害攤給別人看還會伸出另外兩隻手呢?不過反正是外星人,我想我就別在意了。在硬撐了許久之後,終於把人救了出來,女主角其實也是外星人,是要來報仇的外星人,所以就拿了男主角的手環下來,轉成炸彈,進去核心。男主角趁機會趕緊逃了出來,女主角最後就把外星人的船炸掉,就這樣。大致上不難看啦,最可惜的一幕是女主角死而復生的那一段,她從火裡走出來,衣服都被燒光光,我蠻想加入那些圍觀的人說,想知道為什麼他們那麼驚訝。(你劃錯重點了好嗎?驚訝是因為她死了又再復活,不是沒穿衣服的問題!!)
  • Iron Man 2(IMDB, Wikipedia),中譯:鋼鐵人2。Tony因為控制不住palladium的關係,生命不長久了,所以就開始亂搞,並且讓Pepper當CEO。在西班牙遇到他老爸朋友的兒子Ivan Vanko,一陣亂打讓鋼鐵人跟Starks工業的名聲大為降低,讓對手Hammers工業趁機而入。一整個low到谷底,一具鋼鐵人又被美國國防部拿走的情況下,神盾局的人跑出來跟他說palladium不是最好的元素,給他打了抑制Palladium的藥物並給了他他老爸的遺物,說有更好的元素可以用,他摸索一陣子之後,找到了製造新元素的方法,然後噹噹噹,新的core誕生了,arc reactor更為強大。Ivan被Hammers吸收以後,幫他們研發,但不聽他們的話,而是搞自己的,所以在Expo時,他遙控一堆機器人跟Iron Man打,這堆機器人真的很酷啊,但Iron Man跟他的朋友聯手把這堆機器人幹掉,Ivan大魔王現身,最後以解決掉大魔王告終。這一集Pepper的戲份不多,好可惜,我很喜歡她說。然後飾演Natalie的Scarlett Johansson是一個亮點,她的武術動作真的好酷,然後她真的很適合演這種酷酷的角色,The Spirit裡是這樣(有多一點搞笑),這裡也是這樣。

http://en.wikipedia.org/wiki/Whiplash_(comics)#Film

skydrive+FUSE的read

這幾天弄skydrive+fuse的時候,一直在想,這到底適不適合用FUSE來實作?

  1. 以網路程式來說,讀取的時候,要可以告訴伺服器端,客戶端要下載那一段,要下載多少,以HTTP來說,這可以用Range這個header來達成,只要在發request的時候,加上Range header就可以指定。那麼skydrive伺服器端有支援續傳嗎?這個我還沒去確定。
  2. 那麼FUSE的read裡可以使用續傳嗎?在read裡會指定要讀取多少,從哪裡開始讀取,看來是可以使用續傳,可是使用續傳的話,就要考慮一下客戶端的情況,一般在做這種read的時候,是使用一個回圈,然後一次只讀取一部份到緩衝區裡,並不是一次讀完,因此,只讀取一部份的結果,等於會送出許多次的request給伺服器端。
  3. 可以不要送出許多次的request嗎?這樣網路會折返很多次。一次下載完,先放到硬碟的話,就要面對同步的問題。首先第一次read時,就要可以傳回一部份值,並且要把這個下載的動作放到thread去執行,繼續下載,並放到某個暫存的地方,直到下載完成。那接下來第二次要read時,再去讀取已經下載的部份並傳回。這樣會少掉比較多的request,效率也會比較高一點。可是thread在寫的時候,另外一個thread可以去讀取正在寫入的檔案嗎?會不會有什麼奇怪的事情發生呢?這個是需要實驗的地方。
  4. 延續前面的討論,現在已經放到暫存的地方,存取會快很多,可是該怎麼知道伺服器端的檔案被更改了?這應該可以在一開始要read的時候,去判斷檔案大小跟屬性來判斷是不是要重新下載。另外也要考慮到伺服器端檔案被頻繁更動的情況。
  5. 既然會放到暫存的地方的話,是不是乾脆像dropbox那樣用sync的方式來做會比較好?

 

家裡server升級

先從昨天講起,昨天早上本想在今天下午請特別假的,卻發現沒辦法請特別假了。仔細看,才發現原來可以請特別假的期間是到職日到隔年到職日的前一天,啊啊啊!!我還以為跟前公司一樣說,一整個晴天霹靂。所以我去年短短的6個半月時間,就把所有特別假都請完了,今年要撐半年以後才能放特別假啊~
好吧,去年最後一天有請特別假,也是最後一個特別假,然後在家裡升級伺服器,中午有出去吃火鍋,載太座大人回家。去年我弟換NB時,就把他的桌機給我了,一直有打算要幫家裡伺服器升級,但一直沒動手。這次的升級遇到的問題是,機器按了電源開關以後,電源燈一閃即逝,猜想是短路的問題,所以就整個拆掉,看有沒有裝上墊片。拆開一看,發現沒裝墊片,然後機版不是用銅柱去鎖主機板,而是靠凸起跟主機板接觸去鎖。這下可就傷腦筋,就上網查了一下,說這種的比較容易短路,所以他是用透明膠帶去貼凸起,然後戳洞以後再鎖。我就如法泡製,再把主機板…等等的東西都再裝上。裝上、按前面的電源以後,怎麼還是閃了一下就滅掉,於是確定後面的電源開關以後,再去按前面的電源開關,這下就可以了。所以或許不用整個拆掉,可能只是自己耍白痴,後面的電源開關搞錯開跟關了?!算了,反正已經都弄好,硬碟也裝上,可以順利開機就好。
可是,事情往往不是這麼簡單就可以解決。原本有一顆SATA硬碟,我加裝兩顆原來伺服器的IDE硬碟上去,主要的那顆用IDE轉SATA接,另外一個次要的用IDE接。我是想說用原來伺服器主要的那顆硬碟來開機,可是怎麼弄都不行,經過兩個多小時,無數次的試驗之後,發現是因為CentOS做出來的initrd沒有載入SATA的驅動模組,那接下來就無法找到硬碟去掛載主要硬碟上的分割區。本來是想為initrd補上SATA驅動模組的,後來放棄了,就把主要硬碟改用IDE接,次要的則用IDE轉SATA接,這樣,就順利開機了,拜Linux所賜,也不需要重灌,就稍微調整了一下設定、日期時間就收工了。
升級以後的確是爽,原本的伺服器是Celeron 566MHz加384MB的RAM,新的伺服器是AMD Sempron 3000+加1G RAM,雖然規格還是跟現在的電腦差很多,速度快了好多啊!!

UnicodeDecodeError when logging utf-8 string

我的python有使用logging library,可是卻出現下面的錯誤訊息:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 56: ordinal not in range(128)

百思不得其解,明明我的字串就是utf-8,可是卻會出現這個錯誤訊息。查了Python的Unicode How-To好幾次,覺得問題應該是出在檔案上,開檔案時應該要用codecs,那,會不會是logging開啟檔案時,沒有用codecs去開檔案然後處理encoding啊??發現好像也不是,因為使用FileHandler時有指定編碼啊。
上網找了之後,才發現logging的確已經使用codecs來開啟檔案了,那為什麼呢??又再找了幾篇當參考:UTF-8 In Python logging, how?UnicodeEncodeErrors with celery built-in console logging,也有人直接自訂formatter來解決,看著看著,就想到,原來logging裡面已經有處理了啊,再次對utf-8字串作utf-8 encode的結果就是出錯,那就表示不必特別丟utf-8字串,只要丟給他unicode字串,logging就會自動幫我轉了啊~
於是我就不特別再作字串的轉換,直接使用unicode字串,這樣就解決這問題了。

LiveConnect OAuth2

講的比較仔細的是這幾篇:Mobile and Windows desktop appsOAuth 2.0 (Live Connect)Server-side scenarios (Live Connect)Developers – Learn how to bring data from Hotmail, Messenger, and SkyDrive to your mobile apps,第1篇比較粗略,但夠簡單。第2篇是有仔細講解參數以及流程(含圖),可以在遇到問題時做參考。看過以後,發現步驟的確是OAuth2,但就是沒辦法用 python oauth2 去套,就只能照步驟來硬刻了。

有試過httplib2,看能不能parse HTML再去摹擬POST登入,結果會回一個400的錯誤,拿到錯的頁面。python內建的webbrowser,只會用browser開出畫面,但沒有提供方法可以取回在網址列的網址。看來在拿 access token/authorization code 的部份,就只能先組出sign in的url,把瀏覽器開出來,讓使用者先去登入,然後允許以後,再請使用者把網址複製下來,貼到程式的輸入,讓程式去擷取 access token/authorization code,再保存下來。就先這樣子做吧,以後有更好的方法再來修正。

pyskydrive/skydrive .net api client

靈機一動用python+skydrive去找,有找到pyskydrive這個project,試用結果,是不行。專案是去年釋出的,追蹤了一下,發現是因為Skydrive頁面HTML一整個改掉,已經不能用原來的方法去parse了。

改看SkyDrive.Net API Client的source code,看能不能fork出來改。可是看跟試驗的結果,發現也不行使用。他使用的方法也不是正規作法,因此認証那關都過不了。

看來只能自己刻了。在LiveAPI的網站上有看到是使用 OAuth 2,就先安裝了 python oauth2 的library,可是參考範例以後,發現怎麼樣也套不上微軟的LiveAPI OAuth。於是就只能看官方的介紹跟文章來實驗與重刻了。

skydrive+fuse?

好像沒人寫,先找資料看看。

收集資料:

整體看起來,不要使用python,使用 mono+fuse+SkyDrive .Net API Client 似乎是比較好的解?!