2008年8月28日 星期四

DoCoMo The Road to Hokusai’s Waterfall

The Road to Hokusai’s Waterfall (去葛飾北齋畫中瀑布的地方)


額外參考資料:
葛飾北齋作品畫冊專輯

NTT DoCoMo Vision 2010

DoCoMo為人類的無線未來勾勒出什麼樣的美麗新世界。
片中的母親在逛街時,看到一雙她覺得兒子會喜歡的球鞋,便利用手機上的攝影機,將這雙鞋的樣子「實況轉播」到她兒子的手機螢幕上。結果兒子不喜歡這雙的樣子,便在電話中告訴他媽媽將「鏡頭」轉到其他樣式的鞋子上。透過這樣的方式,這位媽媽幫兒子買的鞋子,被兒子喜歡的機率應該蠻高的。
媽媽在回家前,還利用手機遙控家裡的智慧型冰箱,讓冰箱中的牛肉先解凍。等到她到家後,就可以將解凍的牛肉直接料理了。
片中的機器寵物,類似Sony最近在日本大賣的「愛寶」機器狗。可是片中的機器寵物具有更強的人工智慧,可以陪主人下棋。
未來的網路遊戲,將會是讓玩家有身歷其境感覺的VR遊戲。所謂VR(Virtual Reality,中文翻成虛擬實境)指的是玩家戴上VR眼鏡和其他感應設備後,可以讓自己完全地融入遊戲中。
最後片子快結束的時候,女主角和她的法國男友在巴黎結婚了。她的祖母人在日本,沒有辦法親自參加。可是她透過類似「視訊會議」的方式,透過網路,也一樣能夠和在場的親友一起分享她孫女結婚的喜悅。
雖然「高科技」一向會給人一種難以親近的印象,或是有些人覺得科技會讓人與人的距離越來越遠。可是我個人卻相信,善用傳播和通信的科技,可以很輕易地把分散在世界各地的人連在一起,就像這部短片中描述的一樣......人與人之間,終究會相連。

NTT DoCoMo Vision 2010 Part I-1


NTT DoCoMo Vision 2010 PartI - 2


NTT DoCoMo partI-3


NTT DoCoMo Mobile Future


資料參考來源:
http://www.charlietai.com/japan/020503.htm

NTT DoCoMo 演奏會篇

Mobile Life Story "Concert" version

來看看NTT DOCOMO 所提出對於2010年的近未來,通訊技術對於生活會產生什麼樣的影響,最新的發表為「演奏會」篇(之前的「同學會」篇也好令人感動啊!),這次整篇故事由一個演奏會貫穿4個小故事組成,這裡面的人事物,透過手機將生活、親情、愛情,連成一線,讓科技暖暖的、自然的融入生活,手機帶領的未來生活不再冷冷冰冰。(套一句廣告詞,科技始終來自於人性)

首先看到的第一部是「父與女」
一開始看到在紐約,爸爸回想起以前那時很不贊成女兒還在玩音樂(是希望趕快嫁人嗎?),女兒回說不做音樂要做什麼?難道要像爸爸一樣從事跟水相關的工作嗎?

從回想中拉回,這時旁邊有一個外國人訪問那位爸爸(小谷先生)說,對這次的對方水質管理系統的採購等等的問題,小谷先生說有好消息會通知他,進入一棟大樓時生物辨識技術自動啟動,牆壁上用對方的名字來歡迎,並簡介場所,眼鏡經過通訊可以指示方向,同時在會議室中的外國人也知道了對方目前在哪裡以預作準備,這時小谷的個人行動祕書提醒他,女兒的演奏會快開始了,要送花過去嗎?(PDA會自己思考,真屌)爸爸回答說:「讓我想想」(對電腦也嘴硬),掃描確認身分後,進去了會議室當然就先寒喧一番,問問對方家裡狀況等等,接下來進入會議正題,拿出行動電腦,只要有插孔就可以執行,所需要的其他東西都在桌子裡了(長的亂像USB的)。

場景回到東京,看到了女兒在等待上場,這時候收到了一束爸爸寄來的花,上面有影像留言,裡面說「演唱會加油,我專注在自己的道路(水)上,你也要在自己所選的路上加油!」(果然天下父母都是嘴硬心軟啊)

第二部「戀人們」
一開始看到透過虛擬影像在練習音樂的男主角,離線後身體有點不舒服,線上聯繫了醫生後,醫生透過遠端操作了解身體的狀況,血壓心跳等等。

場景來到了超商,看到了一位女性用類似手機與放大鏡的東西在檢查蔬菜的名稱,這時候生病的男主角打電話來了
男:「好久不見,元氣?」女方這時看起來臉很臭
男:「可以幫我買個藥嗎?我把ID給你!」
女:「為什麼?拜託分手的前女朋友不是很奇怪嗎?」雖然這樣說,最後還是答應幫男方買藥,出了超商時用非接觸式錢包,付了款。

到了男方家後,看見虛弱的男方用手指按鈕來操控開門,開了門後
女:「藥在這裡!」男:「謝謝」
女:「盆栽長大了,沒想到你還有澆水呢!」(男若有所思的樣子,會不會是一起買的盆栽?)
男:「明天有現場演唱會」女:「你還持續在玩音樂啊?」
男:「如果有時間的話,來看….」
女:「你這種狀態(生病)在說什麼?我要回去了,保重」

隔天女方走在路上時收到了男方的影像留言,裡面說謝謝因為他買的藥讓她的感冒好了,要來聽演唱會嗎的邀約。

最後原本以為對方不會來的男方看到女方出現在演奏會場。

第三部「快樂銀髮族」
一開始一對老夫妻邊走邊聊天說:「現場演唱會啊,有3年多沒聽過了」,兩人在討論自己的兒子小時候學音樂時還邊學邊哭的糗事,突然想起入場卷帶了沒,妻說:「沒問題,在這裡面」(長的像項鍊一樣的東西)也問起飛機票有沒有問題,妻說:「沒問題,預訂了你喜歡的走道那一邊歐」

場景回到老夫妻的家裡,妻正在準備晚餐的時候,透過冰箱來把機票確認好了,這個系統還會記住個人喜好幫忙購物等等,很像家庭管家的感覺。

上了車後,看到後座顯示出目的地的資訊,在車上想起魚不知道餵了沒,老媽媽叫老爸爸自己確認,這時就看到老爸爸透過手上的機器,遙控家裏的機器人,觀看家裡的狀況,老媽媽想起沒澆水,還可以透過搖控來澆水,就像自己在家一樣。

到了會場,看到了自己的兒子,兒子問起票沒有問題吧?父說:「沒問題,都在這裡面了」

第四部「小朋友的冒險」
透過通訊設備,讓小朋友的安全狀況得以把握,小朋友在跟朋友道別後,心裡想到早上媽媽所說今天是爸爸的現場演奏會,腳步不經加快了起來。

去演唱會場的路上,發現了可愛的花朵,透過像放大鏡一樣的東西,知道了植物名稱與特性的知識。
經過一個小巷子時發現了一隻可愛的小貓,在追逐的過程卻不經迷路了,這時在美容院做頭髮的媽媽,透過手上的裝置了解現在小朋友居然在迷路中。

原本驚慌失措的小朋友,拿出了剛才像放大鏡一樣的秘密武器,知道現在的位置與要去的方向,雖然沒看過週遭的景色,但是一點都不擔心。

看的出在美容院的媽媽一直很擔心小朋友在什麼地方,看到一路上方向無誤的安全上公車的小朋友狀態,不經鬆了一口氣。

最後母女2人在演唱會場見面,看著現場的演奏會,台上每個表演者所聯結的生活,因為通訊科技讓人跟人之間串連的更加緊密與幸福。

NTT DoCoMo 演奏會篇 日文版


NTT DoCoMo 演奏會篇 英文版

“人類是因為想上太空,才上了太空的!” 當夢想被提出的那一刻,其實就已經前進了一大步,唯有透過不斷的提出對未來生活的美好想像,人類生活才能創造無限的可能,產品本身也才能被賦予生命,進而去感動使用的人,世界因這些想像而更美好。

資料來源:
http://tw.myblog.yahoo.com/jw!D4I.kQKFERla1Xpj_vMcTW5bsA--/article?mid=2547

利用 序列比對演算法 辨識 抄襲之C程式(轉貼)

剛剛看到的,覺得還滿有趣的。這是暨南大學的碩士論文 (原網頁)。

通常改人家的作業,最簡單的就是換變數;再好一點,就是會改一下相似的迴圈,例如 if else 和 switch、for 和 while 等;或是把 Type 改一改:然後自愛一點的改法,可能還會認真看看,改一下輸出輸入的語法等。

這個論文主要跟傳統 Unix 下的 diff 最大的不同,就是會去檢查上述的這些情況 - 變數替換、函式互換、變數型態替換等等等。詳細可以看看論文,寫的很清楚,滿有趣的。

轉貼資料來源:
http://twntwn.info/blog/ajer001/archives/764

2008年8月27日 星期三

Verilog Levels of Abstraction

  1. Behavioral Representation(Architecture/System/Algorithmic Level)

  2. 這是Verilog中最高層次,在這個層次中我們會依據系統需求,來考慮模組或函式的功能而不去考慮硬體方面詳細的電路是如何運作的。所以會專注在系統架構或演算法的實現,因此在這個階段設計工作就會像在寫C。通常在此層次中所做的是用來模擬並且多半無法合成的,也無法看出電路特性。
  3. Functional Representation(RTL/Dataflow Level)

  4. 在這個層次中,我們必需要指定資料處理的方式,明確的說明資料如何在暫存器及模組間儲存與傳送的流程。在此層次中可看出電路特性。
  5. Structural Representation(Gate Level)

  6. 這個層次中的模組是由邏輯閘連接而成,所以設計的工作就會像以前用描繪邏輯閘來設計線路一樣,所以可以了解實際的硬體線路,但無法很快速的了解高階的行為。在這個層次中我們可以用內建的verilog原生元件來設計,不過由於在不同的製程下,各種的邏輯閘大小、延遲時間等等,也會跟著有所不同,所以此層次通常交由工具程式下去產生。
  7. Physical Representation(Switch/Transistor Level)

  8. 這是Verilog最低階的層次,線路是由開關與儲存點組合而成。在此層次設計時必需清楚知道電晶體的元件特性,但此處寫出的Verilog是無法合成的。


從Behavioral->Functional->Structural->Physical皆可使用Verilog去描述出來,尤其是Behavioral是Verilog的強項。


Verilog in Behavioral Level – NAND Module

module NANDGATE (A, B, F);
input A;
input B;
output F;
reg F;

always @(A or B)
begin
F=~(A & B);
end
endmodule

Verilog in Functional Level – AND Module
module ANDGATE (A, B, F);
input A;
input B;
output F;
wire F;

assign F=A&B;
endmodule

Verilog in Structural Level – OR Module
module ORGATE (A, B, F);
input A;
input B;
output F;

or u1(F, A, B);
endmodule

Verilog in Physical Level – NOT Module
module inv (ina, out);
input ina;
output out;
supply1 vcc;
supply0 gnd;

pmos (out, vcc, ina);
nmos (gnd, out, ina);
endmodule

C語言裏嵌入組合語言

C下的inline組語
在C語言中嵌入組語的程式碼加個__asm__("asm code");
__asm__(
"movl $1,%eax\n\t" // SYS_exit
"xor %ebx,%ebx\n\t"
"int $0x80"
);
注意quote 與\n\t的位置 \n\t是因為gcc其實根據這些與其它c code產生一個.s檔 \n\t只是在.s檔產生newline與tab這在每一行都要除了最後一行不用 另外inline assembly的通式是
__asm__(asm statements : outputs : inputs : clobber);
通式有兩個重要的概念, 一個是可以和C程式傳變數來溝通,另外 如果我們指定了eax ebx...等register,則這下可完了,如果其他的C code 也正在用eax ebx,則compiler必須先把這些值推進stack才能跑你的asm code, 所以我們可以不特別指定register,讓gcc自動作register運用的最佳化。 其實這是其他mips和其他CPU的作法,別種CPU的register命名沒 eax, ebx.....這麼死與囉唆, $1 $2 $3...就搞定,還可以換來換去很有彈性。 這個通式就是在做這樣的事,請看一個例子
int main (void) {
int operand1, operand2, sum, accumulator;

operand1 = rand (); operand2 = rand ();

__asm__ ("movl %1, %0\n\t"
"addl %2, %0"
: "=r" (sum) /* output operands */
: "r" (operand1), "r" (operand2) /* input operands */
: "0"); /* clobbered operands */

accumulator = sum;

__asm__ ("addl %1, %0\n\t"
"addl %2, %0"
: "=r" (accumulator)
: "0" (accumulator), "g" (operand1), "r" (operand2)
: "0");
return accumulator;
}

第一個__asm__是說input的東西把operand1(C的變數)放到r,也就是%1, operand2(C的變數)放到r,也就是%2,然後執行assembly code的 movl與addl, 然後結果放到sum(C的變數)=r 也就是%0, 在這邊我們沒有指定eax ebx,只是很單純的%0 %1 %2 r。 %0 %1 %2 分別對應了output r, input r, input r, %0 %1 %2.....會先對應output裡的register,再對應input裡的register, 就是它們出現的順序。 gcc會幫我們最佳化register的使用。 clobbered operands是一堆registers, 我們告訴gcc說這些registers的值已經被暴力的摧毀(被改變了), 你要重新考慮它們的合法性才行, 在這邊0就是%0,gcc會特別照顧一下它所選的%0的值。 有一些規則要說明

r, g, 0這些東西叫constraints(限制的意思),每一個符號有特殊意義 代表這個暫存器必需是什麼型式的(暫存器有通用暫存器,符點運算暫存器, cpu指令有的允許直接對memory做運算,有的不準等等條件的暫存器, r代表通用型暫存器)。

output operand前一定要有個"="表示這個constraint是read-ony, "="叫constraint modifier。

input output operands後一定要跟著相對應的C 變數的參數, 這是給asm的參數。

如果statement裡真要指定register要多加個%變成%%eax

通用constraints
I, J, K .... P 是根據不同cpu可以做不同的解釋來表示一個範圍的立即定址整數
Q, R, S .... U
0, 1, 2 .... 相對於assembly statement裡的%0 %1 %2 ....
a, b, c .... f 可以根據不同cpu可以做不同定義的registers

m 表示這是一個memory的operand 例如mov eax, data中的data

p 合法的記憶體位址
r 一般通用型register
g 任何的通用型register, memory operand, 立即定址的整數

constraints在386上有
a eax
b ebx
c ecx
d edx
S esi
D edi
I 表示只有constant value (0 to 31)才行

r 可以是eax ebx ecx edx esi edi
q 可以是eax ebx ecx edx
g eax, ebx, ecx, edx or variable in memory
A eax and edx combined into a 64-bit integer (use long longs)

參考資料:
GNU Compiler Collection
AT&T ASM
ARM GCC Inline Assembler Cookbook
GCC-Inline-Assembly-HOWTO
Using Inline Assembly With gcc

NAND Flash SLC、MLC技術解析

許多人對Flash Memory的SLC和MLC區分不清。就拿目前熱銷的MP3隨身聽來説,是買SLC還是MLC Flash Memory Chip的呢?在這裏先告訴大家,如果你對容量要求不高,但是對機器質量、數據的安全性、機器壽命等方面要求較高,那麽SLC Flash Memory Chip的首選。但是大容量的SLC晶片成本要比MLC晶片高很多,所以目前2G以上的大容量,低價格的MP3多是採用MLC晶片。大容量、低價格的MLC晶片自然是受大家的青睞,但是其固有的缺點,也不得不讓我們考慮一番。

什麽是SLC?
SLC英文全稱(Single Level Cell——SLC)即單層式儲存 。主要由三星(samsung)、海力士(Hynix)、美光(Micron)、東芝(toshiba)等使用。

SLC技術特點是在浮置閘極與源極之中的氧化薄膜更薄,在寫入數據時通過對浮置閘極的電荷加電壓,然後透過源極,即可將所儲存的電荷消除,通過這様的方式,便可儲存1個信息單元,這種技術能提供快速的程序編程與讀取,不過此技術受限于Silicon efficiency的問題,必須要由較先進的流程强化技術(Process enhancements),才能向上提升SLC制程技術。

什麽是MLC?
MLC英文全稱(Multi Level Cell——MLC)即多層式儲存。主要由東芝、Renesas、三星使用。
英特爾(Intel)在1997年9月最先開發成功MLC,其作用是將兩個單位的信息存入一個Floating Gate(Flash Memory存儲單元中存放電荷的部分),然後利用不同電位(Level)的電荷,通過内存儲存的電壓控制精准讀寫。MLC通過使用大量的電壓等級,每一個單元儲存兩位數據,數據密度比較大。SLC架構是0和1兩個值,而MLC架構可以一次儲存4個以上的值,因此,MLC架構可以有比較好的儲存密度。

SLC比較MLC的優勢:
目前市場主要以SLC和MLC儲存為主,我們多了解下SLC和MLC儲存。SLC架構是0和1兩個值,而MLC架構可以一次儲存4個以上的值,因此MLC架構的儲存密度較高,並且可以利用老舊的生産程備來提高産品的容量,無須額外投資生産設備,擁有成本與良率的優勢。
與SLC相比較,MLC生産成本較低,容量大。如果經過改進,MLC的讀寫性能應該還可以進一步提升。

SLC比較MLC的缺點:
MLC架構有許多缺點,首先是使用夀命較短,SLC架構可以存取10萬次,而MLC架構只能承受约1萬次的存取。
其次就是存取速度慢,在目前技術條件下,MLC晶片理論速度只能達到2MB左右。SLC架構比MLC架構要快速三倍以上。
再者,MLC能耗比SLC高,在相同使用條件下比SLC要多15%左右的電流消耗。
雖然與SLC相比,MLC缺點很多,但在單顆晶片容量方面,目前MLC還是占了絶對的優勢。由于MLC架構和成本都具有絶對優勢,能滿足未來2GB、4GB、8GB甚至更大容量的市場需求。 

SLC與MLC的識別:
一、看傳輸速度
比如有兩款採用Rockchip晶片的産品,測試時寫入速度有2、3倍優勢的應該是SLC,而速度上稍慢的則是MLC。即使同様採用了USB2.0高速接口的MP3,也不能改變MLC寫入慢的缺點。
二、看FLASH型號
一般來説,以K9G或K9L為開頭型號的三星Flash Memory則是MLC,以HYUU或HYUV為開頭型號的現代Flash Memory應是MLC。具體晶片編號以三星和現代為例:三星MLC晶片編號為:K9G****** K9L*****。現代MLC晶片編號為:HYUU**** HYUV***

簡單總結:
如果説MLC是一種新興的Flash Memory技術,那麽它的“新”就表現在:成本低!
雖然MLC的各項指標都落後於SLC Flash Memory。但是MLC在架構上取勝SLC,MLC肯定是今後的發展方向,而對于MLC傳輸速度和讀寫次數的問題已經有了相當多的解决方法,例如採用三星主控晶片,wear leveling技術,4bit ECC校驗技術,都可以在採用MLC晶片的時候同様獲得很好的使用效果,其性能和使用SLC晶片的没有什麽差別,而會節省相當多的成本.

項目

Samsung(SLC

Toshiba(MLC

使用壽命

存取壽命長

可存取1萬次

寫入速度

9MB/s 以上

1.5MB/s 以上

工作電壓

3.3V/1.8V

3.3V

封裝技術

可配合其他技術 設計一體成型

塑膠組裝



資料來源:
http://aax1985.spaces.eepw.com.cn/articles/article/item/24831
http://blog.yam.com/registry/article/15636037
http://av.beareyes.com.cn/2/lib/200702/07/20070207400.htm
http://210.71.214.3/UserFiles/File/940913-indus.pdf

點滴理財法「六大守則」

點滴理財法~~六大守則~~
「你講了那麼久,到底哪一支股票可以買?」「有沒有什麼理財方式可以一本萬利?」這是政大財務管理系所教授周行一演講時,常被問的問題。

大家都希望快速致富。但多數富翁並非一夕致富。

《下一個富翁就是你》的作者史丹利(Thomas Stanley)和丹寇(William Danko)研究美國富有人士20年發現:這些人學歷不高,沒有繼承龐大遺產,也無天外飛來的橫財,他們的財富來源是勤奮工作和規律儲蓄。

一點一滴,穩健地積累財富,是擁有小富的途徑,也是晉升大富的基礎。

「點滴理財法」6守則,請牢記在心:
  1. 努力工作

  2. 周行一在《不理財也發財!﹖》中告訴大家一個事實:「有錢人的財富都是他們努力工作,從他們獻身熱愛的工作中獲得的報酬,並非從理財而來。」因此最穩健的投資方式就是工作。工作可以讓你的收入源源不斷。
    台灣IBM公司軟體事業處副總經理陳永生就是這個理念的實踐者。他的理財祕訣就是找到公司好、制度好、薪水好的好工作。他在1987年進入IBM,第三年起,年薪即達100萬元。同時IBM還提供員工每個月可以薪水的十分之一購買市價8折的IBM股票。陳永生在IBM17年,長期投資IBM的成效,他拒絕透露獲利數字,只明快地回答「當然有賺啊!」。
    「『第一桶金』大多靠苦幹上班。」萬寶週刊社長朱成志在《改變您發財的DNA》中也有相同的見解。
  3. 規律儲蓄

  4. 陳永生一直以來,最多的理財方式就是定存。把錢存在銀行,是不少所謂「專家」大肆批評的錯誤。然而周行一在《不理財也發財!﹖》中指出,銀行存款是你的核心持股。每個人應準備可支付6個月生活費的活期存款,以隨時應急,也應有可支付一年生活費的定期存款。剩下的錢才能拿去投資。
    「規律儲蓄是致富的不二法門。」墨爾基(Burton G. Malkiel)於《穩健理財十守則》一書中開宗明義地說。規律儲蓄意即需要持之以恆地存錢,但是花錢的誘惑太多、太方便,存錢實在難。聯傑財務顧問公司執行長蕭碧華建議,儲蓄一定要先設定目標,例如買房子、存教育基金等,有目標,才會有動力。暢銷理財書作家阮慕驊教大家一個實用的觀念:「收入-儲蓄=支出」,每個月領到薪水先儲蓄一部分,其餘的才是可花用的錢。
  5. 不跑短線

  6. 企圖透過短線交易快速致富是投資的地雷,大衛‧巴哈(David Bach)在《富貴成雙》指出,累積財富可能要花幾十年,而非幾個月。他給投資者的忠告是:「頻繁進出股市,也會最快損失一大筆錢。」記住,十「短」九賠!
  7. 別借錢投資

  8. 有時候借錢是為了投資,然而只有投資在自己身上或本業時,例如借錢受教育,才是正確的舉債,才能借錢。周行一在《不理財也發財!﹖》中力勸:不要借錢做自己不在行的投資,也不要為了過度消費而借貸,錯誤的舉債可能讓你萬劫不復。
    大衛‧巴哈也提出警語:「借錢永遠是輸家。絕對不要用借來的錢買股票。」
  9. 先買保險

  10. 「有養家義務的人,不買保險就是失責。」墨爾基在《穩健理財十守則》一書中說。作者有很深的體會。作者的先生一向沒有投保,結婚後他們買了房子,生了小孩,沒想到先生卻突然逝世,留下沈重的房貸,家庭經濟頓時緊繃。因此作者的理財原則是:先針對人生可能的風險做補強,再談投資。
  11. 購買自用住宅

  12. 墨爾基建議:自用住宅是個好投資,如果你買得起,盡可能擁有自己的房子。自用住宅可以節稅(在台灣,每一戶可有購屋貸款利息扣除額30萬元),也是強迫儲蓄的好方法,同時帶給人極大的心靈滿足感。


買房子買車子必需自己想清楚做好決定
日本的大前研一對個人的建議如下:從根本重新認識生活形態還有拋棄偏見。簡單的說,就是把社會上覺得原本應該有的生活形態重新以中下階層的態度重新考慮過。比如:是否真的需要買車揹車貸?是否真的需要買房子揹房貸?這些省來下的錢能不能在市郊買一間好一點的別墅,平常工作天則住離公司近的小公寓…等等。

2-bit Left Shift

32位元左移2位元
// 32-bit Shift left by 2
module leftShift2 (in,out);
input [31:0] in;
output [31:0] out;
reg [31:0] out;
out = { in[29:0], 1'b0, 1'b0 };
endmodule // leftShift2

module leftShift2 (in,out);
input [31:0] in;
output [31:0] out;
reg [31:0] out;
out = in << 2;
endmodule

參考來源:
http://inst.eecs.berkeley.edu/~cs61c/fa04/lectures/L26-dg-singlecpu.pdf

16-bits Sign Extender to 32-bits

這個Sign Extender主要是希望把16bits的有號數變成32bits的有號數
verilog範例程式如下:
// Sign extender from 16- to 32-bits.
module signExtend (in,out);
input [15:0] in;
output [31:0] out;
reg [31:0] out;
out = { in[15], in[15], in[15], in[15],
in[15], in[15], in[15], in[15],
in[15], in[15], in[15], in[15],
in[15], in[15], in[15], in[15],
in[15:0] };
endmodule // signExtend

另一個比較好的寫法
// Sign Extender
module SignExtend(In, Out);
input [15:0] In;
output [31:0] Out;
assign Out = {{16{In[15]}},In[15:0]};
endmodule

Joint(Concatenation) Operation - Joint different operands as one by { and }
Example 1:
// A = 1'b1, B = 2'b00, C = 2'b10, D = 3'b110
Y = { B, C} // Y is 4'b0010
Y = { A, B, C, D, 3'b001} // Y is 11'b10010110001
Y = { A, B[0], C[1]} // Y is 3'b101
Example 2:
wire [1:0] a, b; wire [2:0] x; wire [3;0] y, Z;
assign x = {1’b0, a}; // x[2]=0, x[1]=a[1], x[0]=a[0]
assign y = {a, b}; /* y[3]=a[1], y[2]=a[0], y[1]=b[1],
y[0]=b[0] */
assign {cout, y} = x + Z; // Concatenation of a result

Replication Operation - Repetition operand several times
Example 1:
A = 1'b1; B = 2'b00; C = 2'b10; D = 3'b110;
Y = { 4{A} } // Y is 4'b1111
Y = { 4{A}, 2{B}, C} //Y is 8'b1111000010

Example 2:
wire [1:0] a, b; wire [4:0] x;
assign x = {2{1’b0}, a}; // Equivalent to x = {0,0,a }
assign y = {2{a}, 3{b}}; //Equivalent to y = {a,a,b,b,b}

有parameter的寫法
<code>module SIGN_EXTEND(in, out);
parameter INSIZE = 16, OUTSIZE = 32;

input [INSIZE-1:0] in;
output [OUTSIZE-1:0] out;
assign out = {{ OUTSIZE-INSIZE {in[INSIZE-1]}}, in};
endmodule // SIGN_EXTEND</code>

資料來源:
berkeley Single Cycle CPU Datapath with Verilog
Multicycle Processor Design in Verilog

2008年8月26日 星期二

Register File in Verilog

在CPU裏面最重要的結構就是暫存器檔案(Register File)。Reading the Register FileWriting into the Register File底下列出簡單的Register File的Verilog範例程式碼

I/O Input
(1)Clock Signal(時脈訊號):clk
(2)Write Enable Signal(寫入暫存器訊號):en_write
(3)Address Bus(指定暫存器編號,需要3個5位元):ra1、ra2、wa
(4)Input Data Bus(寫入的資料,需要1個32位元):wd

I/O Output
(1)Output Data Bus(輸出的資料,需要2個32位元):rd1,rd2
module regfile
(
input [4:0] ra1,output [31:0] rd1,
input [4:0] ra2,output [31:0] rd2,
input clk,
input en_write,
input [4:0] wa,input [31:0] wd
);

reg [31:0] registers[31:0];
// 暫存器0永遠輸出0
assign rd1 = (ra1 == 5'b00000) ? 32'h00000000 : registers[ra1];
assign rd2 = (ra2 == 5'b00000) ? 32'h00000000 : registers[ra2];
// 當en_write=1時,執行將wd寫入暫存器的動作
always @(posedge clk)
begin
if ( en_write )
registers[wa] <= wd;
end

endmodule

2008年8月25日 星期一

勞保年金 vs 國民年金

最近一直聽到勞保年金/國民年金,這倒底是幹麻的呀?
所以就跑去勞工保險局看了一下,"勞保年金給付介紹"

首先一個重點是:"勞保老年年金與勞退金不同",勞保老年年金是由勞工、雇主及政府共同繳納保險費,勞工負擔百分之二十、雇主負擔百分之七十、政府負擔百分之十,等到勞工符合老年年金請領條件時,每個月可領取的給付。
勞退新制是雇主每個月提撥薪資百分之六到勞工個人帳戶,帳戶跟著勞工走,不怕一旦換工作,年資須重新計算;勞工退休時也是每個月領取,勞保老年年金與勞退金可以同領取。

勞保年金大概的重點列出來給大家看吧!詳細的還是看勞保局的簡報檔吧^^

1.什麼是年金?(俗稱:月退)
(1)提出申請,按月發放
(2)長期性給付,保越久,領越多
(3)活到老,領到老

2.勞保年金
現在的勞保老年給付,只能一次領完。
從民國98年1月1日以後,就變成可以有2種選擇啦。方案A:一次領完,方案B:每個月領。

3.國民年金
自97.10.1起,滿25 歲、未滿65 歲國民,在沒有參加公教保、軍保、農保和勞保期間參加的新制度繳費繳到65 歲,每個月可領取老年年金給付,領到死亡當月為止

兩個的差異比較表如下圖:



個人覺得每個月領錢錢蠻不錯的,因為以前只有軍公教才有所謂的終身退休俸,現在變成大家都有囉^^
也可以避免領不到或花光光或被騙光退休金,造成很多社會的問題^^

最後雖然以後可以每個月領錢錢,現在還是要規劃一些以後的生活啦!不能只想靠月退金喔^^

講那麼多,我只想知道我可以一個月領多少啦,請點"老年給付試算"

另一個勞工退休金,就是勞退新制裏的雇主提撥6%+自己提撥6%(應該沒有很多人自己又提撥6%@@)
不知道已經有多少退休金在個人帳戶裏嗎?如果你有讀卡機+自然人憑證+Windows XP...那可以到勞工保險局網路申報及查詢作業系統
我是Vista系統耶,很抱歉,勞保局的這個查詢系統Vista不支援@@
不能線上查的人
1.臨櫃查詢
勞工親自攜帶國民身分證正本或其他可辨識身分證明文件正本(如駕照、附照片之健保IC卡等),至勞保局各地辦事處辦理,經確認為勞工本人後,由勞保局人員列印其勞工退休金個人專戶資料。
2.以勞動保障卡查詢
勞工須親自向勞保局委託之4家金融機構(台新銀行、土地銀行、玉山銀行、台北富邦銀行)申請發給勞動保障卡。持該卡至發卡銀行之自動櫃員機查詢,並可列印查詢單。

額外參考資料:
今周刊605期 教你拿最多退休金/勞保年金、國民年金全解讀 選對多賺100
勞保老年年金與勞退金不同 可同時領
勞工保險局的勞退新制簡介

霹靂神州II之蒼玄泣 37夜變、38失去鋒芒的神劍

  1. 精采武學:

  2. 白忘機:熾陽炎流,移天乾坤,斷雲掌。
    赭杉軍:法王臨‧天極烈焰,五行化天術,天嶽三章。
    蒼:蒼龍玄。
    默狩:默流弒。
    瞾雲裳:漫天飄雪血飄紅,藍關雪擁千層起。
    柳生劍影:萬神劫。
    滄海孤劍:滄破浪,逆水斷。
    天草二十六:奔浪關。
    斷風塵:神魔斷。
    華顏無道:魔羅法擊,惡露殺道。
    冥風滅靈:烈陽焚日,冥風斷魂掌。
    異度魔將:魔噬天下。
    不二做:劍旋十字斬,風龍捲。
  3. 法術陣法:

  4. 鳳凰鳴:沖夷無極,天卷飛昇(叫出背後的卷軸飛行)。
    孟極:寒霜蝕魂,暴雪冰鋒。
    算天河:戒神儀荼羅邪印,戒神儀,天羅之咒。
  5. 武器:

  6. 紅樓劍閣鎮閣神劍:歲月輪
  7. 藥草醫術:

  8. 九禍的救治之法
    黑狗兄:她需要服用雷公膽,以此為靈力貫透全身,才可以貫通血脈,使全身恢復運行。
    恨長風:雷公膽?
    黑狗兄:此膽出自一種傳說的靈禽,終年生活在雷電交加之雲端。
    黑狗兄:由於雷公膽靈力過強,若沒有藥引直接服用,將會使全身過度復甦,導致氣盡而亡。
    恨長風:這...什麼藥引?
    黑狗兄:天穹三鳴。
    恨長風:天穹三鳴?
    黑狗兄:天穹山上一年三鳴,需要以此雷聲激動全身血脈,方可服用雷公膽。
    黑狗兄交給恨長風一個吸音瓶

    解救蒼的方法
    (1)解破逆返魔源之人
    (2)保住蒼的軀體。幽若山,天之口。
    蒼提到的兩項藥物
    一是無根之花,二是神鳥之靈
    黑狗兄:無根之花,名若其義,只有花形不具根葉,這種花,相傳在無花之境才有存在。那這個神鳥之靈,我還真的沒聽過。
  9. 物品:

  10. 吸音瓶(吸取天穹之鳴的容器)
    攝魂鏡(照射人體靈魂的物品):拜江山將攝魂鏡交給孟極,要孟極把攝魂鏡交給玄貘照射白忘機以確定身份
  11. 異獸:


  12. 文學詩句:

  13. 伊達我流跟競天宗門徒混戰時的旁白:身如五鼓銜山月,命似三更油盡燈。
    道隱鳳凰鳴:心懷一襟朗月,劍藏七尺乾坤,慣看滿城煙雨,回首不入烽雲。

    幽若,及通往地獄與天堂的雙岔路,又稱幽若關,分為天之口與地之口,生人進入,在到達幽若之前,不得偏向任何一方,若是心思把持不定,只會落入無間,
    看到天地雙峰,就是進入幽若關了。
---------------------------------------------------------------------------
萬血邪籙,開啟神州之柱,識界玄貘,變化天地之序
紅樓劍閣,論定劍鋒之最,棄天魔帝,造就毀滅之端

2008年8月22日 星期五

Ubuntu 加上 FreeNX 遠端桌面

當我們需要「遠端登入、文字介面」的時候,我們會用rlogin, telnet,或者有加密功能的ssh。

但如果需要「遠端登入、圖形介面」的時候,我們可以用什麼工具?
基本上我們可以選擇使用的通訊協定/工具至少有:
1.XDMCP(X Display Manager Control Protocol) =>各種Unix/Linux的X Window
2.RDP(Remote Desktop Protocol) => MS 的Remote Desktop
3.RFB(Remote Frame Buffer) for Virtual Network Computing (VNC) => VNC

但我們現在要看的,是一套更棒的叫"FreeNX",的遠端桌面程式。

NX 是由 NoMachine 這家公司推出的技術,NoMachine 很大方的將它們的核心技術 NX 以 GPL 的方式開放,而 FreeNX 則是網路上 OpenSource 社群以 NX 技術為基礎開發的 OpenSource 程式,目前來說,NoMachine 和 RealVNC 類似,有提供一個免費的版本供人,使用而 FreeNX 和這個版本相比,目前差異性並不大,而且網路上的教學,大部分的說明在安裝 FreeNX 的同時,也是要裝這版免費版,以取得其中的核心 NX 技術來用。

和VNC不同,VNC是直接把螢幕轉成圖形資料傳輸,FreeNX為了低頻寬的桌面連線,如同windows使用的RDP,有做壓縮、安全加密機制和超棒的session restore。FreeNX為了這些功能,變成了跟X Server有著很密切的關係,所以FreeNX無法支援將Windows做為遠端Sever。

使用FreeNX的動機主要有下面幾點
1.全程使用SSH機制加密 (based on openSSH), 原本的X11 基本上是 Clear Text 完全沒有加密
2.windows下面有免費的client可以使用,open source 也有人開始做
3.有對畫面壓縮,能夠有效降低網路頻寬,要連到伺服器不用再遠端經過自己的電腦
4.可以Suspend/Resume你的桌面session可以看到,不用怕自己的電腦關機程式又要重做,如同一般的windows遠端桌面一樣
底下這張圖是NX Server和NX Client及X Server/windows之間的關係圖大家可以先看一下demo的影片


有興趣的話請到Download NX Free Edition for Linux - i386準備下載3個deb package到你的server端
你會看到下圖的畫面,請依序按裝1.client 2.node 3.server
都裝好了之後,接下來換設定client端
情況1:windows client
nxclient for windows
安裝好後,請執行NX connection wizard的程式
情況2:ubuntu client
請執行/usr/NX/bin/xclient

接下來的設定畫面應該都差不多如下:
為什麼是port 22呢?因為FreeNX是走SSH的channel囉,所以如果你的SSH Server有改port這裏也要跟著改。



就可以很開心的連到server去玩遠端桌面囉^^

資料參考來源:
http://groups.google.com/group/hch-computer-home/web/freenxnomachine
http://hi.baidu.com/ubuntu8/blog/item/154027d928a5a52f10df9b68.html
http://moto.debian.org.tw/viewtopic.php?t=6423
鳥哥的Linux私房菜遠端連線伺服器Telnet/SSH/VNC/XDMCP/RSH
http://openfacts.berlios.de/index-en.phtml?title=NX_Components

X86 Linux Kernel 系統初始化概述

Linux Kernel Overview Cn
View SlideShare presentation or Upload your own. (tags: kernel)

2008年8月20日 星期三

勞貸利率計算

勞貸利率=郵政公司二年期定期儲金機動利率+年息0.042%
2.767% = 2.725% + 0.042%

98/02/23,1.125% + 0.042% = 1.167%
98/01/10,1.175% + 0.042% = 1.217%
97/12/15,1.575% + 0.042% = 1.617%
97/11/12,2.225% + 0.042% = 2.267%
97/11/04,2.475% + 0.042% = 2.517%
97/10/24,2.575% + 0.042% = 3.450%
97/10/23,3.408% + 0.042% = 3.450%
97/10/03,2.675% + 0.042% = 2.717%
97/07/04,2.725% + 0.042% = 2.767%
97/04/03,2.665% + 0.042% = 2.707%
96/12/28,2.635% + 0.042% = 2.677%
96/09/28,2.555% + 0.042% = 2.597%
96/06/29,2.485% + 0.042% = 2.527%
96/04/11,2.285% + 0.042% = 2.327%


不過勞工房屋貸款已經"功成身退"了,現在要辦由內政部營建署的"住宅補貼作業規定"。

詳內政部營建署網站

信義房屋的政府優惠貸款比較表


三仟億政府優惠房貸利率則是=郵匯局二年期定期儲金機動利率加1%(機動調整)。政府固定補助年率0.125%,目前為3.6%。

論文英文常見的文法錯誤

(1) *** transform, *** equation, *** method, *** algorithm 在論文當中,當成是可數名詞,而非專有名詞。
可數名詞單數時,前面要要冠詞 (a 或 the)
Fourier transform is important for signal processing. (錯誤)
The Fourier transform is important for signal processing. (正確)
A Fourier transform is important for signal processing. (正確)
Fourier transforms are important for signal processing. (正確)
I have written the Matlab program of Parks-McClellan’s algorithm (錯誤)
I have written the Matlab program of the Parks-McClellan’s algorithm (正確)

(2) 論文視同正式的文件,對 not, is, are 不用縮寫
they’re (錯誤) they are (正確)
he’s (錯誤) he is (正確)
aren’t (錯誤) are not (正確)
don’t (錯誤) do not (正確)

(3) Suppose, assume 後面要加關係代名詞
Suppose x is a large number. (錯誤)
Suppose that x is a large number. (正確)

(4) 每一個子句都有一個動詞,而且只有一個動詞

(5) In this paper, in this section, in this chapter 開頭的句子,應該用現在式,而非未來式
In this paper, we will introduce the fast algorithm of DCT. (錯誤)
In this paper, we introduce the fast algorithm of the DCT. (正確)

(6) 在 conclusion 當中回顧文章一內容,用過去式

(7) time domain, frequency domain 前面也加冠詞
in time domain (錯誤) in the time domain (正確)

(8) 不以 “this paper”, “section *”, “Ref. [*]” 當主詞用
This paper describes several concepts. (錯誤)
In this paper, we describe several concepts. (正確)
Ref. [1] proposed the method. (錯誤)
In Ref. [1], Parks and McClellan proposed the method. (正確)

(9) 提及某個 equation 時,直接括號加數字即可
  in equation (3) (錯誤) in (3) (正確)
 提及某個 section, table, or figure 時,前面不加冠詞,而且常用大寫
in the section 4 (錯誤) in Section 4 (正確)
in the table 5 (錯誤) in Table 4 (正確)

(10) 寫論文不是寫文學作品,不要用高明、漂亮、但沒有保握的文法。儘量用簡單而有把握的文法。

(11) 當不確定哪一種文法用法是正確的時候………………………….可以用 Google 來看看,哪一種用法的使用率較高 (加 “ ”)使用率較高的,通常是正確的用法

(12) 投稿至國際學術期刊時,可以用網路上的論文編修服務,來修改文法上的錯誤
李國鼎科技發展基金會:http://www.ktli.org.tw/html/menu3.htm
柯泰德:http://www.chineseowl.idv.tw/html/lla.html
牛津學社:http://www.oxeditor.com
editage:http://www.editage.tw

2008年8月19日 星期二

JPEG



參考網站:
JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
Lee's Stego Research Notes
靜宜大學 胡育誠博士 影像編碼講義
JPEG簡易文檔 v2.14
淺談JPEG圖像壓縮演算法
用VB語言編程實現JPEG資料壓縮
JPEG詳細說明文檔

據說要做JPEG Codec的話最好參考一下底下這2份文件
Interchange Format(JFIF) ISO/IEC 10918-1 and ISO/IEC 10918-2

ISO/IEC 10918-1:1994 主要內容
Information technology -- Digital compression and coding of continuous-tone still images: Requirements and guideline
連續色調靜態圖片的數位壓縮及編碼:要求和指南

ISO/IEC 10918-2:1995 主要內容
Information technology -- Digital compression and coding of continuous-tone still images: Compliance testing
連續色調靜態圖片的數位壓縮及編碼:相容性測試

INFORMATION TECHNOLOGY – DIGITAL COMPRESSION AND CODING OF CONTINUOUS-TONE STILL IMAGES – REQUIREMENTS AND GUIDELINES
JPEG File Interchange Format

資料轉貼來源:
http://djj.ee.ntu.edu.tw/ADSP8.ppt

2008年8月18日 星期一

霹靂神州II之蒼玄泣 35造反、36枕中的天書

  1. 精采武學:

  2. 銀鍠朱武:一斬風月,氣雙流
    黑羽恨長空:一劍橫天恨無人
    華顏無道:摩羅法擊
    不二做:龍捲風
    西門寒照:鐵馬干戈,銀河落九天,千秋浪擲‧劍封無悔
    蒙面女:春去無回,藍關雪擁千層起,便看冰魂白上紗
    拜江山:封傾山河
    樓無痕:行雲走影
    嘯天颺東海絕藝:海無邊‧天地無際‧東海揚波,海無邊‧天地無際‧浪捲九州,海無邊‧天地無際‧濤翻蒼穹
    東宮神璽:紫霓貫空,玉印火嘯,神逸風流,紫霓貫空
    風肆險:罡濤肆險,十里波瀾,浪險傾天變
    棘狼夜凶:荊棘裂旋斬,狼影之殺
    任劍誰:切齒怒目橫向天,衝冠豎髮亂開雲
  3. 法術陣法:

  4. 紅樓劍陣:飛天仙流劍陣
    拜江山:靈動之風‧草木皆兵,靈動之風,幽冥召喚
    赭杉軍:法王臨‧天極聖印,太極方印,伏天王‧降天一‧紫微破星圖
  5. 武器:

  6. 涅盤劍(黑羽恨長風)
  7. 藥草醫術:


  8. 物品:

  9. 明聖天書:在玉匣枕裏面
    銀鍠令牌:號令鬼守閣人員的令牌
  10. 異獸:


  11. 文學詩句

  12. 黑羽恨長風之詩號:山關留痕忘歲月,獨少一劍恨天涯。

---------------------------------------------------------------------------
萬血邪籙,開啟神州之柱,識界玄貘,變化天地之序
紅樓劍閣,論定劍鋒之最,棄天魔帝,造就毀滅之端

UHCI,OHCI,EHCI差異

USB(Universal Serial Bus)通用串列匯流排:USB1.1規格支援兩種速率:低速(low speed)1.5Mbps和全速(full speed)12Mbps.
新的USB2.0規格除了支援原有的兩種速度外,還而外支援高速(high speed)480Mbps。

    USB host controller(USB主控器)必定是下列3種規格:
  1. UHCI : Intel公司提倡,UHCI線路比OHCI線路簡單多了,但是需要比較複雜的驅動程式,對CPU負擔也微重了些,UHCI採用I/O-mapped I/O方式(CPU使用I/O指令來存取USB controller),採用的廠商有Intel,VIA。
    在UHCI 中一個SOF 會出現一個Setup Token。
  2. OHCI:Compaq(康柏)公司主導,採用Memory-mapped I/O(CPU使用記憶體指令來存取USB controller),採用的廠商有Compaq,iMace,OPTi,SiS,Ali。
    在OHCI 中一個SOF 會可能出現三個Setup token。
  3. EHCI:USB規格,相容於UHCI,OHCI,只有USB2.0(EHCI)才提供高速480Mbps傳輸效率。

資料來源:
http://ynie.myweb.hinet.net/Linux/1.101.7.html
http://chamberplus.myweb.hinet.net/mp3_4.htm

2008年8月15日 星期五

ChamberPlus System Level Studio

一個介紹USB蠻不錯的地方喔!
(零)USB Q&A
(一)USB 系統軼事
(二)USB DIY 講座(一)
(三)USB DIY 講座(二)
(三)USB DIY 講座(三)USB特質
(四)USB DIY 講座(四)認識USB IC
(五)USB DIY 講座(五)Enumeration
(六)USB DIY 講座(六)USB I/O
(七)USB DIY 講座(七)USB Controller
(八)USB DIY 講座(八)Bulk Transfer基礎
(九)USB DIY 講座(九)基礎應用(一)
(十)USB DIY 講座(十)應用軟體簡介
(十一)USB DIY 講座(十一)驅動程式
(十二)USB DIY 講座(十二)實作範例USB ISP(一)

USB Discussion

這是一份義隆電子吳進男的USB簡介的簡報,寫的還不錯唷。
USB Discussion
View SlideShare presentation or Upload your own. (tags: usb)

還可以再參考一下底下這篇也寫得相當不錯喔!


其它參考網站:
USB Made Simple
USB之家

資料來源:
http://www.eecs.stut.edu.tw/~test3/ieet/word/951011.pdf
http://www.beyondlogic.org/usbnutshell/usb-in-a-nutshell.pdf

Introduction to AMBA Bus System

本篇文章主要是介紹ARM Limited.公司所推出的AMBA 協定(Advanced Micro-controller Bus Architecture)。AMBA 協定目前是open 且free 的,讀者可從ARM 的網站(www.arm.com)下載完整的Specification。這篇文章並沒有打算說明完整的AMBA 協定內容,詳細的Spec.還是請讀者閱讀ARM所提供的文件。原本的AMBA 協定包含了四大部分: AHB, ASB, APB, Test Methodology,限於篇幅的關係,我們挑選較重要的AHB, APB 加以基本的介紹,並探討AHB 的一些重要的特性。


資料來源:
http://tpe-wh3.dwins.net/download/member_file/2002/soc/2002-5-1.pdf

Android Architecture


舊版(M5-rc14)架構
What is Android?
Android的來源:
Google 併購 Android 公司後所推出的手機平台,採用 Linux 為作業系統。所以系統程式包含了 C 語言的標準函式庫,但應用程式主要以Java語言在Dalvik VM虛擬機器執行,應用程式開發環境以Eclipse為主,加上 Android Development Tools (ADT) Plugin。

Android的目的:
Android平台的主旨是提供一個應用程式架構(application framework),好讓各家OEM業者可不須從頭進行軟體開發,就能推出一系列的Android平台手機。

Android本身是一套軟體堆疊(Software Stack),或稱為「軟體疊層架構」,疊層主要分成三層:作業系統、中介軟體(Middleware,大陸方面稱為:中間件)、應用程式。想開發可在Android平台執行的應用程式,必須用Java程式語言撰寫才行。
中介軟體,凡是介於作業系統與應用程式間的,多概稱為中介軟體,Android的中介軟體可再細分出兩層,底層為函式庫(Library)及虛擬機器(Virtual Machine;VM),上層為應用程式框架(Application Framework)。

Android Presentation

Android 雖然用Java程式語言來開發、撰寫應用程式,但卻不使用一般大家目前在用的Java Runtime(J2ME)版本來執行Java程式,而是用Android自有的Android Runtime來執行。
    Android Runtime包含下面兩個核心。
  1. Core Libraries

  2. 核心函式庫裡頭已經包含了絕大多數Java程式語言所需要呼用的功效函式,接著每一個Android應用程式都會以自屬的process而且Android不是用一個Dalvik虛擬機器來同時執行多個Android應用程式,而是每個Android應用程式都用一個自屬的Dalvik虛擬機器來執行。
  3. Dalvik Virtual Machine

    • Dalvik虛擬機器是一種暫存器型態的虛擬機器。在撰寫開發時就已經設想用最少的記憶體資源來執行,以及前述的「同時可執行多個VM個體」。

    • Dalvik虛擬機器有許多地方是參考Java虛擬機器設計,Dalvik虛擬機器所執行的中介碼並非是Java虛擬機器所執行的Java Bytecode,同時也不直接執行Java的類別檔(Java Class File),而是依靠轉換工具將Java bytecode轉為Dalvik VM執行時特有的dex(Dalvik EXcutable)格式,稱為.dex。Dalvik VM相較於Java VM最大的不同在於Java VM為Stack-based,而Dalvik是register-based。 以技術層面考量Register-based VM的特性有個很大的好處,那就是對於現有主流的硬體架構,如此很容易與現有系統整合且最好化,而所需要的資源也相對較少。 甚至在硬體實作VM上會比較容易實現。 最重要的是Dalvik 並非是Java ME的實作,因此沒有Java ME授權相關的議題。

Android在應用程式框架中的軟體元件主要有10個,如Activity Manager(活動管理員)、Window Manager(視窗管理員)、Resource Manager(資源管理員)等,在此無法詳述每個元件的功效,因此以下挑數個重點元件來說明。

舉例來說,Content Providers(內容管理員)負責應用程式與應用程式間的資料存取傳遞,例如某一個應用程式可以去存取通訊錄應用程式內的聯絡人資料。或者呼用Resource Manager可存取的非程式碼資源,例如:當地性設定(該地貨幣、語言、時間格式)、圖像...等。

再者,應用程式若呼用Notification Manager(通知管理員),則可以在狀態列(Status Bar)的地方設計出應用程式自有的警示(Alert)訊息。至於Activity Manager則提供應用程式的生命週期管理。

更多詳細內容請看:Google Android裡頭有什麼?

Androidology - Part 1 of 3 - Architecture Overview
Androidology - Part 2 of 3 - Application Lifecycle

Androidology - Part 3 of 3 - APIs

A first hand look at building an Android application

Android Demo

    Android Linux Kernel:
     based on version 2.6.23 kernel
     ARCH_GOLDFISH configuration for ARM architecture
     Using EABI (Embedded ABI) Feature
     YAFFS2 File System
     OpenBinder IPC
    Kernel Patches
     ARM Architecture : ARMv5TE or later
    • For EABI Support
    • ex) ARM926EJ-S, XScale(PXA900), ARM1176JZ(F)-S
     YAFFS2 File System :
    • For NAND memory support
     OpenBinder
    • For IPCs among the equal applications on mobile devices
    • ex) Intent Broadcasting

參考資料:
Android 手機開發平台
研發觀點 開放手機革命:市場觀察與Android SDK
大廠在前 Android何時出頭?
真實平台移植過程
源始程式下載
Android on OMAP Kernel Patch
HTC TyTN II Vogue Linux
Android OS 現身 HTC TyTN II
Android - An Open Handset Alliance Project Documentation
Google Android裡頭有什麼?

這個網頁給大家參考一下吧!Google手機程式設計寫得蠻不錯的唷!
http://ccc.kmit.edu.tw/mybook/GooglePhoneProgramming/GooglePhoneProgramming.htm
[2008] 陳鍾誠, Google手機程式設計 (未出版草稿) -- 下載
http://ccc.kmit.edu.tw/mybook/GooglePhoneProgramming/GooglePhoneProgramming.doc

2008年8月14日 星期四

Doxygen and Graphviz

要由原始程式碼產生文件資料,需要安裝2個套件,分別為Doxygen及Graphviz。

(1)Doxygen套件是產生類別,屬性及函式功能說明文件之文件產生器程式庫,可協助程式設計者將特定格式之註解裡的說明文件導出,產生說明文件,目前Doxygen支援的程式語言包含C++/C/Java/Object-C/Python/IDL/Fortran/VHDL/PHP/C#等程式語言,產生出的文件格式可以是HTML/LATEX/PDF/RTF/PS/...等等。
如果你是Vim的愛用者,那可以再加個美化doxygen註解的外掛,如下圖:(2)Graphviz套件可以將指定格式的結構化資訊,轉換為抽象圖形或網路圖形,在Doxygen套件裡的圖形處理,是引用這個套件來產生類別階層圖。
當你使用Graphviz的套件又有在Doxygen裏設定好後啊,你就可以看到如下圖的結果當然啦,還沒有強到把整個程式的流程圖畫給你看,但最少把Caller跟Callee畫出來給你看囉,算是還蠻不錯的啦。

Fedora安裝
yum install doxygen doxygen-doxywizard graphviz graphviz-doc

Ubuntu安裝
apt-get install doxygen doxygen-doxywizard graphviz graphviz-doc

Windows就要自己去下載囉
http://www.stack.nl/~dimitri/doxygen/download.html
http://www.graphviz.org/Download_windows.php


蠻不錯的Windows的安裝使用教學
Doxygen文件產生器(一)安裝
Doxygen文件產生器(二)設定

Doxygen syntax coloring in Vim

Examples of output generated by doxygen

如果想了解如何寫doxygen的註解,請參考下面的網頁,如果連過去是亂碼,請手動調整"編碼"為繁體中文即可
Doxygen簡介

An event notification library - libevent

libevent
The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. Furthermore, libevent also support callbacks due to signals or regular timeouts.
libevent is meant to replace the event loop found in event driven network servers. An application just needs to call event_dispatch() and then add or remove events dynamically without having to change the event loop.
簡單的講就是可以變成事件驅動的方式寫程式

1. 從官網下載libevent的tarball後
2. ./configure && make && make install
3. echo "/usr/local/lib" >> /etc/ld.so.conf
4. ldconfig
5. 下載下面那個範例程式就可以試玩囉!

然後可以下載http://www.zhongguowen.com/demo/fhttpd/fhttpd.tar.gz
基於libevent的http server程式碼如下:

/**
* http server example, supported by libevent
*
* @autor lowellzhong<at>gmail<dot>com
* @website http://www.zhongguowen.com/
*/
#include <stdio.h>
#include <event.h>
#include <evhttp.h>


void fhttpd_gencb(struct evhttp_request * evreq, void * arg);


int main(int argc, char** argv)
{
struct event_base *evbase = NULL;
struct evhttp *evhttp = NULL;
unsigned short port = 8080;
const char *host = "0.0.0.0";


/* 1). event initialization */
evbase = event_init();
if(NULL == evbase)
{
perror("event_base_new");
return 1;
}


/* 2). event http initialization */
evhttp = evhttp_new(evbase);
if(NULL == evhttp)
{
perror("evhttp_new");
return 2;
}


/* 3). set general callback of http request */
evhttp_set_gencb(evhttp, &fhttpd_gencb, NULL);


/* 4). bind socket */
if(0 != evhttp_bind_socket(evhttp, host, port))
{
perror("evhttp_bind_socket");
return 3;
}
fprintf(stderr, "http server is running (%s:%d)\n", host, port);


/* 5). start http server */
if(0 == event_base_dispatch(evbase))
{
perror("event_base_dispatch");
return 4;
}


/* 6). free resource before exit */
evhttp_free(evhttp);
event_base_free(evbase);


return 0;
}


void fhttpd_gencb(struct evhttp_request * evreq, void * arg)
{
struct evbuffer *evbuff = NULL;


fprintf(stderr, "[%s] %s\n", evreq->remote_host, evreq->uri);


/* create a evbuffer variable for response */
evbuff = evbuffer_new();
if(NULL == evbuff)
{
perror("evbuffer_new");
return;
}


/* store response html in evbuffer */
evbuffer_add_printf(
evbuff,
"<html>\n<meta http-equiv=\"Content-Type\" content=\"text/html;"
"charset=GB2312\" />\n<head>\n<title>Welcome %s</title>\n</head>\n<body>\n"
"<h1>Welcome %s</h1>\n<p>your ip:%s, port %d</p>\n"
"<p>request uri: %s</p>\n<p><a href=\"<A href='http://wwww.zhongguowen.com\">www.zhongguowen.com</a></p></body>\n</html>",
evreq->remote_host, evreq->remote_host, evreq->remote_host,
evreq->remote_port, evreq->uri
);


/* change response charset, 'text/html; charset=ISO-8859-1' by default */
evhttp_add_header(evreq->output_headers, "Content-Type", "text/html; charset=GB2312");

/* send response html to client */
evhttp_send_reply(evreq, HTTP_OK, "", evbuff);


/* don't forget to release evbuffer */
evbuffer_free(evbuff);
}

browser連接上的執行結果

但也有人說libevent不好,要改用libev@@
libev官網如下:http://software.schmorp.de/pkg/libev.html

資料及參考來源:
libevent
http://www.monkey.org/~provos/libevent/
Gea-Suan Lin's Blog
http://blog.gslin.org/archives/2005/11/24/220/
羅威爾的博客 - 基於libevent的http server範例
http://www.zhongguowen.com/blog/?p=142
有簡易版DNS及http server的程式碼
http://unx.ca/log/2006/10/17/new-libevent-dns-and-http-support/

2008年8月13日 星期三

Socket Descriptors



這是一張Socket Descriptors的概念圖

Linux啟動時間的極限優化(轉貼)

Linux啟動時間的極限優化(轉貼)

在上次完成嵌入式應用的Linux裁減後,Linux的啟動時間仍需要 7s 左右,雖然勉強可以接受,但仍然沒有達到我個人所追求的目標——2s 以內。況且,在實際的商用環境中,設備可靠性的要求可是“5個9”(99.999%,即OOS時間低於5分鐘/年),這就意味著每減少一秒鐘Linux啟動(設備復位)時間,對可靠性都是一個明顯的提升。
言歸正傳,如何著手對Linux的啟動時間進行優化呢?
CELF(The Consumer Electronics Linux Forum)論壇為我們指引了一個方向。

(1)首先是對Linux啟動過程的跟蹤和分析,生成詳細的啟動時間報告。
較為簡單可行的方式是通過PrintkTime功能為啟動過程的所有內核信息增加時間戳,便於匯總分析。PrintkTime最早為CELF所提供的一個內核補丁,在後來的Kernel 2.6.11版本中正式納入標準內核。所以大家可能在新版本的內核中直接啟用該功能。如果你的Linux內核因為某些原因不能更新為2.6.11之後的版本,那麼可以參考CELF提供的方法修改或直接下載它們提供的補丁:http://tree.celinuxforum.org/CelfPubWiki/PrintkTimes
開啟PrintkTime功能的方法很簡單,隻需在內核啟動參數中增加“time”即可。當然,你也可以選擇在編譯內核時直接指定“Kernel hacking”中的“Show timing information on printks”來強制每次啟動均為內核信息增加時間戳。這一種方式還有另一個好處:你可以得到內核在解析啟動參數前所有信息的時間。因此,我選擇後一種方式。
當完成上述配置後,重新啟動Linux,然後通過以下命令將內核啟動信息輸出到文件:
dmesg -s 131072 > ktime
然後利用一個腳本“show_delta”(位於Linux源碼的scripts文件夾下)將上述輸出的文件轉換為時間增量顯示格式:
/usr/src/linux-x.xx.xx/scripts/show_delta ktime > dtime
這樣,你就得到了一份關於Linux啟動時間消耗的詳細報告。

(2)然後,我們就來通過這份報告,找出啟動中相對耗時的過程。
必須明確一點:報告中的時間增量和內核信息之間沒有必然的對應關係,真正的時間消耗必須從內核源碼入手分析。
這一點對於稍微熟悉編程的朋友來說都不難理解,因為時間增量隻是兩次調用printk之間的時間差值。通常來說,內核啟動過程中在完成一些耗時的任務,如創建hash索引、probe硬件設備等操作後會通過printk將結果打印出來,這種情況下,時間增量往往反映的是信息對應過程的耗時;但有些時候,內核是在調用printk輸出信息後才開始相應的過程,那麼報告中內核信息相應過程的時間消耗對應的是其下一行的時間增量;還有一些時候,時間消耗在了兩次內核信息輸出之間的某個不確定的時段,這樣時間增量可能就完全無法通過內核信息反應出來了。
所以,為了準確判斷真正的時間消耗,我們需要結合內核源碼進行分析。必要的時候,例如上述第三種情形下,還得自己在源碼中插入printk打印,以進一步確定實際的時間消耗過程。
以下是我上次裁減後Linux內核的啟動分析:

內核啟動總時間: 6.188s

關鍵的耗時部分:
1) 0.652s - Timer,IRQ,Cache,Mem Pages等核心部分的初始化
2) 0.611s - 內核與RTC時鐘同步
3) 0.328s - 計算Calibrating Delay(4個CPU核心的總消耗)
4) 0.144s - 校準APIC時鐘
5) 0.312s - 校準Migration Cost
6) 3.520s - Intel E1000網卡初始化

下面,將針對上述各部分進行逐一分析和化解。

(3)接下來,進行具體的分項優化。
CELF已經提出了一整套針對消費類電子產品所使用的嵌入式Linux的啟動優化方案,但是由於面向不同應用,所以我們隻能部分借鑒他們的經驗,針對自己面對的問題作出具體的分析和嘗試。
內核關鍵部分(Timer、IRQ、Cache、Mem Pages……)的初始化目前暫時沒有比較可靠和可行的優化方案,所以暫不考慮。
對於上面分析結果中的 2、3 兩項,CELF已有專項的優化方案:“RTCNoSync”和“PresetLPJ”。
前者通過屏蔽啟動過程中所進行的RTC時鐘同步或者將這一過程放到啟動後進行(視具體應用對時鐘精度的需求而定),實現起來比較容易,但需要為內核打補丁。似乎CELF目前的工作僅僅是去掉了該過程,而沒有實現所提到的“延後”處理RTC時鐘的同步。考慮到這個原因,我的方案中暫時沒有引入這一優化(畢竟它所帶來的時間漂移已經達到了“秒”級),繼續關注中。
後者是通過在啟動參數中強制指定LPJ值而跳過實際的計算過程,這是基於LPJ值在硬件條件不變的情況下不會變化的考慮。所以在正常啟動後記錄下內核信息中的“Calibrating Delay”數值後就可以在啟動參數中以下面的形式強制指定LPJ值了:
lpj=9600700
上面分析結果中的 4、5 兩項都是SMP初始化的一部分,因此不在CELF研究的範疇(或許將來會有採用多核的MP4出現?……),隻能自力更生了。研究了一下SMP的初始化代碼,發現“Migration Cost”其實也可以像“Calibrating Delay”採用預置的方式跳過校準時間。方法類似,最後在內核啟動參數中增加:
migration_cost=4000,4000
而Intel的網卡驅動初始化優化起來就比較麻煩了,雖然也是開源,但讀硬件驅動完全不比讀一般的C代碼,況且建立在如此膚淺理解基礎上的“優化”修改也實在難保万全。基於可靠性的考慮,我最終在兩次嘗試均告失敗後放棄了這一條路。那麼,換一個思維角度,可以借鑒CELF在“ParallelRCScripts”方案中的“並行初始化”思想,將網卡驅動獨立編譯為模塊,放在初始化腳本中與其它模塊和應用同步加載,從而消除Probe阻塞對啟動時間的影響。考慮到應用初始化也可能使用到網絡,而在我們的實際硬件環境中,只有eth0是供應用使用的,因此需要將第一個網口初始化的0.3s時間計算在內。
除了在我的方案中所遇到的上述各優化點,CELF還提出了一些你可能會感興趣的有特定針對性的專項優化,如:

ShortIDEDelays - 縮短IDE探測時長(我的應用場景中不包含硬盤,所以用不上)
KernelXIP - 直接在ROM或Flash中運行內核(考慮到兼容性因素,未採用)
IDENoProbe - 跳過未連接設備的IDE口
OptimizeRCScripts - 優化initrd中的linuxrc腳本(我採用了BusyBox更簡潔的linuxrc)
以及其它一些尚處於設想階段的優化方案,感興趣的朋友可以訪問CELF Developer Wiki了解詳情。

(4)優化結果
經過上述專項優化,以及對inittab、rcS腳本的冗餘裁減,整個Linux內核的啟動時間從優化前的 6.188s 下降到了最終的 2.016s,如果不包含eth0的初始化,則僅需 1.708s(eth0初始化可以和系統中間件及部分應用加載並行),基本達到了既定目標。與Kexec配合,可以大大降低軟件故障導致的復位時間,有效的提升了產品的可靠性。

參考資料:
http://www.celinuxforum.org/CelfPubWiki/BootupTimeResources

什麼是Zero-Copy?

什麼是Zero-Copy?
想要了解這個名詞是指什麼意思的話,讓我們從一個簡單的範例開始,這個範例是server讀取一個檔案,然後把檔案資料經由socket將資料傳送給client。
簡化範例程式如下:

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

Figure 1的圖說明了,這兩行程式實際的運作流程。(1)當執行read函式後,進入Kernel的syscall read(),檔案資料會經由DMA傳到Kernel管的Buffer,然後再由CPU將檔案資料搬到user buffer(tmp_buf)裏。
(2)執行write後,Kernel的syscall write(),再用CPU去把user buffer的資料搬到socket buffer裏,資料進到socket buffer後,會再經由DMA的方式將資料送出去給client。

問題分析:
從整個流程你應該會發現有一堆資料是重覆的,如果能把這些部份改掉,那就可以減少記憶體的消耗並增加效能。
以硬體的角度來看,其實是可以做到直接跳過記憶體的資料暫存的,直接把檔案資料傳到網路去,這樣子的功能是最直接最有效率的,但並不是所有的硬體都支援這種方式。

那我們是否可以減少user buffer這個部份呢?答案是肯定的,我們必需使用mmap來取代read的功能。
簡化範例程式如下:

tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);

Figure 2的圖說明了,這兩行程式實際的運作流程。(1)mmap執行後,如同read system call會將檔案資料經由DMA複製一份到kernel buffer,但不同的地方是,read()會需要把kernel buffer複製到user buffer,mmap()並不會,mmap的user buffer跟kernel buffer是同一個位置,所以mmap可以減少一次CPU copy。
(2)write()執行,把kernel buffer經由CPU複製到socket buffer,然後再經由DMA複製到client去。

問題分析:
但是使用mmap來改善並不是不需要付出代價的,當你使用mmap+write的方法時,假設同時又有另外一支程式對同一個檔案執行write時,將會引發SIGBUS的訊號,因為你執行了一個錯誤的記憶體存取,而它的預設處理行為是,系統砍掉你的程式,並且產生core dump。
當然一支網路程式不應該這麼做處理的,有兩個方式來處理這個情況:
(1)方法一:在SIGBUS訊號設置callback function,當SIGBUS出現時由這個新設置的handler來進行處理,但這種方式不好,因為它是事後去補救,並不是正規的解決方法。
(2)方法二:使用租約(lease)的方式(windows裏稱opportunistic locking機會鎖)
如:
if(fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
perror("kernel lease set signal");
return -1;
}
/* l_type can be F_RDLCK F_WRLCK */
if(fcntl(fd, F_SETLEASE, l_type)){
perror("kernel lease set type");
return -1;
}

更好的做法是使用sendfile函式
簡化範例程式如下:
sendfile(socket, file, len);
Figure 3的圖說明了,這行程式在Kernel 2.1版本的實際運作流程,sendfile直接取代了read/write兩個函式,並且減少了context switch的次數。(1)sendfile執行後,檔案資料會經由DMA傳給Kernel buffer,再由CPU複製到socket buffer去
(2)再把socket buffer的資料經由DMA傳給client去,所以執行了2次DMA Copy及1次的CPU Copy,總共3次的資料複製。

問題分析:
所以到目前為止我們已看到改善了不少地方了,但還是有一份重複的資料,那就是socket buffer,這份資料是否也可以不要呢?基本上也是可行的,只要硬體提供一點點幫助是可以做到的,那就是gather(聚合)的功能,這個功能主要的目的是,待發送端不要求存放的資料位址是連續的記憶體空間,可以是分散在記憶體的各個位置。所以到了2.4的kernel以後,socket buffer的descriptor做了一些變動,以支援gather的需求,而這個功能就是Zero-Copy。
這種方式不僅僅是減少了context switch而且也減少了buffer的使用,從上層的程式來講,也不需要做任何的變動。所以程式同樣的還是底下這行
sendfile(socket, file, len);
Figure 4的圖說明了,這行程式在Kernel 2.4版本的實際運作流程(1)sendfile執行後,檔案資料經由DMA傳給Kernel buffer,但已不會再把資料copy到socket buffer了,socket buffer只會去管有那些Kernel buffer的address及資料長度,所以圖是用apend。
(2)資料傳給client去也是用DMA的方式,但來源變成kernel buffer了。

所以就完成了,不需要CPU去搬資料,而是純DMA搬資料的Zero-Copy了。

原本資料來源:
http://www.linuxjournal.com/article/6345

測試文章:有人針對這篇文章去進行測試的實驗結果
雖然它測起來的傳輸速度似乎沒差,但應再加上CPU負載的資料及記憶體使用量去分析,如果速度一樣但CPU loading變輕,client端很多但記憶體使用量減少,那還是有很高的實用價值。
但還有個問題,網路傳輸不見得只有直接傳檔案啊,如果是傳非檔案的資料,sendfile還是適用嗎?
http://bbs.lpi-china.org/viewthread.php?tid=4292&extra=page%3D1

實現SIGIO驅動的socket範例

此程式由realtang所撰寫的,實現非同步的UDP Socket做法。
在接收到資料及檢測到異常時,引發SIGIO訊號。
SIGIO訊號是非同步傳輸的專用訊號。

Server端程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h> /*socket address struct*/
#include <arpa/inet.h> /*host to network convertion*/
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/ioctl.h>
#define MAX_TRANSPORT_LENTH 512

static int g_var = 0;
static int g_skt = 0;
void sig_handler(int signum)
{
char  buf[MAX_TRANSPORT_LENTH+1] = "";
int len = 0;
len = read(g_skt,buf,MAX_TRANSPORT_LENTH);
if (len<0)
{
perror("Read socket failed");
exit(-1);
}
else
{
printf("In SIGIO handler,got msg:%s\n",buf);
}
}

int main()
{
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family =  AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(50001);

signal(SIGIO,sig_handler);

g_skt = socket(AF_INET,SOCK_DGRAM,0);
if(g_skt == -1)
{
perror("Create socket failed");
exit(-1);
}

int len = sizeof(addr);
int ret = 0;

int on = 1;
    ret = fcntl(g_skt, F_SETOWN, getpid());//Set process or process group ID to receive SIGIO signals
if(-1 == ret)
{
perror("Fcntl F_SETOWN failed");
exit(-1);
}
    ret = ioctl(g_skt, FIOASYNC, &on);
if(-1 == ret)   
{
perror("Fcntl FIOASYNC failed");
exit(-1);
}   
    ret = ioctl(g_skt, FIONBIO, &on);
if(-1 == ret)   
{
perror("ioctl FIONBIO failed");
exit(-1);
}

ret = bind(g_skt,(struct sockaddr *)&addr,sizeof(addr));
if(-1 == ret)
{
perror("Bind socket failed");
exit(-1);
}
while(1)
{
printf("I am running\n");
sleep(2);
}
close(g_skt);
}

Client端程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>   /*socket address struct*/
#include <arpa/inet.h>   /*host to network convertion*/
#include <sys/socket.h>
#include <signal.h>
#define MAX_TRANSPORT_LENTH 512

int main()
{
 struct sockaddr_in addr;
 memset(&addr,0,sizeof(addr));
 addr.sin_family =  AF_INET;
 addr.sin_addr.s_addr = inet_addr("192.168.1.106");
 addr.sin_port = htons(50001);
 
 int sock;
 sock = socket(AF_INET,SOCK_DGRAM,0);
 if(sock == -1)
 {
  perror("Create socket failed");
  exit(-1);
 }
 
 int ret;
 ret = connect(sock,(struct sockaddr *)&addr,sizeof(addr));
 if(ret == -1)
 {
  perror("Connect socket failed");
  exit(-1);  
 }  
 while(1)
 {
  printf("Will send messge to server\n");
  write(sock,"Some unknown infomation\n",MAX_TRANSPORT_LENTH);
  sleep(1);
 }
 
}

daemon修改後的Server版本:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#define MAX_LENTH 1500

static int nqueue = 0;
void sigio_handler(int signum)
{
if (signum = SIGIO)
nqueue++;
return;
}

static recv_buf[MAX_LENTH];
int main(int argc, char *argv[])
{
int sockfd, on = 1;
struct sigaction action;
sigset_t newmask, oldmask;
struct sockaddr_in addr;

memset(&addr, 0, sizeof(addr));
addr.sin_family =  AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(50001);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Create socket failed");
exit(-1);
}
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("Bind socket failed");
exit(-1);
}

memset(&action, 0, sizeof(action));
action.sa_handler = sigio_handler;
action.sa_flags = 0;
sigaction(SIGIO, &action, NULL);
if (fcntl(sockfd, F_SETOWN, getpid()) == -1) {
perror("Fcntl F_SETOWN failed");
exit(-1);
}
if (ioctl(sockfd, FIOASYNC, &on) == -1) {
perror("Ioctl FIOASYNC failed");
exit(-1);
}

        sigemptyset(&oldmask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGIO);
while (1) {
int len;
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
while (nqueue == 0)
sigsuspend(&oldmask);
len = recv(sockfd, recv_buf, MAX_LENTH, MSG_DONTWAIT);
if (len == -1 && errno == EAGAIN)
nqueue = 0;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
if (len >= 0)
printf("recv %d byte(s)\n", len);

}
}

資料來源:
http://www.linuxsir.org/bbs/showthread.php?t=214611

Socket 基本多工方式介紹

Socket 多工輸入/輸出 - select() 功能呼叫
Socket 多工連線 - fork() 功能呼叫
Socket 多工連線 - fork() 流程圖
Socket 多工範例 - xinetd
Socket 多工範例 - xinetd 流程圖

資料來源:
http://140.127.138.46/tsnien/Teach_Manu/F7858/F7858_HTML/chap10/chap10-m.htm

2008年8月12日 星期二

UDP Server/Client 範例程式

好像不少人會找這個Sample Code, 小修改一下好了. 先前的Code有不少的Warning出現而且會Crash耶!
底下分別列出UDP Server及Client的範例程式.

UDP Server(udp-server.c) 利用 socket 介面設計網路應用程式程式啟動後等待 client 端連線,連線後印出對方之 IP 位址並顯示對方所傳遞之訊息,並回送給 Client 端。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/errno.h>
#include <string.h>
#include <arpa/inet.h>

#define SERV_PORT 5134

#define MAXNAME 1024

extern int errno;

main(){
        int socket_fd;   /* file description into transport */
        int recfd; /* file descriptor to accept        */
        int length; /* length of address structure      */
        int nbytes; /* the number of read **/
        char buf[BUFSIZ];
        struct sockaddr_in myaddr; /* address of this service */
        struct sockaddr_in client_addr; /* address of client    */
        /*
         *      Get a socket into UDP/IP
         */
        if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) <0) {
                perror ("socket failed");
                exit(EXIT_FAILURE);
        }
        /*
         *    Set up our address
         */
        bzero ((char *)&myaddr, sizeof(myaddr));
        myaddr.sin_family = AF_INET;
        myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        myaddr.sin_port = htons(SERV_PORT);

        /*
         *     Bind to the address to which the service will be offered
         */
        if (bind(socket_fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) <0) {
                perror ("bind failed\n");
                exit(1);
        }

        /*
         * Loop continuously, waiting for datagrams
         * and response a message
         */
        length = sizeof(client_addr);
        printf("Server is ready to receive !!\n");
        printf("Can strike Cntrl-c to stop Server >>\n");
        while (1) {
                if ((nbytes = recvfrom(socket_fd, &buf, MAXNAME, 0, (struct sockaddr*)&client_addr, (socklen_t *)&length)) <0) {
                        perror ("could not read datagram!!");
                        continue;
                }


                printf("Received data form %s : %d\n", inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));
                printf("%s\n", buf);

                /* return to client */
                if (sendto(socket_fd, &buf, nbytes, 0, (struct sockaddr*)&client_addr, length) < 0) {
                        perror("Could not send datagram!!\n");
                        continue;
                }
                printf("Can Strike Crtl-c to stop Server >>\n");
        }
}
UDP  Client (udp-client.c) 本程式啟動後向 Server (udp server) 要求連線,並送出某檔案給 Server,再由 Server 收回該檔案並顯示出該檔案的內容
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <string.h>

#define SERV_PORT 5134
#define MAXDATA   1024

#define MAXNAME 1024
int main(int argc, char **argv){
        int fd;     /* fd into transport provider */
        int i;     /* loops through user name */
        int length;    /* length of message */
        int size;    /* the length of servaddr */
        int fdesc;    /* file description */
        int ndata;    /* the number of file data */
        char data[MAXDATA]; /* read data form file */
        char data1[MAXDATA];  /*server response a string */
        char buf[BUFSIZ];     /* holds message from server */
        struct hostent *hp;   /* holds IP address of server */
        struct sockaddr_in myaddr;   /* address that client uses */
        struct sockaddr_in servaddr; /* the server's full addr */

        /*
         * Check for proper usage.
         */
        if (argc < 3) {
                fprintf (stderr, "Usage: %s host_name(IP address) file_name\n", argv[0]);
                exit(2);
        }
        /*
         *  Get a socket into UDP
         */
        if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
                perror ("socket failed!");
                exit(1);
        }
        /*
         * Bind to an arbitrary return address.
         */
        bzero((char *)&myaddr, sizeof(myaddr));
        myaddr.sin_family = AF_INET;
        myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        myaddr.sin_port = htons(0);

        if (bind(fd, (struct sockaddr *)&myaddr,
                                sizeof(myaddr)) <0) {
                perror("bind failed!");
                exit(1);
        }
        /*
         * Fill in the server's UDP/IP address
         */

        bzero((char *)&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(SERV_PORT);
        hp = gethostbyname(argv[1]);

        if (hp == 0) {
                fprintf(stderr, "could not obtain address of %s\n", argv[2]);
                return (-1);
        }
        bcopy(hp->h_addr_list[0], (caddr_t)&servaddr.sin_addr,
                        hp->h_length);

        /**開起檔案讀取文字 **/
        fdesc = open(argv[2], O_RDONLY);
        if (fdesc == -1) {
                perror("open file error!");
                exit (1);
        }
        ndata = read (fdesc, data, MAXDATA);
        if (ndata < 0) {
                perror("read file error !");
                exit (1);
        }
        data[ndata + 1] = '\0';

        /* 發送資料給 Server */
        size = sizeof(servaddr);
        if (sendto(fd, data, ndata, 0, (struct sockaddr*)&servaddr, size) == -1) {
                perror("write to server error !");
                exit(1);
        }
        /** 由伺服器接收回應 **/
        if (recvfrom(fd, data1, MAXDATA, 0, (struct sockaddr*)&servaddr, &size) < 0) {
                perror ("read from server error !");
                exit (1);
        }
        /* 印出 server 回應 **/
        printf("%s\n", data1);

}
部份編譯時遇到的Warning問題
Warning 1:
warning: incompatible implicit declaration of built-in function ‘exit’ [enabled by default]
加入#include <stdlib.h>
Warning 2:
warning: incompatible implicit declaration of built-in function ‘bzero’ [enabled by default]
加入#include <string.h>
執行結果如下: udp server side:

udp client side:
資料來源: 底下我原本參考的地方似乎已經失連了.
http://140.127.138.46/tsnien/Teach_Manu/F7858/Aid/udpserver.c
http://140.127.138.46/tsnien/Teach_Manu/F7858/Aid/udpclient.c

TCP Server/Client 範例程式

Server的PortNumber隨便取一個大1204且不在/etc/services中出現的號碼即可
/***  TCP Server tcpserver.c
 *
 * 利用 socket 介面設計網路應用程式
 * 程式啟動後等待 client 端連線,連線後印出對方之 IP 位址
 * 並顯示對方所傳遞之訊息,並回送給 Client 端。
 *
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/errno.h>


#define SERV_PORT 5134


#define MAXNAME 1024


extern int errno;


main()
{
 int socket_fd;      /* file description into transport */
 int recfd;     /* file descriptor to accept        */
 int length;     /* length of address structure      */
 int nbytes;     /* the number of read **/
 char buf[BUFSIZ];
 struct sockaddr_in myaddr; /* address of this service */
 struct sockaddr_in client_addr; /* address of client    */
/*                             
 *      Get a socket into TCP/IP
 */
 if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) <0) {
  perror ("socket failed");
  exit(1);
 }
/*
 *    Set up our address
 */
 bzero ((char *)&myaddr, sizeof(myaddr));
 myaddr.sin_family = AF_INET;
 myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 myaddr.sin_port = htons(SERV_PORT);


/*
 *     Bind to the address to which the service will be offered
 */
 if (bind(socket_fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) <0) {
  perror ("bind failed");
  exit(1);
 }


/*
 * Set up the socket for listening, with a queue length of 5
 */
 if (listen(socket_fd, 20) <0) {
  perror ("listen failed");
  exit(1);
 }
/*
 * Loop continuously, waiting for connection requests
 * and performing the service
 */
 length = sizeof(client_addr);
 printf("Server is ready to receive !!\n");
 printf("Can strike Cntrl-c to stop Server >>\n");
 while (1) {
  if ((recfd = accept(socket_fd,
      (struct sockaddr_in *)&client_addr, &length)) <0) {
   perror ("could not accept call");
   exit(1);
         }


  if ((nbytes = read(recfd, &buf, BUFSIZ)) < 0) {
   perror("read of data error nbytes !");
   exit (1);
  }
   
  printf("Create socket #%d form %s : %d\n", recfd,
  inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));
  printf("%s\n", &buf);
 
  /* return to client */
  if (write(recfd, &buf, nbytes) == -1) {
   perror ("write to client error");
   exit(1);
  }
  close(recfd);
  printf("Can Strike Crtl-c to stop Server >>\n");
 }
}

/*  TCP  Client program (tcpclient.c)
 *
 *  本程式啟動後向 Server (tcpserver) 要求連線,
 *  並送出某檔案給 Server,再由 Server 收回該檔案
 *  並顯示出該檔案的內容
 *
 */


#include <stdio.h>
#include <netdb.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>


#define SERV_PORT 5134
#define MAXDATA   1024


#define MAXNAME 1024
main(argc, argv)
int argc;
char **argv;
{
 int fd;      /* fd into transport provider */
 int i;      /* loops through user name */
 int length;     /* length of message */
 int fdesc;     /* file description */
 int ndata;     /* the number of file data */
 char data[MAXDATA]; /* read data form file */
 char data1[MAXDATA];  /*server response a string */
 char buf[BUFSIZ];     /* holds message from server */
 struct hostent *hp;   /* holds IP address of server */
 struct sockaddr_in myaddr;   /* address that client uses */
 struct sockaddr_in servaddr; /* the server's full addr */


 /*
  * Check for proper usage.
  */
 if (argc < 3) {
  fprintf (stderr,
   "Usage: %s host_name(IP address) file_name\n", argv[0]);
  exit(2);
 }
 /*
  *  Get a socket into TCP/IP
  */
 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  perror ("socket failed!");
  exit(1);
 }
 /*
  * Bind to an arbitrary return address.
  */
 bzero((char *)&myaddr, sizeof(myaddr));
 myaddr.sin_family = AF_INET;
 myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 myaddr.sin_port = htons(0);


 if (bind(fd, (struct sockaddr *)&myaddr,
   sizeof(myaddr)) <0) {
  perror("bind failed!");
  exit(1);
 }
 /*
  * Fill in the server's address and the data.
  */


 bzero((char *)&servaddr, sizeof(servaddr));
 servaddr.sin_family = AF_INET;
 servaddr.sin_port = htons(SERV_PORT);


 hp = gethostbyname(argv[1]);
 if (hp == 0) {
  fprintf(stderr,
   "could not obtain address of %s\n", argv[2]);
  return (-1);
 }


 bcopy(hp->h_addr_list[0], (caddr_t)&servaddr.sin_addr,
  hp->h_length);
 /*
  * Connect to the server連線.
  */
 if (connect(fd, (struct sockaddr *)&servaddr,
    sizeof(servaddr)) < 0) {
  perror("connect failed!");
  exit(1);
 }
 /**開起檔案讀取文字 **/
 fdesc = open(argv[2], O_RDONLY);
 if (fdesc == -1) {
  perror("open file error!");
  exit (1);
 }
 ndata = read (fdesc, data, MAXDATA);
 if (ndata < 0) {
  perror("read file error !");
  exit (1);
 }
 data[ndata] = '\0';


 /* 發送資料給 Server */
 if (write(fd, data, ndata) == -1) {
  perror("write to server error !");
  exit(1);
 }
 /** 由伺服器接收回應 **/
 if (read(fd, data1, MAXDATA) == -1) {
  perror ("read from server error !");
  exit (1);
 }
 /* 印出 server 回應 **/
 printf("%s\n", data1);


 close (fd);
}



 

Makefileall:tcpserver tcpclient
tcpserver:tcpserver.c
gcc $^ -o $@
tcpclient:tcpclient.c
gcc $^ -o $@


底下是另一個TCP Client的範例
用C實作TCP socket連接/讀/寫,使用fcntl設定nonblocking以處理connect overtime情況,使用select來處理socket讀寫overtime。

/* 
 
 * on Unix: 
 
 *    cc -c connector.c 
 
 *    cc -o connector connector.o 
 
 * 
 
 * on Windows NT: 
 
 *    open connector.c in Visual Studio 
 
 *    press 'F7' to link -- a project to be created 
 
 *    add wsock32.lib to the link section under project setting 
 
 *    press 'F7' again 
 
 * 
 
 * running: 
 
 *    type 'connector' for usage 
 
 */ 
 
 
#include <stdio.h>  
 
#include <stdlib.h>  
 
#include <string.h>  
 
#include <stdarg.h>  
 
#include <errno.h>  
 
#include <fcntl.h>  
 
#ifdef WIN32  
 
#include <winsock2.h>  
 
#else  
 
#include <unistd.h>  
 
#include <sys/types.h>  
 
#include <sys/socket.h>  
 
#include <sys/ioctl.h>  
 
#include <netinet/in.h>  
 
#include <arpa/inet.h>  
 
#include <netdb.h>  
 
#endif  
 
 
#ifndef INADDR_NONE  
 
#define INADDR_NONE     0xffffffff  
 
#endif  
 
#define MAX_STRING_LEN  1024  
 
#define BUFSIZE  2048  
 
 
#ifndef WIN32  
 
#define SOCKET int  
 
#else  
 
#define errno WSAGetLastError()  
 
#define close(a) closesocket(a)  
 
#define write(a, b, c) send(a, b, c, 0)  
 
#define read(a, b, c) recv(a, b, c, 0)  
 
#endif  
 
 
char buf[BUFSIZE];  
 
 
static char i_host[MAX_STRING_LEN];  /* site name */ 
 
static char i_port[MAX_STRING_LEN];  /* port number */ 
 
 
void err_doit(int errnoflag, const char *fmt, va_list ap);  
 
void err_quit(const char *fmt, ...);  
 
int tcp_connect(const char *host, const unsigned short port);  
 
void print_usage();  
 
 
//xnet_select x defines  
 
#define READ_STATUS  0  
 
#define WRITE_STATUS 1  
 
#define EXCPT_STATUS 2  
 
 
/* 
 
s    - SOCKET 
 
sec  - timeout seconds 
 
usec - timeout microseconds 
 
x    - select status 
 
*/ 
 
SOCKET xnet_select(SOCKET s, int sec, int usec, short x)  
 
{  
 
 int st = errno;  
 
 struct timeval to;  
 
 fd_set fs;  
 
 to.tv_sec = sec;  
 
 to.tv_usec = usec;  
 
 FD_ZERO(&fs);  
 
 FD_SET(s, &fs);  
 
 switch(x){  
 
  case READ_STATUS:  
 
  st = select(s+1, &fs, 0, 0, &to);  
 
  break;  
 
  case WRITE_STATUS:  
 
  st = select(s+1, 0, &fs, 0, &to);  
 
  break;  
 
  case EXCPT_STATUS:  
 
  st = select(s+1, 0, 0, &fs, &to);  
 
  break;  
 
 }  
 
 return(st);  
 
}  
 
 
int tcp_connect(const char *host, const unsigned short port)  
 
{  
 
    unsigned long non_blocking = 1;  
 
    unsigned long blocking = 0;  
 
    int ret = 0;  
 
    char * transport = "tcp";  
 
    struct hostent      *phe;   /* pointer to host information entry    */ 
 
    struct protoent *ppe;       /* pointer to protocol information entry*/ 
 
    struct sockaddr_in sin;     /* an Internet endpoint address  */ 
 
    SOCKET s;                    /* socket descriptor and socket type    */ 
 
    int error;  
 
 
#ifdef WIN32  
 
    {  
 
        WORD wVersionRequested;  
 
        WSADATA wsaData;  
 
        int err;  
 
   
 
        wVersionRequested = MAKEWORD( 2, 0 );  
 
   
 
        err = WSAStartup( wVersionRequested, &wsaData );  
 
        if ( err != 0 ) {  
 
            /* Tell the user that we couldn't find a usable */ 
 
            /* WinSock DLL.                               */ 
 
            printf("can't initialize socket library\n");  
 
            exit(0);  
 
        }  
 
    }  
 
#endif      
 
      
 
    memset(&sin, 0, sizeof(sin));  
 
    sin.sin_family = AF_INET;  
 
      
 
    if ((sin.sin_port = htons(port)) == 0)  
 
        err_quit("invalid port \"%d\"\n", port);  
 
      
 
    /* Map host name to IP address, allowing for dotted decimal */ 
 
    if ( phe = gethostbyname(host) )  
 
        memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);  
 
    else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE )  
 
        err_quit("can't get \"%s\" host entry\n", host);  
 
      
 
    /* Map transport protocol name to protocol number */ 
 
    if ( (ppe = getprotobyname(transport)) == 0)  
 
        err_quit("can't get \"%s\" protocol entry\n", transport);  
 
      
 
    /* Allocate a socket */ 
 
    s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);  
 
    if (s < 0)  
 
        err_quit("can't create socket: %s\n", strerror(errno));  
 
      
 
    /* Connect the socket with timeout */ 
 
#ifdef WIN32  
 
    ioctlsocket(s,FIONBIO,&non_blocking);  
 
#else  
 
    ioctl(s,FIONBIO,&non_blocking);  
 
#endif  
 
    //fcntl(s,F_SETFL, O_NONBLOCK);  
 
    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1){  
 
        struct timeval tv;   
 
        fd_set writefds;  
 
        // 设置连接超时时间  
 
        tv.tv_sec = 10; // 秒数  
 
        tv.tv_usec = 0; // 毫秒  
 
        FD_ZERO(&writefds);   
 
        FD_SET(s, &writefds);   
 
        if(select(s+1,NULL,&writefds,NULL,&tv) != 0){   
 
            if(FD_ISSET(s,&writefds)){  
 
                int len=sizeof(error);   
 
                //下面的一句一定要,主要针对防火墙   
 
                if(getsockopt(s, SOL_SOCKET, SO_ERROR,  (char *)&error, &len) < 0)  
 
                    goto error_ret;   
 
                if(error != 0)   
 
                    goto error_ret;   
 
            }  
 
            else 
 
                goto error_ret; //timeout or error happen   
 
        }  
 
        else goto error_ret; ;   
 
 
#ifdef WIN32  
 
        ioctlsocket(s,FIONBIO,&blocking);  
 
#else  
 
        ioctl(s,FIONBIO,&blocking);  
 
#endif  
 
 
    }  
 
    else{  
 
error_ret:  
 
        close(s);  
 
        err_quit("can't connect to %s:%d\n", host, port);  
 
    }  
 
    return s;  
 
}  
 
 
void err_doit(int errnoflag, const char *fmt, va_list ap)  
 
{  
 
    int errno_save;  
 
    char buf[MAX_STRING_LEN];  
 
 
    errno_save = errno;   
 
    vsprintf(buf, fmt, ap);  
 
    if (errnoflag)  
 
        sprintf(buf + strlen(buf), ": %s", strerror(errno_save));  
 
    strcat(buf, "\n");  
 
    fflush(stdout);  
 
    fputs(buf, stderr);  
 
    fflush(NULL);  
 
    return;  
 
}  
 
 
/* Print a message and terminate. */ 
 
void err_quit(const char *fmt, ...)  
 
{  
 
    va_list ap;  
 
    va_start(ap, fmt);  
 
    err_doit(0, fmt, ap);  
 
    va_end(ap);  
 
    exit(1);  
 
}  
 
 
#ifdef WIN32  
 
char *optarg;  
 
 
char getopt(int c, char *v[], char *opts)  
 
{  
 
    static int now = 1;  
 
    char *p;  
 
 
    if (now >= c) return EOF;  
 
 
    if (v[now][0] == '-' && (p = strchr(opts, v[now][1]))) {  
 
        optarg = v[now+1];  
 
        now +=2;  
 
        return *p;  
 
    }  
 
 
    return EOF;  
 
}  
 
 
#else  
 
extern char *optarg;  
 
#endif  
 
 
#define required(a) if (!a) { return -1; }  
 
 
int init(int argc, char *argv[])  
 
{  
 
    char c;  
 
    //int i,optlen;  
 
    //int slashcnt;  
 
 
    i_host[0]  =  '\0';  
 
    i_port[0]  =  '\0';  
 
 
    while ((c = getopt(argc, argv, "h:p:?")) != EOF) {  
 
        if (c == '?')  
 
            return -1;  
 
        switch (c) {   
 
        case 'h':  
 
            required(optarg);  
 
            strcpy(i_host, optarg);  
 
            break;  
 
        case 'p':  
 
            required(optarg);  
 
            strcpy(i_port, optarg);  
 
            break;  
 
        default:  
 
            return -1;  
 
        }  
 
    }  
 
 
    /*  
 
     * there is no default value for hostname, port number,  
 
     * password or uri 
 
     */ 
 
    if (i_host[0] == '\0' || i_port[0] == '\0')  
 
        return -1;  
 
 
    return 1;  
 
}  
 
 
void print_usage()  
 
{  
 
    char *usage[] =  
 
    {  
 
        "Usage:",  
 
        "    -h    host name",  
 
        "    -p    port",  
 
        "example:",  
 
        "    -h 127.0.0.1 -p 4001",  
 
    };     
 
    int i;  
 
 
    for (i = 0; i < sizeof(usage) / sizeof(char*); i++)  
 
        printf("%s\n", usage[i]);  
 
      
 
    return;  
 
}  
 
 
int main(int argc, char *argv[])  
 
{  
 
    SOCKET fd;  
 
    int n;  
 
 
    /* parse command line etc ... */ 
 
    if (init(argc, argv) < 0) {  
 
        print_usage();  
 
        exit(1);  
 
    }  
 
 
    buf[0] = '\0';  
 
 
    /* pack the info into the buffer */       
 
    strcpy(buf, "HelloWorld");  
 
 
    /* make connection to the server */ 
 
    fd = tcp_connect(i_host, (unsigned short)atoi(i_port));  
 
 
    if(xnet_select(fd, 0, 500, WRITE_STATUS)>0){  
 
        /* send off the message */ 
 
        write(fd, buf, strlen(buf));  
 
    }  
 
    else{  
 
        err_quit("Socket I/O Write Timeout %s:%s\n", i_host, i_port);  
 
    }  
 
 
    if(xnet_select(fd, 3, 0, READ_STATUS)>0){  
 
        /* display the server response */ 
 
        printf("Server response:\n");  
 
        n = read(fd, buf, BUFSIZE);  
 
        buf[n] = '\0';  
 
        printf("%s\n", buf);  
 
    }  
 
    else{  
 
        err_quit("Socket I/O Read Timeout %s:%s\n", i_host, i_port);  
 
    }  
 
    close(fd);  
 
 
#ifdef WIN32  
 
    WSACleanup();  
 
#endif  
 
 
    return 0;  
 
}

資料來源:
http://140.127.138.46/tsnien/Teach_Manu/F7858/Aid/tcpserver.c
http://140.127.138.46/tsnien/Teach_Manu/F7858/Aid/tcpclient.c
http://www.zeali.net/entry/13

一個小故事讓我們明白資金流通的意義

“又是炎熱小鎮慵懶的一天。太陽高掛,街道無人,每個人都債台高築,靠信用度日。這時,從外地來了一位有錢的旅客,他進了一家旅館,拿出一張1000 元鈔票放在櫃檯,說想先看看房間,挑一間合適的過夜,就在此人上樓的時候---- 店主抓了這張1000 元鈔,跑到隔壁屠戶那裡支付了他欠的肉錢...