2009年3月29日 星期日

今天打羽球

今天跟實驗室助理還有一個大四學弟、碩一學弟、老弟一起去助理家附近打羽球。
其實,打的還可以啦~

不過,今天我想說的是,一開始打的還算可以,我比較擅長的是打遠球。
而最後,感覺這樣好像沒有什麼技巧可言,所以,開始練習吊球,也就是打近球。

最後連續輸了兩場,
其實,因為跟女生打,所以,比較沒有用心打,等到最後要追回來時,早就追不回來了~

老弟在旁邊說,既然要打球就要贏球,但是,輸球(贏球)要有風度!
想想是確實是這樣啦,心理還是有一點悔恨啦~

下次要讓人,還是要真的有實力再讓人,若沒有實力還是跟別人硬拼啦,才不會比較後悔~

2009年3月28日 星期六

Four types of network-related information





InformationData fileStructureKeyed lookup functions
Hosts/etc/hostshostentgethostbyaddr, gethostbyname
Networks/etc/networksnetentgetnetbyaddr,getnetbyname
Protocols/etc/protocolsprotoentgetprotobyname,getprotobynumber
Services/etc/servicesserventgetservbyname,getservbyport

DRBL企鵝龍 - 備份linux與windows的免費軟體

在windows下有ghost可以使用
而在linux下本身有dd這一個指令可以使用,但是,dd的缺點是1比1的備份,它原本主要的功能是建立映像檔。
而我猜企鵝龍可以具有壓縮的備份,因此,以後若真的有空,就拿來玩一下吧~
而且,它還可以備份windows哩

參考資料:
企鵝龍
Clonezilla Live對Windows系統的備份與還原
Clonezilla Live - 免安裝的單機版還原系統

2009年3月26日 星期四

重建xp的mbr

學弟的電腦出現一個問題,會出現grub找不到,但是,他只有灌xp啊~
原因就是之前linux的mbr被grub占住了。
我猜有兩個辦法可以解決,第把mbr刪掉(用spfdisk)
第二個是用xp的修覆光碟

進入Recovery Console後,修復MBR的流程如下:

1. 選擇要登入哪一個Windows安裝,一般來說只會有一個選項而已,也就是
"1: C:\WINDOWS",所以就只要輸入1就可以了。
2. 輸入系統管理員的密碼,如果當初沒有設定密碼就直接按下Enter,如果不知道密碼,那就沒有辦法了。
3. 輸入fixmbr,程式會詢問是不是真的要重新建立MBR,反正沒有別的路走了,所以也只能鍵入Y後按下Enter。
4. 輸入exit離開程式並重新開機。

一般來說,就只要進行這些步驟就可以了,但是我的經驗中仍然有碰過別的問題
如果開機的時候出現以下訊息:

下列檔案遺漏或損毀,無法啟動 Windows:
《Windows root》\sytem32\hal.dll.
請重新安裝一份上列檔案的拷貝
那就要再進行額外的動作修復。

基本上的修復流程和之前修復MBR的流程一樣,但是在鍵入"fixmbr"的地方改成鍵入"bootcfg /rebuild",接下來程式會自動掃描各個硬碟分割區,看看到底安裝了幾個系統。找到了想要使用的系統之後按Y將它新增到開機清單,再輸入載入識別元,也就是你想要它顯示的名字,一般來說就是輸入"Windows XP",然後輸入OS載入選項,也就是剛剛掃描結果的選項代號,最後也是輸入exit離開,全部的流程到此結束。

參考資料:
在Windows XP SP2下修復MBR

Socket Options

#include <sys/socket.h>
int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen);
int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t optlen);
Both return:0 if OK, -1 on error

主要用來設定socket的一些行為,若想要更深入的使用socket的話,這兩個函數一定要會使用。
而SCTP一共有17種Socket Options
1. SCTP_ADAPTION_LAYER
這一個不是很懂。
2. SCTP_ASSOCINFO
這一個主要有三個目的
(i)去檢索目前某一個存在的association
(ii)去改變目前存在的association的相關參數
(iii)去設定未來所要建立的association的預設參數
會用到的資料結構
struct sctp_assocparams {
sctp_assoc_t sasoc_assoc_id;
uint16_t sasoc_asocmaxrxt;
uint16_t sasoc_number_peer_destinations;
uint32_t sasoc_peer_rwnd;
uint32_t sasoc_local_rwnd;
uint32_t sasoc_cookie_life;
};

3. SCTP_AUTOCLOSE
當idle超過一段時間,則自動切斷連線
4. SCTP_DEFAULT_SEND_PARAM
設定當此association_id要傳送資料給對方時,則要設定哪一個ip為主要的primary ip(因為,SCTP有multi-homing的特性),會用到的資料結構為
struct sctp_sndrcvinfo {
uint16_t sinfo_stream;
uint16_t sinfo_ssn;
uint16_t sinfo_flags;
uint32_t sinfo_ppid;
uint32_t sinfo_context;
uint32_t sinfo_timetolive;
uint32_t sinfo_tsn;
uint32_t sinfo_cumtsn;
sctp_assoc_t sinfo_assoc_id;
};

其中sinfo_flags有以下數值
SCTP_ABORT:Invoke ABORTIVE termination of the association
SCTP_ADD_OVER:Specify that SCTP should override the primary address and use the provided address instead
SCTP_EOF:Invoke graceful termination after the sending of this message
SCTP_UNORDERED:Specify that this message uses the unordered message service
5. SCTP_DISABLE_FRAGMENTS
當要傳送的封包太大時,必需要分割。而預設是有份割的。
若設定預設不分割,當資料太大時,會回傳EMSGSIZE的錯誤訊息
6. SCTP_EVENT
非常重要。
用來設定是否額外傳送資料以外的訊息
struct sctp_event_subscribe {
uint8_t sctp_data_io_event;
uint8_t sctp_association_event;
uint8_t sctp_address_event;
uint8_t sctp_send_failure_event;
uint8_t sctp_peer_error_event;
uint8_t sctp_shutdown_event;
uint8_t sctp_partial_delivery_event;
uint8_t sctp_adaption_layer_event;
};

7. SCTP_GET_PEER_ADDR_INFO
用來讀取對方某一個位址(ip)的資訊
會用到的資料結構為
struct sctp_paddrinfo {
sctp_assoc_t spinfo_assoc_id;
struct sockaddr_storage spinfo_address;
int32_t spinfo_state;
uint32_t spinfo_cwnd;
uint32_t spinfo_srtt;
uint32_t spinfo_rto;
uint32_t spinfo_mtu;
};

spinfo_state:有以下可能數值
SCTP_ACTIVE:Address is active and reachable
SCTP_INACTIVE:Address cannot currently be reached
SCTP_ADDR_UNCONFIRMED:No heartbeat or data has confirmed this address
8. SCTP_I_WANT_MAPPED_V4_ADDR
在傳送給Application前,會把ip4的位址轉換成ipv6的位址(預設是enabled)
9. SCTP_INITMSG
用來設定要傳送INIT message的參數
會用到以下資料結構
struct sctp_initmsg {
uint16_t sinit_num_ostreams;
uint16_t sinit_max_instreams;
uint16_t sinit_max_attempts;
uint16_t sinit_max_init_timeo;
};

10. SCTP_MAXBURST
不太了解
11. SCTP_MAXSEG
設定segment的最大值
12. SCTP_NODELAY
設定SCTP傳送是不DELAY的,預設是關的
13. SCTP_PEER_ADDR_PARAMS
用來取得或是改變association的參數值。
資料結構
struct sctp_paddrparams {
sctp_assoc_t spp_assoc_id;
struct sockaddr_storage spp_address;
uint32_t spp_hbinterval;
uint16_t spp_pathmaxrxt;
};

14. SCTP_PRIMARY_ADDR
用來決定某一個association要送過去的address主要是哪一個
要用到的資料結構
struct sctp_setprim {
sctp_assoc_t ssp_assoc_id;
struct sockaddr_storage ssp_addr;
};

15. SCTP_RTOINFO
設定封包來回的時間
struct sctp_rtoinfo {
sctp_assoc_t srto_assoc_id;
uint32_t srto_initial;
uint32_t srto_max;
uint32_t srto_min;
};

16. SCTP_SET_PEER_PRIMARY_ADDR
請peer設定一個IP Address為其peer的primary address
struct sctp_setpeerprim {
sctp_assoc_t sspp_assoc_id;
struct sockaddr_storage sspp_addr;
};

17. SCTP_STATUS
檢索目前association的情況
struct sctp_status {
sctp_assoc_t sstat_assoc_id;
int32_t sstat_state;
u_int32_t sstat_rwnd;
u_int32_t sstat_unackdata;
u_int32_t sstat_penddata;
u_int32_t sstat_instrms;
u_int32_t sstat_outstrms;
u_int32_t sstat_fragmentation_point;
struct sctp_paddrinfo sstat_primary;
};

2009年3月25日 星期三

60歲後,你最後悔的會是什麼?

60歲後,你最後悔的會是什麼?

比利時一家雜誌,曾對全國60歲以上的老人進行了一次問卷調查,

調查的題目是:你最後悔的是什麼?並列出了十幾項生活中容易後悔的事情,供被調查者選擇。於是,就得出了這樣結果…

一.72% 的人後悔年輕時努力不夠,以至事業無成:
由此我想起一個故事:一個40歲的人被醫師告知罹患了絕症,最多再活三年時間。因此,他為了使自己最後的生命更有意義,擬出一項『三年要做十件事』的工作計劃。
其中包括寫一本書、學一門外語、搞一項發明、辨一座工廠、遊 30 座名山、賞 50 座城市等等,而且,計劃定出後便立即付諸行動。在過了二年零八個月的時候,10 項計劃目標全都完成。

可當他再到醫院復診時,卻發現是醫師當時拿錯了病歷,自己根本沒有患病。
所謂事業,並不是資本主義體制主流價值所界定的財富、名氣與權位,而是忠實地依照自己的志趣、理想與人生觀所付出努力而獲得的成就,此一成就不一定需要來自既有社會機制的公開性肯定,而更需要自我的審視與滿足,就如同『三年要做十件事』的工作計劃一般。

二.有 67% 的人後悔年輕時,錯誤地選擇了職業:
應該說,錯誤不在於選擇了什麼職業,而在於選擇了安穩,或屈從了現實,放棄了志趣、特長與理想。在資本主義體制裡,不論是處於經濟增長階段,或是在當前全球資本主義危機的時代,謀生和追求物質富裕的生活,是絕大多數人們的不二選擇,即便所選擇的職業壓抑了特長、與志趣相違、背離理想,甚至,明知從事該項職業形同剝削壓迫別人權益的共犯,初期難以適應,時間一久也就麻痺無覺,還會為自己的職業「罪惡」振振有辭地辯解!

當然,在台灣,由於從小到大的教育體制都是為資本主義經濟與資本家集團的利潤而服務,而不是為了開發受教育者的生命潛能、引導認識自己的志趣與特長、啟發對理想的堅持與追求,所以,更根本的問題是,人們從來不知自己在資本主義體制所設定極其有限的選擇外,還有更多元、更多樣、更多種的可能性!

三.有 63% 的人後悔對子女教育不夠或方法不當:
因為台灣深受「文憑主義」的宰制,以及依循既有教育體制與社會體制的價值取向,即使年輕一代的父母,對於子女的教育態度仍著重於考試、成績與升學,目的也無非是將來能獲得更好的功名利碌;並且,縱容小孩養成「消費主義」的習性,形塑「物化」的自我本位心態,以成為市場經濟新的購買力來源。這些教育方式,除了複製了自己的價值觀系統給下一代,以使其提早適應馴化於資本主義的統治秩序外,並不能激發子女的生命自主性動力,以邁向實現自我的征途;培育思考獨立性與批判性的公民意識,以積極參與公共事務.......
其實,對子女教育不夠或方法不當的後悔,是反應了對自己過往生命歷程的後
悔!

四.有 58% 的人後悔鍛鍊身體不足:
忘了是誰說過這樣一句話:「60 歲以前想用身體換一切,60 歲以後想用一切換身體。」世界之上,還有什麼東西能比身體健康更寶貴?不過,經由運動以鍜鍊身體是需要花費時間的,在工作繁忙的現代社會,要定期運動談何容易?

五.最有趣的是,只有 11% 的人後悔沒有賺到更多的錢:
11% 這麼低的比例,大既只有實施高度社會福利制度的比利時或歐洲國家才有可能,若在所有人權保障都由個人承擔的台灣,這肯定是排名第一位最後悔的選項!

想像自己已是六十歲吧!若不想後悔,趁現在趕緊重新思考和做出決斷?

--
You got a dream, you gotta protect it. People can't do something themselves,
they wanna tell you that you can't do it. You want something? Go get it.
Period. 如果你有夢想的話,你就得捍衛它,那些自己沒有成才的人會説你也不能成才。
你想要得到什麼的話,就得努力去做,說完了。--當幸福來敲門
--

2009年3月24日 星期二

如何在C/C++把宣告函數與實作分開在不同的函數

如何在C/C++把函式的宣告與實作分開呢?

在C++中,可以用以下方式把類別的實作與函式分開。

宣告:
foo.h

#ifndef FOO_H
#define FOO_H

class foo {
public:
foo();
};

#endif


實作:
foo.cpp

#include "foo.h"
#include <iostream>
using namespace std;

foo::foo() {
cout << "Hello World!! << endl;
}


主函式:
main.cpp
#include "foo.h"

int main() {
foo foo1;
return 0;
}


執行過程:
$ g++ -c foo.cpp -o foo.o
$ g++ -c main.cpp -o main.o
$ g++ foo.o main.o -o main
$ ./main

就是這麼簡單

而C的部分呢,就必需要透過"用gcc 自製 Library"
而把宣告與實作分開的好處就是你寫的library不會被別人看光光啦~

2009年3月23日 星期一

SCTP Initiation Structure (SCTP_INIT)

When an application calls sendmsg()() using a one-to-many style socket API, it uses an SCTP initiation structure to set the protocol parameters for a new association. The SCTP initiation structure is represented by the sctp_initmsg ancillary data structure. The name of the ancillary data type is SCTP_INIT, and the data type is defined at the IPPROTO_SCTP level.

Following is the data structure for the sctp_initmsg structure:

struct sctp_initmsg {
uint16_t sinit_num_ostreams;
uint16_t sinit_max_instreams;
uint16_t sinit_max_attempts;
uint16_t sinit_max_init_timeo;
};


where:

sinit_num_ostreams

Represents the number of outbound streams.
sinit_num_ostreams

Represents the number of inbound streams to be supported.
sinit_max_instreams

Represents the maximum number of times INIT must be transmitted before giving up an association setup.
sinit_max_init_timeo

Represents the largest time out value to use in attempting a INIT.

The values provided by an application are for a association and overrides the default values.


參考資料:
SCTP Ancillary Data Structures

Adobe Reader 8.1.1 下讀中文字型

要在linux下使用adobe reader要看內容有其它語言的文件,在windows下adobe reader會自己偵測,不過,在linux下就要自己安裝啦~

1. 先去 Adobe 下載字型,裡頭有正體中文、簡體中文、日文和韓文可下載
就下載extra的部分,應該就全部有包含了吧~
2. 解壓縮後,在終端機下執行 sudo ./INSTALL
3. 第一個問題,不理它按 Enter
4. 第二個問題,打入 accept 同意
5. 第三個問題,打入 /opt,如果你安裝時手賤改了目錄,那我也不知道該打什麼
6. 完成

參考資料:
Adobe Reader 8.1.1 下讀中文字型

2009年3月22日 星期日

設定一開機就可以掛載NTFS系統

由ubuntu 7.10好像就可以支援對ntfs作讀寫的作動。

先由fdisk -l了解目前電腦中有哪一些磁區
$ sudo fdisk -l

Disk /dev/sda: 251.0 GB, 251000193024 bytes
255 heads, 63 sectors/track, 30515 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x854b863b

Device Boot Start End Blocks Id System
/dev/sda1 2 30515 245103705 f W95 Ext'd (LBA)
/dev/sda5 2 13055 104856223+ 7 HPFS/NTFS
/dev/sda6 13056 26109 104856223+ 7 HPFS/NTFS
/dev/sda7 26110 30515 35391163+ 7 HPFS/NTFS

Disk /dev/sdb: 82.3 GB, 82348277760 bytes
255 heads, 63 sectors/track, 10011 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00006f55

Device Boot Start End Blocks Id System
/dev/sdb1 1 2611 20972826 26 Unknown
/dev/sdb2 * 2612 5222 20972857+ 83 Linux
/dev/sdb3 5223 9749 36363127+ 7 HPFS/NTFS
/dev/sdb4 9750 10011 2104515 f W95 Ext'd (LBA)
/dev/sdb5 9750 10011 2104483+ 82 Linux swap / Solaris

由以上就可以知道我有兩棵硬碟

而我想要把/dev/sda5掛載在/home/ren/vmware下,有兩個方法
先建立/home/ren/vmware資料夾
$ make -p /home/ren/vmware
1. 手動掛載:
$ sudo mount /dev/sda5 /home/ren/vmware
2. 開機動作掛載:
修改/etc/fstab檔案
$ sudo vi /etc/fstab
加入以下這一行
/dev/sda5 /home/ren/vmware ntfs-3g defaults 0 0
如此一來,每一次開機,系統就會幫你掛載好啦!!
要注意的是上面每一個空格代表的是<tab鍵>
參考資料:
Ubuntu 無法掛載NTFS磁區
Ubuntu: NTFS, Webcam and Skype
開機掛載 /etc/fstab 及 /etc/mtab

2009年3月20日 星期五

poll()

在unix network programming可以透過poll()來避免blocking的產生。

例如在client端,同時要透過read()接收server端傳送過來的訊息,而還要讓client可以讓使用者輸入字串到server端,看起來好像沒有問題,但是,當呼叫read()時,應用程式會一直block在那裡,而使用者就不能輸入訊息了。

這樣就不能同步處理啦~
可以用select來處理,或可以用poll來處理,最好應該是用thread來處理吧~

#include <poll.h>
int poll(struct pollfd *fdarray,unsigned long nfds,int timeout);


範例程式:
...

2009年3月19日 星期四

ubuntu - 兩張網卡同時上網

因為要使用SCTP的multihoming的特性,所以,要設定在ubuntu下同時有兩張網卡在work...

因此找到以下網站可以教別人如何同時兩張網卡work

兩張網卡兩個IP同時上網

各位大大你們好:
我已經可以使用了!
我在說明一下我的需求,一個是無線,一個是有線,各給一個ip。
我會想這樣做是因為一個要連server做事,一個要遠端別台電腦看
東西,兩個網域不同。

作法:
1.首先你必須確定你的網卡是已經都驅動好的!呵呵!
2.編輯/etc/network/interfaces 加入:
auto eth0 eth1
注意如果你看到這兩行:
auto lo #不要動它!
iface lo inet loopback #也不要動到它!

3.之後你只要用ifconfig去各別指定ip就可以了!或者指定dhcp

當然這裡你也可以把所有的設定都寫在interfaces 例如:
iface eth0 inet static #設定第一張網卡
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1
#--------------------------------------------
iface eth1 inet dhcp #設定第二張網卡為dhcp

都設定好了就
sudo /etc/init.d/networking restart
重新啟動網路。

因我會常切換網域所以我是沒有設定。
現在一起啟動兩張網卡做事還蠻方便的!
我的配備X61 使用Ubuntu 7.10 現在要切換到
windows機會比較少了!


但是,很奇怪,我設定之後,無線網卡的部分確沒有抓到正確的ip,
所以,我就改回原本的設定。
然後,我用traceroute
traceroute tw.yahoo.com
耶,就可以成功的,反正,就是內容都不要改,然後,用traceroute tw.yahoo.com
就可以了,真的很神奇~上面是在ubuntu 7.10下會有這一種情況,但是,在ubuntu 8.04就連設定都不用設定就可以同時兩個都連線了。

[2009.04.11 補充]
不知道是不是有一個檔案可以設定這一些router哩?
今天遇到一個問題,我的筆電有無線網卡與有線網卡。
一開始的時候筆電就預設無線網卡為主要的對外出口,很巧的,這個時候無線網路出現問題了,噹~然後,這個時候我就出不去了~
非常的奇怪,我的有線還是好的啊~
因該是router table沒有設定的的關係~
我的有線網卡的gateway是192.168.20.254
打以下指令可以解決~
$ route add default gw 192.168.20.254
詳情可以參考
透過指令修改ubuntu的網路設定

[2009.04.26 補充]
現在出現一個問題啦~
當電腦A有兩張網卡為別是eth0, eth1
當要連線到外面時,均是透過eth0連線出去, 若這個時候我把eth0的網路線拔掉,但是,eth1的網路,在線路方面還是通的。這個情況會變成他一直要去try eth0這一個網路,然後,就沒有辦法使用網路了。可以使用
$ route -n
去了解本機的route table,優先權是由上而下開始的,先找是不是自己目前這一個網域。
除非使用
$ sudo /etc/init.d/networking restart
否則,系統不會重置route table的

ptt網友有建議什麼building的,但是,這一個的功用好像是把多個網卡合在一個ip上,跟我們想要的好像不一樣。我們是希望若目前這一個ip沒有辦法出去,則馬上跑下一個ip。

因此,在設置環境時,就必需兩張網卡,均在不同的網域下。才能使用。

參考資料:
兩張網卡兩個IP同時上網
Ubuntu分享網路連線
雙網卡bonding配置,實現負載均衡

2009年3月18日 星期三

ldconfig

系統預設的函式庫都是由 ldconfig 設定後寫入 /etc/ld.so.cache 當中!
然後供系統來讀取使用!那麼您如何知道目前的函式庫有多少呢?
使用 ldconfig 就可以知道啦!
以 ldconfig -p 可以列出 /etc/ld.so.cache 的內容呢!那麼 /etc/ld.so.conf 又是什麼呢?!
很簡單,那就是『目前你的系統中主要的函式庫放置的目錄』,以上式為例,則主要的 XFree86 函式庫放置在 /usr/X11R6/lib 當中,另外還有常用的 kerberos 的函式庫也擺在其中!
如果您的其他函式庫需要寫入系統中,讓系統可以很快的找到該函式庫而予以取用的話,那麼將你所安裝的套件(通常是 tarball 的套件)所產生的 lib 目錄,給他寫到 /etc/ld.so.conf 這個檔案中,然後再以 ldconfig 重新建立 /etc/ld.so.cache 即可!

[2009.05.12 補充]
若只是要暫時的設定要search某一個目錄的library, 可以使用設定LD_LIBRARY_PATH環境變數,例:我要加入/usr/local/lib
則使用以下指令
$ LD_LIBRARY_PATH=/usr/loca/lib:$LD_LIBRARY_PATH

參考資料:
鳥哥

建立sctp編譯環境

直接透過apt取得sctp的套件

$ sudo apt-get install libsctp-dev lksctp-tools
下面有一個範例程式:
server.c

//==============================include header file============================
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//=============================================================================

//==============================define variable================================
#define BUFFERSIZE 8192
#define SERV_PORT 9877
#define LISTENQ 1024
//=============================================================================

int main(int argc,char **argv)
{
int echo_to_all = 0;

int ret_value;
int sock_fd;
int msg_flags; // 記錄要傳送訊息時的相關設定
char readbuf[BUFFERSIZE];
struct sockaddr_in servaddr,cliaddr;
struct sctp_sndrcvinfo sri; // 記錄send, recv, association的相關資訊
struct sctp_event_subscribe evnts;
int stream_increment = 1;
socklen_t len;
size_t rd_sz; // size_t指的就是unsigned long:rd_sz表示recv,send-size

if (argc == 2) {
// 把字串轉換成數字
stream_increment = atoi(argv[1]);
}

// 建立socket的型態為SCTP的one-to-many的型態
sock_fd = socket(AF_INET,SOCK_SEQPACKET,IPPROTO_SCTP);
if (sock_fd == -1) {
printf("socket error\n");
exit(-1);
}
// 初始化server address的設定
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);

// 把socket與所建立的address綁在一起
ret_value = bind(sock_fd,(struct sockaddr *) &servaddr,sizeof(servaddr));
if (ret_value == -1) {
printf("bind error\n");
exit(-1);
}

// Set up for notifications of internet
bzero(&evnts,sizeof(evnts));
evnts.sctp_data_io_event = 1; // 使得sctp_sndrcvinfo可以伴隨資料傳送過來
ret_value = setsockopt(sock_fd,IPPROTO_SCTP,SCTP_EVENTS,&evnts,sizeof(evnts));
if (ret_value == -1) {
printf("setsockopt error\n");
exit(-1);
}

// 設定等待多少個client端連線
ret_value = listen(sock_fd,LISTENQ);
if (ret_value == -1) {
printf("listen error\n");
exit(-1);
}

printf("start wait...\n");
for (;;) {
len = sizeof(struct sockaddr_in);
// 等待client端連線
rd_sz = sctp_recvmsg(sock_fd,readbuf,sizeof(readbuf),(struct sockaddr *) &cliaddr,&len,&sri,&msg_flags);
if (stream_increment) {
sri.sinfo_stream++;
if (sri.sinfo_stream >= 100) {
sri.sinfo_stream = 0;
}
}
// 把接收到的資料回送給client
ret_value = sctp_sendmsg(sock_fd,readbuf,rd_sz,(struct sockaddr *) &cliaddr,len,sri.sinfo_ppid,sri.sinfo_flags,sri.sinfo_stream,0,0);
if (ret_value == -1) {
printf("sctp_sendmsg error\n");
exit(-1);
}
}
return 0;
}

client.c
//==============================include header file============================
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//=============================================================================

//==============================define variable================================
#define BUFFERSIZE 8192
#define SERV_PORT 9877
#define LISTENQ 1024
#define MAXLINE 4096
#define SCTP_MAXLINE 800
#define SERV_MAX_SCTP_STRM 10
//=============================================================================

//==============================define function================================
void sctpstr_cli(FILE *fp,int sock_fd,struct sockaddr *to,socklen_t tolen);
void sctpstr_cli_echoall(FILE *fp,int sock_fd,struct sockaddr *to,socklen_t tolen);
//=============================================================================

//==============================main function==================================
int main(int argc,char **argv)
{
int sock_fd; // client所建立的socket
struct sockaddr_in servaddr; // 記錄server的sockaddr_in的相關資訊
struct sctp_event_subscribe evnts; // 設定要觀查的事件
int echo_to_all = 0; // 決定server是否有多個
int ret_value = 0;

// 沒有設定server的ip
if (argc < 2) {
printf("Missing host argument - use '%s host [echo]'\n",argv[0]);
exit(-1);
}
// 設定多個server的ip
if (argc > 2) {
printf("Echoing messages to all streams\n");
echo_to_all = 1;
}

// init setting
sock_fd = socket(AF_INET,SOCK_SEQPACKET,IPPROTO_SCTP); // 設定為sctp為one-to-many的型態
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 感覺這一行應該是多餘的
servaddr.sin_port = htons(SERV_PORT);
ret_value = inet_pton(AF_INET,argv[1],&servaddr.sin_addr); // 把字串轉換成網路位元組序二進位值,當回傳為1時,表轉換
成功

if (ret_value != 1) {
printf("translate server ip address error!!\n");
exit(-1);
}

// 設定要觀察的事件,所有都清除,只設定io event
memset(&evnts,0,sizeof(evnts));
evnts.sctp_data_io_event = 1; // 監控sctp的data的io事件:1是打開的意思
ret_value = setsockopt(sock_fd,IPPROTO_SCTP,SCTP_EVENTS,&evnts,sizeof(evnts));
if (ret_value == -1) {
printf("setsockopt error\n");
exit(-1);
}
if (echo_to_all == 0) {
// 此client只傳給一個server
sctpstr_cli(stdin,sock_fd,(struct sockaddr *) &servaddr,sizeof(servaddr));
}
else {
// 要設定傳給許多個server
sctpstr_cli_echoall(stdin,sock_fd,(struct sockaddr *) &servaddr,sizeof(servaddr));
}
close(sock_fd);
return 0;
}
//=============================================================================

//==============================function implemen==============================
void sctpstr_cli(FILE *fp,int sock_fd,struct sockaddr *to,socklen_t tolen)
{
struct sockaddr_in peeraddr; // client的相關位址資料
struct sctp_sndrcvinfo sri; // send and recv端的資料
char sendline[MAXLINE],recvline[MAXLINE];
socklen_t len;
int out_sz,rd_sz;
int msg_flags;
int ret_value=0;

bzero(&sri,sizeof(sri));
// block住,等待client端輸入
while (fgets(sendline,MAXLINE,fp) != NULL) {
if (sendline[0] != '[') {
printf("Error, line must be of the form '[streamnum]text'\n");
continue;
}
// string to long integer,表示sendline這一個字串是base在10進位去轉換
// 透過設定的stream number來傳送資料
sri.sinfo_stream = strtol(&sendline[1],NULL,0);
out_sz = strlen(sendline); // 輸入字串的長度,用來記錄等一下要傳送幾個字元
ret_value = sctp_sendmsg(sock_fd,sendline,out_sz,to,tolen,0,0,sri.sinfo_stream,0,0);
if (ret_value == -1) {
printf("sctp_sendmsg error\n");
exit(-1);
}

len = sizeof(peeraddr);
rd_sz = sctp_recvmsg(sock_fd,recvline,sizeof(recvline),(struct sockaddr *) &peeraddr,&len,&sri,&msg_flags);
printf("From str:%d seq:%d (assoc:0x%x):",sri.sinfo_stream,sri.sinfo_ssn,(u_int) sri.sinfo_assoc_id);
printf("%.*s",rd_sz,recvline);
}
}

void sctpstr_cli_echoall(FILE *fp,int sock_fd,struct sockaddr *to,socklen_t tolen)
{
struct sockaddr_in peeraddr;
struct sctp_sndrcvinfo sri;
char sendline[SCTP_MAXLINE], recvline[SCTP_MAXLINE];
socklen_t len;
int rd_sz,i,strsz;
int msg_flags;
int ret_value=0;

// the client initializes the sri structure used to set up the stream it will be sending and receiving from
bzero(sendline,sizeof(sendline));
bzero(&sri,sizeof(sri));
while(fgets(sendline,SCTP_MAXLINE-9,fp) != NULL) {
strsz = strlen(sendline);
// delete the newline character that is at the end of the buffer
if (sendline[strsz-1] == '\n') {
sendline[strsz-1]='\0';
strsz--;
}
// 傳送SERV_MAX_SCTP_STRM筆資料給server
for (i=0;i<SERV_MAX_SCTP_STRM;i++) {
snprintf(sendline+strsz,sizeof(sendline)-strsz,".msg.%d",i);
ret_value = sctp_sendmsg(sock_fd,sendline,sizeof(sendline),to,tolen,0,0,i,0,0);
if (ret_value == -1) {
printf("sctp_sendmsg error\n");
exit(-1);
}
}
// 由server接收SERV_MAX_SCTP_STRM筆資料
for (i=0;i<SERV_MAX_SCTP_STRM;i++) {
len = sizeof(peeraddr);
rd_sz = sctp_recvmsg(sock_fd,recvline,sizeof(recvline),(struct sockaddr *) &peeraddr,&len,&sri,&msg_flags);
printf("From str:%d seq:%d (assoc:0x%x):",sri.sinfo_stream,sri.sinfo_ssn,(u_int) sri.sinfo_assoc_id);
printf("%.*s\n",rd_sz,recvline);
}
}
}


編譯:
$ gcc server.c -o server -lsctp
$ gcc client.c -o client -lsctp
$ ./server &
$ ./client 127.0.0.1
> [1]Hello World!!


參考資料:
網絡編程基礎--學習筆記
UNIX Network Programming
sctp_exporter.cc
SCTP Ancillary Data Structures
Stream Control Transmission Protocol

2009年3月16日 星期一

configure

在ptt有網友問說程式在舊的gcc下跑才會過,
那麼要如何設定才可以不用重灌哩?

有人回答啦~不過基本上還是要看configure這一個script怎麼設定的

./configure CC="/usr/bin/gcc34" CXX="/usr/bin/g++34"

大概是這樣!!

2009年3月15日 星期日

透過指令安裝deb檔

Debian,以及基于 Debian 的系統,如 Ubuntu 等,所使用的包格式為 deb。以下為操作 deb 包的常用 Dpkg 指令表,供初學的朋友參考。

安裝包
dpkg -i package.deb

刪除包
dpkg -r package

刪除包(包括配置文件)
dpkg -P package

列出與該包關聯的文件
dpkg -L package

顯示該包的版本
dpkg -l package

解開 deb 包的內容
dpkg –unpack package.deb

搜索所屬的包內容
dpkg -S keyword

列出當前已安裝的包
dpkg -l

列出 deb 包的內容
dpkg -c package.deb

配置包
dpkg –configure package

注意:更多選項可通過 dpkg -h 查詢,有些指令需要超級用戶權限才能執行。

參考資料:
解開或安裝 deb檔~ dpkg 指令

2009年3月14日 星期六

行事曆網路同步

又是同步化的問題啦~
如果在多台電腦上都有行事曆的話,
這裡發現一個軟體可以做到。
鼓奇日曆(Googi Calendar)

另外,聽說,Google就要推出離線版的日曆了,真是期待啊~

2009年3月13日 星期五

修改XP內建遠端port值

Windows XP「遠端桌面」功能預設以編號3389連接埠作訊息傳輸,但可因應需要而進行修改︰
啟動Regedit
到路徑HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TerminalServer\WinStations\RDP-Tcp中
DWORD值PortNumber,把數值由3389改為任何的數值便可。

如此一來,可以避免被別人入侵...

2009年3月9日 星期一

多線程程式設計 - Semaphore

Semaphore比mutex更好用

main.c

#include <stdlib.h>
#include <stdio.h>
#include <semaphore.h>
sem_t sem;
main()
{
int rc;
rc = sem_init(&sem, 1, 1);
if (rc == -1) {
perror("sem_init() failed");
exit(1);
}
int pid = fork();
if (pid==0) /* child */
{
int i;
srand(1234);
for(i=0; i<30; i++)
{
sem_wait(&sem);
sleep(rand()&3);
printf("child: %d\n", i);
sem_post(&sem);
}
}
else /* parent */
{
int i;
srand(54321);
for(i=0; i<30; i++)
{
sem_wait(&sem);
sleep(rand()&3);
printf("parent: %d\n", i);
sem_post(&sem);
}
}
}


執行:
$ gcc -lpthread main

參考資料:
Linux下的Semaphore用法
高手進階必讀:Linux內核的同步機制
Linux Device Drivers學習筆記-Chapter.5
Getting Started With POSIX Threads

多線程程式設計 - pthread_create 傳遞參數的用法

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

struct test
{
int no;
char name[80];
};

void *pfunc(void *data)
{
// 後面的括號是把void型態的指標轉成strut test的指標
// 而前面的*號是後面指標指到的位址提取出來
struct test tt = * (struct test*) data;
printf("%d\n",tt.no);
printf("%s\n",tt.name);
}

int main()
{
struct test itest;
pthread_t th;
itest.no = 100;
strcpy(itest.name,"Hello");

// 產生一個thread,其去執行pfunc這一個函數
pthread_create(&th,NULL,pfunc,(void*) &itest);
return 0;
}

參考資料:
pthread_create 傳遞參數的用法

pointer function 與 return pointer

今天意外的發現,宣告一個return pointer的function與pointer function非常相似
下面就是宣告一個回傳值為int指標的foo函式
int* foo();

下面就是宣告一個回傳值為int的pointer function
int *foo();

有沒有覺得很像啊?不只是很像,根本是一模一樣。
好像compiler會幫我們自己判斷。

為了避免compiler判斷錯誤,我們可以使用以下的方法來強制定義那一個*號是要形容前面的int還是後面的foo
重新宣告一次
下面就是宣告一個回傳值為int指標的foo函式
(int*) foo();

下面就是宣告一個回傳值為int的pointer function
int (*foo)();

而若想要宣告一個回傳值為int的指標的pointer function
(int*) (*foo)();

main.c

#include <stdio.h>
#include <stdlib.h>

int* getvalue()
{
int *y;
y = malloc(sizeof(int));
*y=1;
return y;
}

int main()
{
int *x;
x = getvalue();
printf("value:%d\n",*x);

return 0;
}

參考資料:
C++ Gossip: 函式指標

刪除outlook express內重複的信件

為了避免透過outlook下載下來郵件之後,然後,在一些意思的情況下,下載下來的郵件不見,所以我在設定outlook時,會設定成由伺服器下載郵件之後,不刪除郵件上的資料。

但是,這樣會有一個問題,當我重灌電腦之後,因為,我會備份之前郵件的資料。當我再次設定好郵件的設定之後,會發現我電腦有的郵件會再次的收一次,所以,我的outlook express會許多重覆的信件。

因為解決目前的這一個問題,上網找到一堆outlook express duplicate,但是,好像都是要付費的。

因此,只好用另外的取代方案了。

找到一個可以尋找電腦內的重覆檔案的軟體,我本來是打算先把郵件先拉出到外面建立的一個資料夾,但是,EasyDuplicate這一個portable的軟體好像效果不是很好,我直接看就可以看到一堆重覆的檔案,它竟然收尋不到?!

但是,我把郵件拖曳到外面的這一個動作,意外的讓我發現,因為,在windows下同一個資料夾不能有相同的檔名,所以,相同檔名的檔案系統會另外幫它取名字,在原本的檔名後面加上(nn),其中nn是表示一組數字。

如此一來,再透過windows下的收尋,尋找*(*)*就可以找到重覆且被重新命名的文件,最後再把它們塞回outlook express就好啦~
=================================================================
以下項目請檢查是否有打勾(答案是要的)?

帳號:
在伺服器保留郵件備份...
從[刪除的郵件]資料夾刪除後, 就從伺服器刪除...

工具→選項→維護:
結束時清空刪除的郵件...
離開IMAP資料夾時清除...
=================================================================

2009年3月8日 星期日

多線程程式設計 - openmp

最近在學習如何增加thread,與使用fork的方法。

但是,增加thread與fork就好像把一支程式分成許多隻小程式,但是,在每一個時間還是只有一個小程式可以被執行,其它的小程式均必需要排隊。

若你手上有多核心的電腦,在幾乎不用修改程式碼,就可以讓原本的許多的小程式可以被分配到多個核心上去跑。

而那一個就是openmp

main.c

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>

void Test(int n)
{
int i;
for (i=0;i<10000;++i)
{
// waste time
}
printf("%d, ",n);
}

int main(int argc,char **argv)
{
int i;
#pragma omp parallel for
for (i=0;i<10;i++)
Test(i);
printf("\n");
}


執行編譯:

$ gcc -fopenmp main.c

而-fopenmp就是開啟openmp功能的參數

參考資料:
Linux下程式
OpenMP並行編程簡易教程

windows下程式
簡易的程式平行化方法-OpenMP(一)簡介
簡易的程式平行化方法-OpenMP
充分利用多核心的程式

GOMP
OpenMP Exercise

多線程程式設計 - wait()

#include <sys/types.h>
#include <sys/wait.h>

// 是表示父行程等待其中一個(任何一個)子行程結束,回傳值是表示哪一個子行程,而status是表示當這一個子行程結束時的狀態
pid_t wait(int *status);
// 表示父行程等待一個指定的pid直到這一個pid完成執行,子行程的結束狀態會儲存在status中
pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t id , siginfo_t *infop , int options );


參考資料:
wait(2) - Linux man page
POSIX thread (pthread) libraries

多線程程式設計 - exec()

fork()有提到有兩個使用fork()的方式
1. 複製自己這一個程序,另外去執行
2. 複製自己這一個程序,透過exec()這一個函式去執行另外的程序並且取代自己的程序,只有process id延續下來

有一個例子看了會比較好理解
main.c

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char command[256];

int main()
{
int rtn; // 子程序的返回數值
while(1) {
// 由終端機讀取要執行的命令
printf(">");
fgets(command,256,stdin);
command[strlen(command)-1]=0;
if (fork()==0) {
execlp(command,NULL);
// 如果exec函數返回,表明沒有正常執行命令,列印錯誤訊息
perror(command);
exit(errno);
}
else
{
// 父程序,等待子程序結序,並列印子程序的返回值
wait(&rtn);
printf("child process return %d\n",rtn);
}
}
return 0;
}


參考資料:
wait(2) - Linux man page
Linux下的多進程編程(轉) - fork(),exec()等函數的使用

多線程程式設計 - thread

Process 和 Thread 的差別

1.Process
Heavy weight, 獨立的Program Segment、Data Segment, OS resource 等, 但切換時(Context Switch), CPU 需將當前 Process 的狀態保存起來,再載入下一個要執行的 Process 的狀態,負責儲存這個狀態的稱為PCB (Process Controll Block,或稱 Process Descriptor)
2.Thread
Light weight process,有自己的 program counter, register set, stack space, no (or a little) context switch
每個 Thread 有自己的 PC, register set, stack space 表示 thread 可以有自己的 call graph 來執行程式。一個Process中的Thread共用Address Space(包括Program/Data Segment、Resource),因此切換Thread時不需要切換/複製Address Space,處理代價相對輕很多。

非常重要的參考資料:
POSIX thread (pthread) libraries

main.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *print_message_function( void *ptr );

main()
{
pthread_t thread1, thread2;
char *message1 = "Thread 1";
char *message2 = "Thread 2";
int  iret1, iret2;

/* Create independent threads each of which will execute function */
// pthread pthread_create( &a_thread, a_thread_attribute, (void *)&thread_function,(void *) &some_argument);
// 設定這一個thread一開始就執行哪一個function,第四個參數是表示要餵給這一個function什麼數值
// 並且設定了當這一個thread完成之後,要把回傳值設定給哪一個變數
iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);
iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) message2);

/* Wait till threads are complete before main continues. Unless we  */
/* wait we run the risk of executing an exit which will terminate   */
/* the process and all threads before the threads have completed.   */

pthread_join( thread1, NULL);   // the main process will wait for the end of the thread1
pthread_join( thread2, NULL);   // the main process will wait for the end of the thread2
// 若照這樣說,以上兩個thread必定會依照以上的順序完成

printf("Thread 1 returns: %d\n",iret1);
printf("Thread 2 returns: %d\n",iret2);
exit(0);
}

void *print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s \n", message);
pthread_exit(0);   // 結束thread
}


執行
$ gcc -lpthread main.c

pthread_t pthread_self(void);
可以取得自己的thread id,pthread_t就是unsigned long integer,用printf的是%llu

pid_t getpid(void);
取得目前process的id,且這一個函數一定會成功

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
第一個參數要存放產生的thread的id
第二個參數要存放的是屬性
第三個參數表示這一個thread要執行的function
第四個參數表示要給上面的function的參數值
若成功,回傳值為0
若遇到要執行的function需要多個參數時,使用的方式就是把參數包成struct再傳過去

int pthread_join(pthread_t thread, void **value_ptr);
呼叫此函數的程序會等到thread執行完後,才繼續執行。
而第二個參數是表示當thread執行完後的回傳值要傳回到value_ptr
若此函數成功,則回傳0
這一個函式非常適合放在主程式要結束之前,因為,不一定是thread先結束或是main先結束,若main先結束,會導致thread會被強制結束,所以,把這一個函式放在main結束之前,非常適合。

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
建立一個新的mutex供multi-thread使用
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
用來操作mutex,避免出現race condition的情況
int pthread_mutex_destroy(pthread_mutex_t *mutex);
當不需要再使用到mutex就可以使用以上的函數

void pthread_exit(void *value_ptr);
在 main(parent thread)裡的 exit 呼叫將結束整個
process ,這將導致所有的 thread 一起結束。所以說,如果 exit 在 printf 前被執
行的話,將不會有任何的輸出產生。事實上,在任何一個 thread (不論 parent or
child)裡呼叫 exit 都將導致 process 結束,而所有的 thread 也跟著一起結束了。
所以如果要結束一個 thread ,我們必須使用 pthread_exit 這個函數。

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
在執行此函數前要先lock mutex,當結束時,會叫醒其它在等待此mutex的thread。
而我覺得pthread_cond_t與pthread_mutex_t的差別在於
pthread_cond_t有一點像是queue(大家都在裡面睡覺),裡面可能有很多個thread在等待,當其它thread用pthread_cond_signal時,會由這一個queue中選一個優先權較高的thread選一個出來。
而pthread_mutex_t則是一直在那裡等,直到有人釋放mutex

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
叫醒因cond在等待的thread

int pthread_delay_np (const struct timespec *interval);
除了使用 delay 來達成同步的效果外,另一個錯誤發生在 sleep 系統呼叫;如同
exit 對 process 的影響一樣,當 thread 呼叫 sleep 時,講導致整個 process 停下
來。這表示所有屬於這個 process 的 thread 也將跟著停頓下來。因此在上面這個程
式中,呼叫 sleep 除了平白讓程式慢了20秒,並不會有什麼額外影響。另外一個適用
的函數是 pthread_delay_np (np 表示 not process)。舉例來說,要讓thread 停頓
兩秒鐘,可以用下列程式:
struct timespec delay;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_delay_np( &delay );

[2014.07.17 補充]
執行緒所使用的記憶體資源並不會釋放,直到被某個執行緒所join。

如果是joinable thread的話
當thread function結束或調用pthread_exit()時
都不會釋放所佔用的stack或其他資源,只有在呼叫pthread_join()之後才會被釋放

若是detached thread
這些資源在thread function退出時或調用pthread_exit()時會自動被釋放

而detached的屬性有三種設定方法:
1) 在pthread_create時指定pthread_attr的detachstate
pthread_t thread_p;
pthread_attr_t attr_p;
pthread_attr_init(&attr_p);
pthread_attr_setdetachstate(&attr_p, PTHREAD_CREATE_DETACHED);
pthread_create(&thread_p, &attr_p, thread_func, NULL);
pthread_attr_destroy(&attr_p);
2) 在pthread_create後,呼叫pthread_detach()
pthread_create(&thread_p, &attr_p, thread_func, NULL);
pthread_detach(thread_p);
3) 在pthread_create後,在thread function中pthread_detach自己
pthread_detach(pthread_self());

以上是很重要的一個觀念。
[Linux pthread] joinable thread or detached thread?
pthread_join函數及linux線程

參考資料:
Linux程式設計- 2.fork, pthread, and signals
LinuxTutorialPosixThreads
Getting Started With POSIX Threads
Posix線程編程指南
library
POSIX 線程詳解(1)
Linux下的多線程編程
pthread_create用法
GThreadPool 高效的執行緒池
Pthread

Debugging with GDB

參考資料:
Debugging with GDB (入門篇)
Study-area的除錯教學
Linux 除錯利器 - GDB 簡介
GNU debugger
用GDB調試程序
GDB 自動化 debug
Debugging with GDB

2009年3月7日 星期六

多線程程式設計 - fork()

#include <unistd.h>

pid_t fork(void);

只有呼叫fork一次,但是會return 2次
一次是parent process回傳的值是child process的identify
第二第是child process回傳的值是0

fork( )有兩個 return 值,大於 0 的是 parent (值不固定),等於 0 的是 child,兩個 process 在 fork( ) 成功後同時存在,並沒有什麼不一樣,可以說是完全一樣的process,fork( ) 之後的parent 與 child 會共同指向 fork() 以下的執行指令,也就是說,parent 和 child 都會執行 fork( ) 以下的程式碼,而且是各自執行,child 會符合 switch 中 pid == 0 的條件、 parent 則會符合 default 中的條件.

child process回傳值是0是有其原因的,
因為每一個process只有一個parent process,而且可以透過getppid得到parent process的identify

而parent process可以有很多child process,而且沒有函數可以取得child process的identify,因此若parent process要追蹤child process則必需記錄fork()回傳的id

在呼叫fork()前,一個file descriptor在父類別被指定,則此父類別可以與子類別共同分享這一個資源.

通常有兩種使用fork()的方式:
1. 一個程序複製它自己,產生另一個程序,使得兩個程序可以同時做事情,像是網路應用程式均是用這一個方法
2. 一個程序去執行另一個程序。同樣的,第一個程序先複製它自己,產生另一個程序。而這一個程序執行exec()來使另一個程序來取代這一個本身的子程序

另外必需注意的是,fork()回傳值的型態是pid_t,不能用int的變數去接

在web application的一個很好的例子是:

pid_t pid;
int listenfd,connfd;
listenfd = socket( ... );
// fill in sockaddr_in{} with server's well-known port
bind(listenfd, ...);
while(1) {
  connfd = accept(listenfd, ... );  // probably blocks
  if ((pid=fork())==0) {
    close(listenfd);  // child closes listening socket
    doit(connfd);     // process the request
    close(connfd);    // done with this client
    exit(0);
  }
  close(connfd);      // parent closes connected socket
}

就可以同時服務很多client端的程式啦~


參考資料:
行程關念
Process Control
Linux下的多進程編程(轉) - fork(),exec()等函數的使用
CS 360 - Internet Programming

2009年3月6日 星期五

Socket : Non-Blocking Communation

非常重要,先把它記錄下來吧。

參考資料:
[C/C++] Scoket Programming : Non-Blocking Communation

用c++封裝socket

因為socket均是用c去實作,基本上是function導向的。
因為我必需要用到Qt去顯示介面,所以為了以後方便使用,我還是先學習如何用c++來封裝socket,到時候若真的有必要的時候,可以直接拿來用啦~

因此,最近找了許多c++來完成封裝socket的程式,最有幫助的應該是以下這一個網頁。
Linux Socket Programming In C++
最後我參考了這一個資料(chart application),完成了傳送檔案的程式,再來完成了傳送struct的程式,最後,在Qt的介面下完成了傳送webcam frame的程式。

本來要寫的清楚一點,但是,因為別人早就寫的很好了,大家參考他的就好了。

我這裡要說的是在server透過tcp的方式傳送給client時,我所遇到的問題。

1. 傳送封包不完美
我一開始的想法是由server透過accept()等待client端的connect(),當連線建立之後,server端就一直傳送切好的packet給client,然後,client端就一直接收就好了。
但是,確出現錯誤啦。原本server與client均在本機端時,執行沒有問題(先透過傳送一個frame),但是,當server與client在不同主機上時,確發現會出現中斷。
這個時候,我只好先拿傳送檔案的程式,在server-clinet在不同主機上會不會出現錯誤,竟然發現檔案接收不完整。原本我以為tcp是標榜可信賴傳送,所以,server一直傳,client一定會準確的收到。我猜想可能是server一直傳,client來不及接收,導致buffer不能容納那麼多資料,就會導致crash。
知道問題點之後,馬上重新設計,當server端與client端收到對方的封包之後,會回傳"I got it"的字串。
舉一個例子:
當server端送出一個packet之後,就等待client回傳"I got it"字串,這樣可以避免server端一直送出訊息,而導致client來不及接收所發生的錯誤。

2. blocking的問題:
同時,也意外的發現,這一個方法可以避免blocking的問題發生,因為,blocking雖然是接收到資料之後,不會馬上傳送而等到buffer滿了之後,才會傳送,但是,這一個等待的時間也有一定的限制。而透過上面這一個方法就可以解決blocking的問題。那為什麼不設定成non-blocking呢?因為設定成non-blocking的情況的話,會有一堆情況要自己處理,會比較複雜。

例如buffer大小是1024,如果只有7byte要傳送的話,本來不會馬上傳送出去,但是,下一個指令是等待對方client回傳"I got it"字串,所有必需等待,結果時間一到,就不得不送出去啦~算是可以不必用non-blocking的方式做到「類」non-blocking的功能!!

主要參考資料:
Linux Socket Programming In C++

非常有參考的資料:
[C/C++] Socket Connection
[C/C++] Scoket Programming : Non-Blocking Communation
A Stream Socket API for C++
Linux Socket Programming 淺談 -- 教你的程式如何透過網路溝通

參考資料:
socket c++
sockets_test.cpp
C++ Sockets Library
Linux man:Send
QT中的SOCKET编程

2009年3月5日 星期四

Qt - QSocketNotifier

在最在研究如何用Qt來寫影像傳輸。
因為,之前學長寫的是用Qt3來修改IHU(I hear you)在原本base在tcp/udp上的應用程式,變成base在sctp上,並且可以完成handover的機制。

而我目前的目標是把影像的部分加進去。

因為Qt通常是你設定好機制之後,丟給Qt系統,系統會幫把把結果顯示出來。
而我Qt的部分是在client端,去讀取server的資料,並且顯示在client的面板上。

因此當client與server端建立連線之後,我必需要馬上讀取server端的資料。

就像我上面所說的,因為,我已經把控制權交給Qt去做了,那我怎麼知道server端有把資料送過來哩?

這個時候就要透過QSocketNotifier來監控,使得當client有東西要接收時,可以去執行相對應的函數。

這裡要記錄一下QSocketNotifier的使用方式。

也可以參考QSocketNotifier的官方說明文件
目前我只有使用到監測Read的部分,同時要注意的是要監控的是connect file descriptor

client的部分就是
::connect( connfd, (struct sockaddr*)&sa,sizeof(sa) );
而就是要監控connfd

而server的部分就是
connfd = ::accept(listenfd,(struct sockaddr *)&client_address, &client_len);
的上面那一個connfd

另外,必需注意一點的是,當因為有資料可以讀取而觸發時,若沒有立刻把QSocketNotifier刪掉,則會一直觸發這一個function,若你的資料一直沒有讀取的話。

例如:
QObject::connect( sn, SIGNAL(activated(int)),myObject, SLOT(dataReceived()) );

dataReceived()的內容的第一行必需為
delete sn;
如此一來才可以安心的接收資料,而不因為一直有資料要讀取,而一直觸發這一個function

Linux指令 - diff

在Linux下有三種比較檔案的指令:diff、cmp、patch
目前先來整理一下diff吧~

cmp是以byte為單位來比較兩個文件;而diff是以行為單位來比較兩個文件~

hello.c

1  #include <stdio.h>
2
3 int main(void)
4 {
5 char msg[] = "Hello world!";
6
7 printf("Hello World!!\n");
8 puts(msg);
9 printf("Welcome to use diff commond.\n");
10
11 return 0;
12 }


hello_diff.c
1  #include <stdio.h>
2 #include <stdlib.h>
3
4 int main(void)
5 {
6 char msg[] = "Hello world,fome hello_diff.c";
7
8 puts(msg);
9 printf("hello_diff.c says,'Here you are,using diff.'\n");
10
11 return 0;
12 }


$ diff hello.c hello_diff.c

結果與解示:
1a2
> #include <stdlib.h>
右邊文件的第2行是新增的(也就是左邊的文件中沒有這一行)
5c6
< char msg[] = "Hello world!";
---
> char msg[] = "Hello world,fome hello_diff.c";
左邊的第5行修改為右邊的第6行
7d7
< printf("Hello World!!\n");
原來左邊的第7行被刪掉了
9c9
< printf("Welcome to use diff commond.\n");
---
> printf("hello_diff.c says,'Here you are,using diff.'\n");
左邊的第9行修改為右邊的第9行


基本上呢,以上都是在描述左邊的文件
>:表示左邊增加一行
<:表示左邊減少一行


而可以用colordiff會有顏色,比較漂亮哩~

參考資料:
鳥哥
Linux: 加上顏色區別的 diff - colordiff