2011/10/26

AWS Toolkit for Eclipse

AWS Toolkit for Eclipse

安裝時出現下面錯誤訊息:
Cannot complete the install because one or more required items could not be found.
Software currently installed: Amazon SimpleDB Management 1.0.0.v201110211341 (com.amazonaws.eclipse.datatools.enablement.simpledb.feature.feature.group 1.0.0.v201110211341)
Missing requirement: Eclipse Data Tools Platform Amazon SimpleDB UI Plug-in 1.0.0.v201110211341 (com.amazonaws.eclipse.datatools.enablement.simpledb.ui 1.0.0.v201110211341) requires 'bundle org.eclipse.datatools.sqltools.sqlscrapbook 1.0.0' but it could not be found
Cannot satisfy dependency:
From: Amazon SimpleDB Management 1.0.0.v201110211341 (com.amazonaws.eclipse.datatools.enablement.simpledb.feature.feature.group 1.0.0.v201110211341)
To: com.amazonaws.eclipse.datatools.enablement.simpledb.ui [1.0.0.v201110211341]

錯誤訊息是說缺少了 Eclipse Data Tools Platform Amazon SimpleDB UI Plug-in,但我從Eclipse Data Tools Platform (DTP)下載最新的套件還是不足。

最後直接去下載Eclipse IDE for Java EE Developers,這個版本的eclipse內建的plug-in比較完整。
再次安裝aws toolkit就可以成功了!


參考資料:
AWS Developer Forums: Eclipse AWS Toolkit install error. ...
having hard time install AWS toolkit for java and eclipse - HPCS

2011/10/04

Postgresql + pgpool-II

抓一張官方的示意圖:

特色
  • Connection Pooling: 減少建立connection的overhead
  • Load Balance: 依照負載去dispatch
  • Replication: 所有的修改都會replication到底下所有database
  • Parallel Query
不外乎就是要來增加throughput啦。

設定
可以參考官方網站,在這邊以下列的架構為例




pgpool 192.168.0.2
DB1 192.168.0.3
DB2 192.168.0.4


2011/09/26

Postgresql建立索引

有沒有建索引的查詢速度真的差很多,預設是使用binary-tree來實作index,時間複雜度是O(logN)。從N變成logN,是有指數級的差距!

建index的方法可以參考


以下舉幾個例子,假如我們的table叫account,email這個欄位很常用來當查詢條件,那我們會為email建立index:
postgres=> CREATE INDEX account_email_idx ON account (email);
postgres=> \d account;
Indexes:
    "account_pkey" PRIMARY KEY, btree (id)   # Primary key預設就會建立index了
    "account_email_idx" btree (email)

Composite index?
Composite indexes are used when two or more columns are best searched as a unit or if many queries reference only the columns specified in the index.
All the columns in a composite index must be in the same table.
我自己把它解釋成當一個query同時帶多個條件時,composite index可以加快查詢的速度。
postgres=> CREATE INDEX account_composite_idx ON account (name, email);
postgres=> \d account;
Indexes:
    "account_pkey" PRIMARY KEY, btree (id)
    "account_composite_idx" btree (name, email)


除了建索引,定期清理(VACUUM) database 也可以加快存取速度
postgres=> VACUUM VERBOSE ANALYZE;

2011/09/22

Implement tornado session in memcached

示範將tornado的session實作在memcached裡面,我是參考這個blog:
玩蛇记-给tornado加上session支持 - 亚历山大同志 - 博客园

Requirement
  • Tornado 2.0
  • python-libmemcached 0.4
    • Pyrex 0.9.4+
    • libmemcached 0.52

邏輯


流程

2011/09/21

Postgresql 小記

常用指令
# Start
su postgres -c "/usr/local/pgsql/bin/pg_ctl start -D /usr/local/pgsql/data"

# Stop
su postgres -c "/usr/local/pgsql/bin/pg_ctl stop -D /usr/local/pgsql/data"

# restart
su postgres -c "/usr/local/pgsql/bin/pg_ctl restart -D /usr/local/pgsql/data"

# Init postgres
sudo mkdir /usr/local/pgsql/data
chown postgres:postgres /usr/local/pgsql/data
su postgres -c "/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data"

# Create user
/usr/local/pgsql/bin/createuser -U postgres -P userA

# Drop user
/usr/local/pgsql/bin/dropuser -U postgres userA

# Create db
/usr/local/pgsql/bin/createdb -O userA -U postgres testdb

# Drop db
/usr/local/pgsql/bin/dropdb -U postgres testdb

# Dump db
/usr/local/pgsql/bin/pg_dump -U postgres testdb > dump.sql

# Restore db
/usr/local/pgsql/bin/psql -U postgres -d testdb < dump.sql

# Grant all privileges
/usr/local/pgsql/bin/psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE testdb TO userA;"

# Enter postgresql command line
/usr/local/pgsql/bin/psql -U postgres -d poesgres -h localhost -p 5432


基本設定
  • /usr/local/pgsql/data/postgres.conf
listen_addresses = '192.168.0.3'   # 設定listen IP

  • /usr/local/pgsql/data/pg_hba.conf
host    all             all             127.0.0.1/32              trust
host    all             all             192.168.0.0/16            trust
host    all             all             10.10.1.5/32              password


Log分析
推薦使用pgFouine - a PostgreSQL log analyzer,它可以分析出某段時間內,做了幾次query,處理時間最久的,次數最多的...可以看一下Sample reports長怎樣。


效能測試:
pgbench
使用 pgbench 进行数据库压力测试
pgbench -U postgres -i -s 50 postgres 
pgbench -U postgres -c 100 -t 100 -S postgres


Performance Tuning PostgreSQL

Python Decorator (裝飾)

參考資料

Example 1
在原始function的return值做裝飾
def D1(fn):
    def newFn():
        return "" + fn() + ""
    return newFn

@D1
def f1():
    return "a sentence"

print f1()


2011/09/20

Video Codec, Container

常聽到一些影音格式與專有名詞:avi、rmvb、mpg4、h.264...
有些是指播放格式,有些是編碼格式,什麼是codec,什麼是container,下列兩篇介紹得很清楚!

大綱:
Codec(編碼):是原始影音的一種資料表示法,它決定影音檔案之畫質、音質好壞、壓縮率。
Container(容器):則是將上述編碼資料打包,方便傳遞、播放。

並非所有的container都可以搭配所有的codec,必須依照特性來存放,請參考Comparison of container formats
Codec也可以單獨使用,不一定要放在container內。

2011/09/19

製作 iPhone/iPad 電子書

想要製作可以在iPhone/iPad上瀏覽的電子書嗎? Go ahead!

首先要釐清,是想做『iBook電子書』還是『電子書App』
  • iBook電子書是可以用iBook這個app來瀏覽的文件檔,透過iTunes就可以自己同步到iPhone/iPad裡面。
  • 而製作一個獨立的電子書App手續就麻煩多了。首先你必須跟Apple申請一個開發者帳號 (年費NT3000),再來製作自己的App(可能需要會寫程式),最後把app submit到apple store,待工作人員審核過了,就可以在apple store供大家下載。

如果你只是想打發時間,想把網路小說放到iPhone那就選擇第一項。如果是想營運收費,那兩樣都可以上架收費。


iBook電子書製作

App電子書製作

初學 Tornado web framework

最近需要架設一個線上平台,對於架設平台我實在沒什麼經驗,從前有開發過網站,會用asp.net、php自己刻東西,但時代不一樣了,為了講求快速,依附在framework上是有必要的。Tornado — 對我來說是個全新的開始,我不會python,對物件導向的程式語言也不熟,平常都在寫C語言居多,更別提用過web framework了...但我堅信我的底子跟熱情可以支撐我去學習這一切!

話說剛開始學習真的超不順,中文英文的教學都很少,官方的文件又寫的很簡略...但看著看著,突然有一天感覺就來了,就像任督二脈被打通般的,我看得懂了,我會使用了,程式寫這麼久了,我覺得對我幫助最大就是學會這種感覺。如果看懂Overview又學會使用Source code裡面的demo程式,對Tornado的掌握度就很高了!

API
Tornado真是個強大的東西,它陳列的API不多,但每一個都是在建立平台時很常見的需求:
  • OAuth第三方認證 - 使用facebook、google、twitter帳號來登入自己的網站,API都很精簡了
  • Python decorator tornado.web.authenticated - Sorry我不知道怎麼翻譯這個,簡單來說就是user尚未通過認證,存取到任何網頁都會被導回login頁面
  • Non-blocking, asynchronous http request - facebook graph API query、backend server query
  • Template - MVC架構開發,這是每個framewrok都必備的
  • Security cookie - 防止cookie被篡改
  • L10N, CSRF protect

IDE選擇

2011/09/14

Python 教學網站

基本語法教學 - 初學者用

推薦IDE - PyDev

如何在MacOS下安裝python套件?
參考官方網站 Python on the Macintosh

建議使用 Macports 套件管理程式來安裝:
# 搜尋套件
port list | grep python

# 安裝python主程式
sudo port install python26

# 安裝 easy_install
sudo port install py26-setuptools

#之後就可以利用easy_install去安裝更多python套件了

2011/09/06

Forward Proxy 的選擇 - Squid

Forward proxy 與 Reverse proxy 是什麼可以參考聯成電腦這篇的說明

用途方面簡單舉幾個例子

  • Forward proxy
    • 用戶套用Hinet的proxy,抓國外的東西會比較快,因為proxy上面已經cache一份。
    • 公司或宿舍網路禁止對外存取facebook或上msn,proxy上有強大的parser可以check/replace特定字眼。
  • Reverse proxy
    • 在多台web server前架一台proxy做分流可以大大提高capacity與安全性,推薦Nginx。
    • 搭配virtual host,可將不同domain name的request導向不同的server。



最近剛好有使用forward proxy的需求,也就是用戶透過proxy連到public網路,如果遇到連接對向很慢或不存在的時候,此時proxy的connection就會被占住,直到timeout為止。剛開始是使用apache,apache prefork或worker模式下,其connection的數量是有限的(再設定檔內指定),若遇到上述的狀況,整體服務狀況就會變得很差。

Proxy的選擇
  • Apache + module proxy: 很吃記憶體,polling的做法不合用
  • Tinyproxy: 較apache省記憶體,但運作方式也是一個thread一個connection,response time比apache還差
  • Lighttpd: 不支援forward proxy,底層為epoll
  • Nginx: 不支援forward proxy,底層為epoll
  • Squid: 支援forward proxy,底層採用epoll (最後的選擇)

Squid的filter真的很強大,過濾ip, port, mac都沒有問題。
效能方面,我讓他hang了20,000條connection,new request上去反應並沒有被影響。

設定方面可以參考

2011/09/04

實作 TableView Section 展開/收合

iPhone中的UITableViewController變化真的很多!
最近剛好需要實作展開/收合的功能,效果如圖所示:

我是參考Expanding/Collapsing TableView Sections的實作,然後在自己簡化。
一個展開/收合的單位為一個Section,Row0表示Parent,Row1~N表示Child,程式碼中都有註解。

2011/08/31

Apache with event MPM mode

最近把apache當forward proxy來使用,遇到反應速度嚴重下降的問題。

Apache在prefork mode下,一個process同時間只能服務一條connection,而worker mode一個thread只能服務一條connection,他們都是blocking mode,一旦所有的process/thread被block住,就沒辦法再服務新的connection,系統全部都在等待io,CPU使用率相對很低。

Event MPM mode可以解決上述問題。

(題外話,就算是把fd設定成non-blocking,搭配polling (select())的方式,其反應速度仍遠不及event-driven,因為fd數目越多polling的時間就會越久,而且polling也是很浪費CPU的。Event-driven的高效能要在current connection量大時才看得出來,特別是keep-alive的connection。)

不過官方網站寫著event mode目前還在實驗階段:
Warning
This MPM is experimental, so it may or may not work as expected.

編譯與設定可以參考

Configure參數
./configure --with-mpm=event #主要是加這個參數

httpd.conf設定檔
        
     StartServers         1
     ServerLimit          1
     MaxClients           50
     MinSpareThreads      1
     MaxSpareThreads      10
     ThreadsPerChild      50
     MaxRequestsPerChild  0


Benchmark在上面的連結中也有提到。

2011/08/08

Oprofile - program performance analysis

一套 profiling 工具,透過分析結果可以得知程式運作這段期間,哪個 function 花費最多時間,哪一行程式碼被執行最多次。

下載

使用步驟
Initial and setup
$ opcontrol --init
$ opcontrol --reset 
$ opcontrol --setup --no-vmlinux --separate=library
$ opcontrol --start
--separate=none: 只想看程式本身的 function
--separate=lib: 想連程式使用到的library都一起看到
適度調整取樣率

啟動想分析的 program
$ ./app

Stop
$ opcontrol --dump
$ opcontrol --stop
$ opcontrol -h

Analysis
$ opreport -l ./app

CPU: CPU with timer interrupt, speed 1999.97 MHz (estimated)
Profiling through timer interrupt
samples  %        image name               symbol name
7251     30.3186  libx264.so.128           x264_cavlc_init
6247     26.1206  libx264.so.128           x264_coeff_level_run16
3018     12.6192  libx264.so.128           x264_analyse_init_costs   encoder/analyse.c:292
1496      6.2552  libavcodec.so.52.72.2    /opt/ffmpeg-0.6.5/lib/libavcodec.so.52.72.2
1426      5.9625  libc-2.12.so             __strcmp_sse42
1120      4.6831  libm-2.12.so             __ieee754_log2f
384       1.6056  libavformat.so.52.64.2   /opt/ffmpeg-0.6.5/lib/libavformat.so.52.64.2
329       1.3756  libc-2.12.so             memcpy
239       0.9993  libc-2.12.so             _int_malloc
213       0.8906  libm-2.12.so             __ieee754_pow
180       0.7526  libx264.so.128           x264_cqm_init

$ opannotate --source ./app

... (執行次數) (花費比例)

               :static uint64_t pop_buffer_value(struct transient * trans)
 11510  1.9661 :{ /* pop_buffer_value total:  89901 15.3566 */
               :        uint64_t val;
               :
 10227  1.7469 :        if (!trans->remaining) {
               :                fprintf(stderr, "BUG: popping empty buffer !\n");
               :                exit(EXIT_FAILURE);
               :        }
               :
               :        val = get_buffer_value(trans->buffer, 0);
  2281  0.3896 :        trans->remaining--;
  2296  0.3922 :        trans->buffer += kernel_pointer_size;
               :        return val;
 10454  1.7857 :}
...



在 AWS EC2 上使用 oprofile
opcontrol --deinit 
modprobe oprofile timer=1
opcontrol --reset 
opcontrol --no-vmlinux --separate=library
opcontrol --start


參考資料

2011/07/21

Django初體驗

Web開發技術真是越來越強大了,一段時間沒碰就跟不上了,此時我的技術力還停留在ASP.Net 2.0與純PHP的時代... 快速開發與MVC架構似乎是framework的最大訴求,最近碰到要寫網頁,在想要選擇什麼走什麼路好,雖然有些許PHP底子,但最後是擔心PHP framework performance的問題,選擇了Python Django,開啟新的學習旅程~

第一步:Quick Start 照著做一次

第二步:了解MTV概念與專案內的manage.py settings.py urls.py各是做什麼用的

第三步:



Oh no! 遇到一些麻煩...
django-nonrel是用來補足Django NoSQL這一塊,目前支援的NoSQL有 MongoDB, ElasticSearch, Cassandra,但Amazon SimpleDB還沒完成阿!只好被迫放棄. (雖然說有找到 boto - Python interface to Amazon Web Services 提供了SimpleDB的存取方式,但就用不到ORM的特性了)


沒想到初體驗這麼快就結束了...

2011/07/19

透過NSURLConnection和NSMutableURLRequest上傳/POST一個檔案

直接來看code
NSURLConnection *conn;
NSMutableData *tempData;  /* Store response */

- (void)uploadFile {
    NSString *boundary = @"0xKhTmLbOuNdArY";
    NSData *imgData = UIImageJPEGRepresentation(image.image, 90);  /* Image data come from UIImageView */
    NSString *urlString = @"http://my_server/upload.php";
    NSURL *url = [[NSURL alloc]initWithString:urlString];

    /* Set up request */
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
    [urlRequest setHTTPMethod:@"POST"];
    [urlRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];

    /* Prepare content part */
    NSMutableData *postData = [NSMutableData dataWithCapacity:[imgData length] + 512];
    [postData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [postData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"file.bin\"\r\n\r\n", @"image"] dataUsingEncoding:NSUTF8StringEncoding]];
    [postData appendData:imgData];
    [postData appendData: [[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    /* Assign content part */
    [urlRequest setHTTPBody:postData];

    tempData = [NSMutableData alloc];
    conn = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
}


Prepare 4 delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [conn release];
    NSLog(@"Error occur");
}

- (void)connection: (NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)aResponse {
    NSInteger status = (NSInteger)[(NSHTTPURLResponse *)aResponse statusCode];
    NSLog(@"%d", status);
}

-(void) connection:(NSURLConnection *)connection didReceiveData: (NSData *) incomingData {
 [tempData appendData:incomingData];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSString *loadData = [[NSMutableString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];    
    NSLog(@"%@", loadData);
}


Server - PHP sample code
<?php 
    if (isset($_FILES["image"]))
    {
        print_r($_FILES["image"]);
    }
?>

參考資料
CocoaDev: HTTPFileUploadSample

2011/07/12

在iPhone上建立HTTP連線(GET/POST)

HTTP Get
Create request
NSString *str = @"http://my_server/get.php?name=brian&gender=male";
NSString *str_enc = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL URLWithString:str_enc] retain];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];

Prepare delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;


HTTP Post
NSString *content = @"name=brian&gender=male";
NSData *postData = [content dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];
 
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:@"http://my_server/post.php"]];
[request setHTTPMethod:@"POST"];

/* Header */
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];

/* Content */
[request setHTTPBody:postData];

connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
// 跟GET一樣設定4組delegate去接response

參考資料

2011/07/07

架設 Apple Push Notification Provider Server

參考下列兩篇教學


針對製作pem的地方我再加強解說(這邊我搞超久的)。
延續上一篇Apple Push Notification Service - 推播服務,此時你手上會有下列東西:

  • CertificateSigningRequest.certSigningRequest (憑證申請書)
  • aps_development.cer (憑證,從Apple下載的)


原文是請你到Keychain Assistant(鑰匙圈存取)去製作apns-dev-cert.p12的檔案,不過我就是沒辦法export出來。還好找到另一個手動解法:HowToCreatePKCS12Certificate

1. 打開"鑰匙圈存取",找到自己的Private Key(專用密鑰),按右建選擇匯出成private_key.p12

2. 將 aps_development.cer 轉換為 aps_development.pem
openssl x509 -in aps_development.cer -inform DER -out aps_development.pem -outform PEM}

3. 將 private_key.p12 轉換為 private_key.pem
openssl pkcs12 -nocerts -out private_key.pem -in private_key.p12

4. 去除轉發密碼
openssl rsa -out private_key_noenc.pem -in private_key.pem

5. 合併 aps_development.pemprivate_key_noenc.pem
cat aps_development.pem private_key_noenc.pem > apns_dev.pem

有了 apns_dev.pem 這個憑證就可以傳送推播了!



傳送時的 message payload 格式也可以參考上面兩篇文章。

最後提供幾個人家implement好的APNS


Production server
  • ssl://gateway.push.apple.com:2195
  • Development server
  • ssl://gateway.sandbox.push.apple.com:2195
  • 2011/07/06

    Facebook API with iPhone app

    最近在研究怎麼使用facebook API,看了蠻多的參考資料:
    Sorry 還蠻雜的,沒有太多時間做整理 ><


    簡單來說,有兩件事情要做
    • 學習使用 Facebook iOS SDK
    • API有哪些?要怎麼使用?
    Facebook API走HTTP協定,資料交換格式為JSON,採用OAuth方式來認證。
    Facebook iOS SDK其實就是幫你搞定這些事情,預設會開啓Safari或fb外掛來display login畫面,其中還包含了permission確認,如果上述兩項無法使用,則改用SDK裡面自己刻的dialog。使用者login後會拿到auth token,之後access API都要靠它。Request包成callback形式,接到response再做對應的事情。資料格式也幫你parse成NSData格式,存取相當方便。

    SDK下載按這裡

    稍微整理SDK使用方式 (不同時期的SDK使用方法不太一樣,下面敘述的是2011/7的版本)

    2011/06/22

    Apple Push Notification Service - 推播服務

    最近在研究怎麼收送push notification,以及怎麼架設notification server。


    推播服務的介紹及運作方式可以參考:
    簡單來說就是每個iOS的app第一次開啓時會向apple註冊推播服務,然後得到專屬的device token,可以想像成擁有專屬的tunnel,再來只要向apple的server傳送message(JSON格式),並帶上target device token,server就會自動幫你轉傳給對方。



    寫一個小小的測試程式
    網站說明非常清楚,大致上的流程如下:
    • 登入 iOS dev center 然後創建一個新的 App ID
    • 開啓 push notification service
    • 下載 "Development Push SSL Certificate" 及 "Provisioning Profile"
    Receiver (iOS app):
    • 安裝 provisioning profile
    • 設定 identifier
    • Write the code to register from remote notification server and get "device token"
    範例:
    要求使用者開啓 Push notification 服務
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        [[UIApplication sharedApplication]registerForRemoteNotificationTypes:
      (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
    }
    

    Implement 3個 delegate
    - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    { 
        /* Get device token */ 
        NSString *strDevToken = [NSString stringWithFormat:@"%@", deviceToken];
    
        /* Replace '<', '>' and ' ' */
        NSCharacterSet *charDummy = [NSCharacterSet characterSetWithCharactersInString:@"<> "];
        strDevToken = [[strDevToken componentsSeparatedByCharactersInSet: charDummy] componentsJoinedByString: @""];
        NSLog(@"Device token=[%@]", strDevToken);
    
        /* 可以把token傳到server,之後server就可以靠它送推播給使用者了 */
    }
    
    - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
    {
        NSLog(@"Error=[%@]", err);
        // TODO: when user do not allow push notification service, pop the warning message.
    }
    
    // This function called when receive notification and app is in the foreground.
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 
    {
        /* 把收到的推播列舉出來 */
        for (id key in userInfo) {
            NSLog(@"Key=[%@], Value=[%@]", key, [userInfo objectForKey:key]); 
        }
    
        /* 印出 Badge number */
        NSLog(@"Badge: %@", [[userInfo objectForKey:@"aps"] objectForKey:@"badge"];
    
    }
    

    Sender (MacOS app):
    • Base on sample code "PushMeBaby"
    • Install "Development Push SSL Certificate"
    • Set target device token then send

    參考資料


    至於要如何架設APNS,可以參考下一篇:架設 Apple Push Notification Provider Server

    2011/06/20

    HTTP server over libevent - sample code (2)

    加入一些parse的function,可以得到request的method、URI、header、body、path

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SVR_IP                         "127.0.0.1"
    #define SVR_PORT                       8080
    
    void dump_cb (struct evhttp_request *req, void *arg) {
        const char        *uri;
        struct evkeyvalq  *headers;
        struct evkeyval   *header;
        struct evbuffer   *evbuf;
        struct evhttp_uri *ev_uri;
        
        /* Get method */
        switch (evhttp_request_get_command(req)) {
            case EVHTTP_REQ_GET: 
                printf("Request method=[GET]\n");
                break;
            case EVHTTP_REQ_POST:
                printf("Request method=[POST]\n");
                break;
            default:
                printf("Request method=[others]\n");
                break;
        }
        
        /* Get path */
        uri = evhttp_request_get_uri(req);
        printf("Request uri=[%s]\n",  uri);
        
        /* Get header */
        printf("Headers:\n");
        headers = evhttp_request_get_input_headers(req);
        for (header = headers->tqh_first; header;
            header = header->next.tqe_next) {
            printf("  %s: %s\n", header->key, header->value);
        }
            
        /* Get body */
        evbuf = evhttp_request_get_input_buffer(req);
        printf("Body:\n");
        while (evbuffer_get_length(evbuf)) {
            int  size;
            char buf[128];
            
            memset(buf, 0, sizeof(buf));
            size = evbuffer_remove(evbuf, buf, sizeof(buf) - 1);
            printf("  %s\n", buf);
        }
        
        /* */
        ev_uri = evhttp_uri_parse(uri);
        if (ev_uri) {
            const char *path;
            char       *decoded_path;
            
            path = evhttp_uri_get_path(ev_uri);
            decoded_path = evhttp_uridecode(path, 0, NULL);
            printf("Request path=[%s]\n", decoded_path);
            
            /* Free resource */
            free(decoded_path);
            evhttp_uri_free(ev_uri);
        }
        
        /* Response */
        evhttp_send_reply(req, HTTP_OK, "OK", NULL);
    }
    
    int main (int argc, char **argv) {
        struct event_base *evbase;
        struct evhttp     *evhttp;
    
        /* Init event base */
        if ((evbase = event_base_new()) == NULL) {
            printf("event_base_new() failed\n");
            return -1;
        }
    
        /* Init evhttp */
        if ((evhttp = evhttp_new(evbase)) == NULL) {
            printf("evhttp_new() failed\n");
            return -1;
        }
    
        /* Set server IP, port */
        if (evhttp_bind_socket(evhttp, SVR_IP, SVR_PORT) == -1) {
            printf("evhttp_bind_socket() failed\n");
            return -1;
        } else {
            printf("Listening on [%s:%d]\n", SVR_IP, SVR_PORT);
        }
        
        /* Set timeout */
        evhttp_set_timeout(evhttp, 10);
        
        /* Set a callback for default path */
        evhttp_set_gencb(evhttp, dump_cb, NULL);
    
        /* Enter event loop */
        event_base_dispatch(evbase);
    
        /* Free resource */
        evhttp_free(evhttp);
        event_base_free(evbase);
        
        return 0;
    }
    
    


    Compile and run
    $ gcc -g -Wall -o httpd httpd.c -levent -I/opt/libevent-2.0.12-stable/include/ -L/opt/libevent-2.0.12-stable/lib/
    $ LD_LIBRARY_PATH=/opt/libevent-2.0.12-stable/lib ./httpd
    Listening on [127.0.0.1:8080]
    
    Request method=[GET]
    Request uri=[/test.php?name=kevin&sex=male]
    Headers:
      User-Agent: Wget/1.11.4 Red Hat modified
      Accept: */*
      Host: 127.0.0.1:8080
      Connection: Keep-Alive
    Body:
    Request path=[/test.php]
    
    Request method=[POST]
    Request uri=[/test.php]
    Headers:
      User-Agent: Wget/1.11.4 Red Hat modified
      Accept: */*
      Host: 127.0.0.1:8080
      Connection: Keep-Alive
      Content-Type: application/x-www-form-urlencoded
      Content-Length: 19
    Body:
      name=sylar&sex=male
    Request path=[/test.php]
    
    

    try to send a request to server...
    # Get
    $wget -q -O - 'http://127.0.0.1:8080/test.php?name=sylar&sex=male'
    
    # Post
    $wget -q -O - --post-data='name=sylar&sex=male' http://127.0.0.1:8080/test.php
    

    2011/06/17

    HTTP server over libevent - sample code (1)

    使用libevent HTTP API寫一個簡易的server。

    #include 
    #include 
    #include 
    
    #define SVR_IP                         "127.0.0.1"
    #define SVR_PORT                       8080
    
    void specific_cb (struct evhttp_request *req, void *arg) {
        struct evbuffer *evbuf;
        
        if ((evbuf = evbuffer_new()) == NULL) {
            printf("evbuffer_new() failed");
            evhttp_send_reply(req, HTTP_INTERNAL, "Internal error", NULL);
            return;
        }
    
        /* Body */
        evbuffer_add_printf(evbuf, "It's work!");
        
        /* Response */
        evhttp_send_reply(req, HTTP_OK, "OK", evbuf);
        
        /* Free resource */
        evbuffer_free(evbuf);
    }
    
    void generic_cb (struct evhttp_request *req, void *arg) {
    
        /* Response */
        evhttp_send_reply(req, HTTP_NOTFOUND, "Not found", NULL);
    }
    
    int main (int argc, char **argv) {
        struct event_base *evbase;
        struct evhttp     *evhttp;
    
        /* Init event base */
        if ((evbase = event_base_new()) == NULL) {
            printf("event_base_new() failed\n");
            return -1;
        }
    
        /* Init evhttp */
        if ((evhttp = evhttp_new(evbase)) == NULL) {
            printf("evhttp_new() failed\n");
            return -1;
        }
    
        /* Set server IP, port */
        if (evhttp_bind_socket(evhttp, SVR_IP, SVR_PORT) == -1) {
            printf("evhttp_bind_socket() failed\n");
            return -1;
        } else {
            printf("Listening on [%s:%d]\n", SVR_IP, SVR_PORT);
        }
        
        /* Set a callback for specific path */
        if (evhttp_set_cb(evhttp, "/test", specific_cb, NULL) < 0) {
            printf("evhttp_set_cb() failed\n");
            return -1;
        }
        
        /* Set a callback for default path */
        evhttp_set_gencb(evhttp, generic_cb, NULL);
    
        /* Enter event loop */
        event_base_dispatch(evbase);
    
        /* Free resource */
        evhttp_free(evhttp);
        event_base_free(evbase);
        
        return 0;
    }
    
    Compile and run
    $ gcc -g -Wall -o httpd httpd.c -levent -I/opt/libevent-2.0.12-stable/include/ -L/opt/libevent-2.0.12-stable/lib/
    $ LD_LIBRARY_PATH=/opt/libevent-2.0.12-stable/lib ./httpd
    Listening on [127.0.0.1:8080]
    
    try to send a request to server...
    $ wget -q -O - http://127.0.0.1:8080/test
    $ wget -S -O - http://127.0.0.1:8080/xxx
    
    不知道為什麼我用2.0.10-stable版本跑會有下列warning,用2.0.12-stable就沒問題了。
    [warn] evhttp_connection_base_new: bufferevent_new failed: No such file or directory
    [warn] evhttp_get_request: cannot get connection on 9: No such file or directory
    

    2011/06/16

    Echo server over libevent - sample code

    一個簡易的echo server,使用了bufferevent。

    Bufferevent
    Bufferevents are higher level than evbuffers: each has an underlying evbuffer for reading and one for writing, and callbacks that are invoked under certain circumstances.

    #include <stdio.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <event.h>
    #include <event2/listener.h>
    
    #define SVR_IP                         "127.0.0.1"
    #define SVR_PORT                       10000
    #define BUFF_SIZE                      1024
    
    static void read_cb(struct bufferevent *bev, void *ctx) {
        char buff[BUFF_SIZE];
        int len;
    
        memset(buff, 0, sizeof(buff));
    
        /* Read data */
        len = bufferevent_read(bev, buff, sizeof(buff));
        printf("read: len=[%d] data=[%s]\n", len, buff);
    
        /* Write data */
        bufferevent_write(bev, buff, len);
    }
    
    static void write_cb(struct bufferevent *bev, void *ctx) {
        printf("write finished\n");
    }
    
    static void event_cb(struct bufferevent *bev, short events, void *ctx) {
        if (events & BEV_EVENT_EOF) {
            printf("client disconnect\n");
            bufferevent_free(bev);
        } else if (events & BEV_EVENT_TIMEOUT) {
            printf("client timeout\n");
            bufferevent_free(bev);
        } else {
            /* Other case, maybe error occur */
            bufferevent_free(bev);
        }
    }
    
    static void accept_cb(struct evconnlistener *lev, evutil_socket_t fd,
            struct sockaddr *sa, int socklen, void *ctx) {
        struct event_base *evbase = ctx;
        struct bufferevent *bev;
        struct timeval tv;
    
        printf("client connect from [%s:%u] over fd [%d]\n", 
                inet_ntoa(((struct sockaddr_in *) sa)->sin_addr), 
                (unsigned short) ntohs(((struct sockaddr_in *) sa)->sin_port), fd);
    
        /* Create socket-based buffer event */
        bev = bufferevent_socket_new(evbase, fd, BEV_OPT_CLOSE_ON_FREE);
        if (bev == NULL) {
            printf("bufferevent_socket_new() failed\n");
            evutil_closesocket(fd);
            return;
        }
    
        /* Set up callback function */
        bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
    
        /* Set up timeout for data read */
        tv.tv_sec = 5;
        tv.tv_usec = 0;
        bufferevent_set_timeouts(bev, &tv, NULL);
    
        /* Enable read event */
        bufferevent_enable(bev, EV_READ);
    }
    
    int main(void) {
        struct event_base *evbase;
        struct sockaddr_in sin;
        struct evconnlistener *lev;
    
        /* Initialize event base */
        if ((evbase = event_base_new()) == NULL) {
            printf("event_base_new() failed\n");
            return -1;
        }
    
        /* Set up server address */
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = inet_addr(SVR_IP);
        sin.sin_port = htons(SVR_PORT);
    
        /* Bind socket */
        lev = evconnlistener_new_bind(evbase, accept_cb, evbase, 
                LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1, 
                (struct sockaddr *) &sin, sizeof(sin));
    
        if (lev == NULL) {
            printf("bind() failed\n");
            return -1;
        } else {
            printf("bind to [%s:%u] successfully\n", SVR_IP, SVR_PORT);
        }
    
        /* Enter event loop */
        event_base_dispatch(evbase);
    
        return 0;
    }
    
    

    編譯 (指定library path)
    gcc -g -Wall -o server server.c -levent -I/opt/libevent-2.0.10-stable/include/ -L/opt/libevent-2.0.10-stable/lib/
    

    C socket client in Linux - sample code

    Sample code

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define LOCAL_IP                        "127.0.0.1"
    #define LOCAL_PORT                      5678
    #define SVR_IP                          "127.0.0.1"
    #define SVR_PORT                        8080
    #define BUF_SIZE                        1024
    #define MESSAGE                         "hello"
    
    int main (int argc, char **argv) {
        struct sockaddr_in  local_addr, server_addr;
        socklen_t           len;
        int                 sock_fd;
        char                buff[BUF_SIZE];
        int                 recv_len;
        
        len = sizeof(struct sockaddr_in);
        
        /* Set local address (In general, we don't care local address) */
        memset(&local_addr, 0, sizeof(local_addr));
        local_addr.sin_family = AF_INET;
        local_addr.sin_addr.s_addr = inet_addr(LOCAL_IP);
        local_addr.sin_port = htons(LOCAL_PORT);
        
        /* Set server address */
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = inet_addr(SVR_IP);
        server_addr.sin_port = htons(SVR_PORT);
    
        /* Create endpoint */
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket()");
            return -1;
        } else {
            printf("sock_fd=[%d]\n", sock_fd);
        }
    
        /* Bind */
        if (bind(sock_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) == -1) {
            perror("bind()");
            return -1;
        } else {
            printf("bind [%s:%u] success\n",
                inet_ntoa(local_addr.sin_addr), ntohs(local_addr.sin_port));
        }
    
        /* Connect */
        if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
            perror("connect()");
            return -1;
        } else {
            printf("connect to [%s:%u] success\n",
                inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
        }
        
        /* Send */
        send(sock_fd, MESSAGE, strlen(MESSAGE), 0);
    
        /* Receive */
        memset(buff, 0, sizeof(buff));
        recv_len = recv(sock_fd, buff, sizeof(buff), 0);
        if (recv_len == -1) {
            perror("recv()");
            return -1;
        } else if (recv_len == 0) {
            printf("Client disconnect\n");
        } else {
            printf("Receive: len=[%d] msg=[%s]\n", recv_len, buff);
        }
        
        return 0;
    }
    
    

    2011/06/15

    C socket server in Linux - sample code

    長期用libevent來implement socket program,都快忘了一般select的用法了,寫了一個echo server
    供各位參考。

    不過有一點我還是不太懂,為何fd_set要copy一份起來,是因為fd_set 經過select function後就無效了嗎?我試過只用一個fd_set,果真只能接收到第一個request,除非每次select前重新把所有的fd做FD_SET()。

    Select的用法可以參考:石頭閒語:select() - I/O Multiplexer

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SVR_IP                          "127.0.0.1"
    #define SVR_PORT                        8080
    #define BUF_SIZE                        1024
    
    int main (int argc, char **argv) {
        struct sockaddr_in  server_addr;
        socklen_t           len;
        fd_set              active_fd_set;
        int                 sock_fd;
        int                 max_fd;
        int                 flag = 1;
        char                buff[BUF_SIZE];
        
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = inet_addr(SVR_IP);
        server_addr.sin_port = htons(SVR_PORT);
        len = sizeof(struct sockaddr_in);
    
        /* Create endpoint */
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket()");
            return -1;
        } else {
            printf("sock_fd=[%d]\n", sock_fd);
        }
    
        /* Set socket option */
        if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {
            perror("setsockopt()");
            return -1;
        }
    
        /* Bind */
        if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
            perror("bind()");
            return -1;
        } else {
            printf("bind [%s:%u] success\n",
                inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
        }
    
        /* Listen */
        if (listen(sock_fd, 128) == -1) {
            perror("listen()");
            return -1;
        }
    
        FD_ZERO(&active_fd_set);
        FD_SET(sock_fd, &active_fd_set);
        max_fd = sock_fd;
    
        while (1) {
            int             ret;
            struct timeval  tv;
            fd_set          read_fds;
    
            /* Set timeout */
            tv.tv_sec = 2;
            tv.tv_usec = 0;
    
            /* Copy fd set */
            read_fds = active_fd_set;
            ret = select(max_fd + 1, &read_fds, NULL, NULL, &tv);
            if (ret == -1) {
                perror("select()");
                return -1;
            } else if (ret == 0) {
                printf("select timeout\n");
                continue;
            } else {
                int i;
    
                /* Service all sockets */
                for (i = 0; i < FD_SETSIZE; i++) {
                    if (FD_ISSET(i, &read_fds)) {
                        if (i == sock_fd) {
                            /* Connection request on original socket. */
                            struct sockaddr_in  client_addr;
                            int                 new_fd;
    
                            /* Accept */
                            new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &len);
                            if (new_fd == -1) {
                                perror("accept()");
                                return -1;
                            } else {
                                printf("Accpet client come from [%s:%u] by fd [%d]\n",
                                    inet_ntoa(client_addr.sin_addr),
                                    ntohs(client_addr.sin_port), new_fd);
    
                                /* Add to fd set */
                                FD_SET(new_fd, &active_fd_set);
                                if (new_fd > max_fd)
                                    max_fd = new_fd;
                            }
                        } else {
                            /* Data arriving on an already-connected socket */
                            int recv_len;
    
                            /* Receive */
                            memset(buff, 0, sizeof(buff));
                            recv_len = recv(i, buff, sizeof(buff), 0);
                            if (recv_len == -1) {
                                perror("recv()");
                                return -1;
                            } else if (recv_len == 0) {
                                printf("Client disconnect\n");
                            } else {
                                printf("Receive: len=[%d] msg=[%s]\n", recv_len, buff);
    
                                /* Send (In fact we should determine when it can be written)*/
                                send(i, buff, recv_len, 0);
                            }
    
                            /* Clean up */
                            close(i);
                            FD_CLR(i, &active_fd_set);
                        }
    
                    } // end of if
                } //end of for
            } // end of if
        } // end of while
        
        return 0;
    }
    
    

    2011/06/08

    Get IPv4 address from sockaddr or socket

    (sock_fd is a socket)
    sockaddr_in  addr_in;
    int          len = sizeof(sockaddr_in);
    
    /* Get sockaddr_in structure information from socket */
    getsockname(sock_fd, (sockaddr *)&addr_in, (socklen_t *)&len);
    
    /* Get address from sockaddr_in */
    printf("IP=[%s] port=[%d]\n", inet_ntoa(addr_in.sin_addr), addr_in.sin_port);
    

    2011/06/02

    netcat

    Top 100 Network Security Tools 排行第四名的軟體,也是我在寫socket program時最常用到的工具,不管是當server/client,tcp/udp都沒問題!


    wiki中已經講述了大部份的使用方法:

    • raw connection
    • http server
    • file server
    • proxy server
    • port scanning
    • port forwarding

    下載:
    The GNU Netcat -- Official homepage -- Downloads

    Support SSL的版本
    netcat SSL

    這一套感覺更強大,可以support UDP和SSL!
    SSL Capable NetCat

    2011/05/19

    UDP server over libevent - sample code

    基本的sample code

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <event.h>
    #include <event2/listener.h>
    
    #define SVR_IP                          "127.0.0.1"
    #define SVR_PORT                        10000
    #define BUF_SIZE                        1024
    
    void read_cb(int fd, short event, void *arg) {
        char                buf[BUF_SIZE];
        int                 len;
        int                 size = sizeof(struct sockaddr);
        struct sockaddr_in  client_addr;
    
        memset(buf, 0, sizeof(buf));
        len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &size);
    
        if (len == -1) {
            perror("recvfrom()");
        } else if (len == 0) {
            printf("Connection Closed\n");
        } else {
            printf("Read: len [%d] - content [%s]\n", len, buf);
            
            /* Echo */
            sendto(fd, buf, len, 0, (struct sockaddr *)&client_addr, size);
        }
    }
    
    int bind_socket(struct event *ev) {
        int                 sock_fd;
        int                 flag = 1;
        struct sockaddr_in  sin;
    
        /* Create endpoint */
        if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
            perror("socket()");
            return -1;
        }
    
        /* Set socket option */
        if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {
            perror("setsockopt()");
            return 1;
        }
    
        /* Set IP, port */
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = inet_addr(SVR_IP);
        sin.sin_port = htons(SVR_PORT);
    
        /* Bind */
        if (bind(sock_fd, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) {
            perror("bind()");
            return -1;
        } else {
            printf("bind() success - [%s] [%u]\n", SVR_IP, SVR_PORT);
        }
    
        /* Init one event and add to active events */
        event_set(ev, sock_fd, EV_READ | EV_PERSIST, &read_cb, NULL);
        if (event_add(ev, NULL) == -1) {
            printf("event_add() failed\n");
        }
    
        return 0;
    }
    
    int main(void) {
        struct event  ev;
    
        /* Init. event */
        if (event_init() == NULL) {
            printf("event_init() failed\n");
            return -1;
        }
    
        /* Bind socket */
        if (bind_socket(&ev) != 0) {
            printf("bind_socket() failed\n");
            return -1;
        }
    
        /* Enter event loop */
        event_dispatch();
    
        return 0;
    }
    

    2011/05/17

    mtrace - 檢查memory leak

    Memory leak中文叫內存洩漏,也是我在開發程式中一個很難搞的問題。拜一些tool所賜,讓工程師可以更容易發現問題所在。

    mtrace是glibc內提供的工具,其實它的原理很簡單,就是把你程式中malloc()與free()的位址全部下來,最後兩兩配對,殘留下來沒有配對到的就是leak。

    1. 安裝glibc-utils

    2. 在程式中include header file並在程式最前面call mtrace()
    e.g. test.c
    #include 
    #include 
    
    int main(void) {
        char *p;
    
        mtrace();
        p = malloc(5);  // 要一塊記憶體,但沒有釋放
    
        return 0;
    }
    

    3. compile
    $ gcc -g -o test test.c
    
    一定要加-g

    4. run program
    $ MALLOC_TRACE=output.log ./test
    
    MALLOC_TRACE指向output file

    5. 抓leak
    $ mtrace ./test ./output.log
    
    結果:
    Memory not freed:
    -----------------
               Address     Size     Caller
    0x0000000000c2d460      0x5  at /tmp/test.c:8
    
    很清楚看到test.c第8行allocate 5 byte未釋放

    --

    不過mtrace算是很陽春的工具,如果是間接allocte記憶體,如call object_new()這種init function,那mtrace就沒辦法表示得那麼清楚了。
    e.g. test2.c
    #include 
    #include 
    #include 
    
    int main(void) {
        GHashTable *ht;
    
        mtrace();
        ht = g_hash_table_new(NULL, NULL);
    
        return 0;
    }
    

    執行mtrace結果:
    Memory not freed:
    -----------------
               Address     Size     Caller
    0x0000000012a24460     0xfc  at 0x2b267c24f3b1
    0x0000000012a24570    0x1f8  at 0x2b267c24f3b1
    0x0000000012a24770    0x1f8  at 0x2b267c24f3b1
    0x0000000012a24970    0x7f0  at 0x2b267c24f3b1
    0x0000000012a25170     0xc0  at 0x2b267c24f3b1
    0x0000000012a25400    0x3f0  at 0x2b267c262ce1
    0x0000000012a25800    0x3f0  at 0x2b267c262ce1
    

    這樣的訊息對我們來說沒什麼幫助,這時候可以借助更強大的工具valgrind或heap checker。

    2011/05/16

    pstack - 列印出process stack trace

    有時候遇到process hang住了,我們想知道各thread目前function call stack為何就可以利用pstack。使用前提是binary還保有symbol(還未strip)

    用法︰
    $  pstack pid
    

    e.g.
    $ pstack `pgrep syslog-ng`
    
    Backtrace for pid 21374
    A syntax error in expression, near `'.
    #0  0x00000034bb6cb14f in poll () from /lib64/libc.so.6
    #1  0x0000000000402957 in main_context_poll (ufds=0x9b1920, nfsd=22,
        timeout_=14303) at main.c:134
    #2  0x00002b3ba086a90f in g_main_context_poll (context=0x966400, block=1,
        dispatch=1, self=) at gmain.c:3093
    #3  g_main_context_iterate (context=0x966400, block=1, dispatch=1,
        self=) at gmain.c:2775
    #4  0x00002b3ba086af0b in g_main_context_iteration (context=0x966400,
        may_block=1) at gmain.c:2843
    #5  0x0000000000402421 in main_loop_run (cfg=0x7ffff43c6040) at main.c:170
    #6  0x00000000004028d9 in main (argc=1, argv=0x7ffff43c6138) at main.c:448
    
    這個訊息跟在gdb下thread apply all bt是一樣的。

    下載︰
    我只找到Oracol Project: GDB pstack

    一些常用的 Linux 指令

    1. 查看某個網路service的連線狀態
    $ netstat -an | grep 127.0.0.1:5432 | awk '{print $6}' | sort | uniq -c
    
    結果:
    10 CLOSE_WAIT
    36 ESTABLISHED
     1 LISTEN
    33 TIME_WAIT


    2. 在top中查看特定的process
    $ top -p `pgrep sshd | sed -e :x -e '$!N;s/\n/,/;tx'`
    # sed 的作用是把換行字元取代成","
    
    結果︰
    PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    2198 root      16   0 90128 3308 2580 S  0.0  0.0   0:02.97 sshd
    2212 test      15   0 90228 1880 1104 S  0.0  0.0   0:20.82 sshd
    2224 root      16   0 90128 3308 2580 S  0.0  0.0   0:00.03 sshd


    3. 監看某一daemon的fd數量
    $ watch -n 1 'ls /proc/`pgrep daemon`/fd | wc -l'
    
    結果︰
    Every 1.0s: ls /proc/`pgrep daemon`/fd | wc -l            Mon May 16 21:01:04 2011
    
    50


    4. sed取代換行字元
    $ sed -e :x -e '$!N;s/\n/,/;tx'
    或
    $ sed '/^.*$/N;s/,/\n/g'
    


    5. 列出server上的服務
    $ netstat -ntulp
    
    結果:
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
    tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN      2304/httpd
    tcp        0      0 10.32.1.10:22               0.0.0.0:*                   LISTEN      2110/sshd
    tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      2136/sendmail: acce
    tcp        0      0 0.0.0.0:443                 0.0.0.0:*                   LISTEN      2304/httpd
    udp        0      0 127.0.0.1:514               0.0.0.0:*                               10575/syslog-ng
    udp        0      0 0.0.0.0:34224               0.0.0.0:*                               10614/collectd
    


    6. 查看Linux distribution
    $ lsb_release -a
    or
    $ cat /etc/issue
    or
    $ cat /proc/version
    

    crontab 自動排程

    安排例行性工作需要用到: backup, log rotate, clean tmp...

    1. 編輯crontab
    $ crontab -e
    或
    $ vim /etc/crontab
    

    2. 設定執行時間
    3  4  *  *  *    root      sh /home/backup.sh
    分 時 日 月 週   身份      指令
    
    上述指令為每天半夜4:03備份資料


    參考資料︰
    鳥哥的 Linux 私房菜 -- 例行性工作排程 (crontab)

    在windows下使用NFS - Windows Services for Unix (SFU)

    我的desktop使用WinXP的環境,長期以來都是在Windows下寫code,Linux下編譯。剛開始是透過samba來存取linux上的檔案,但samba的效能不知道為什麼這麼不盡理想,editor多開幾個file就開始hang住了,可能是editor會見一些function索引的關係吧?

    試過一些samba參數設定,但是都沒有改善。想到NFS協定可以走,但在Windows上找一套free的NFS client實在不容易,大多要付錢而且採用自己的程式介面。最後採用了Windows Services for Unix(SFU) - 這套 Microsoft自己出的軟體。

    1. 設定NFS

    2. 安裝SFU

    3. 將server上的/etc/group與/etc/passwd複製到C:\SFU\etc

    4. 開啟Windows Services for UNIX->Services for UNIX Administration
    選擇樹狀選單中的User Name Mapping->Configuration標籤
    設定Password file與Group file (在剛剛放置的C:\SFU\etc)

    5. 選擇Maps標籤
    按List Windows Users -> 選擇帳號
    按List UNIX Users -> 選擇帳號
    按Add

    6. 依照NFS設定的目錄掛載
    $ mount \\hostname\home\brian\share z:
    


    參考資料:
    「轉貼」Windows下Linux开发环境设置(一)--TFTP服务器设置和NFS服务器设置
    [Help] Windows mount NFS ?
    Optimizing NFS Performance
    在 Windows 系統掛載 UNIX NFS 分享目錄

    輕鬆架設 NFS (Network file system)

    1. 安裝必要工具
    nfs-utils
    portmap
    

    2. 在server上設定分享目錄
    $ vim /etc/exports
    加入 /home/brian/share 192.168.0.100(rw,async,anonuid=123,anongid=123)
    
    參數說明:
    IP: 只允許192.168.0.100的IP存取
    rw: 可讀可寫
    async: 非同步存取加快速度
    123: 是brian在/etc/passwd的id,也就是你將會在Windows會使用brian這個帳號透過NFS存取存取Linux檔案

    2. 重啟NFS
    $ /etc/init.d/portmap start
    $ /etc/init.d/nfs start
    

    3. 檢視一下分享目錄
    $ showmount -e
    

    4. 在其他主機掛載看看
    $ mount -t nfs server_hostname:/home/brian/share /mnt/my_nfs
    


    參考資料
    鳥哥的 Linux 私房菜 -- NFS 伺服器
    柏青哥的 SuSE Linux -- 架設 NFS 及 NIS Server
    Optimizing NFS Performance

    Excel 凍結窗格

    在Office 2007 Excel中,如要在捲動時一部分的欄位固定不動,可以選擇『檢視』->『凍結窗格』。選項中有『凍結最上列』、『凍結最左欄』、『凍結窗格』

    e.g.
    如果想要freeze上面2列、左邊3欄,則先把游標移到『D3』然後再選『凍結窗格』即可。(按鈕的說明寫的不清楚不楚的)

    Session在IE上沒有作用?

    遇到一個很瞎的問題,用PHP寫一段session的code,偏偏IE上就是起不了作用

    寫入
    <?php
    session_start();
    $_SESSION['login'] = "YES";
    ?>
    

    讀出
    <?php
    session_start();
    echo $_SESSION['login'];
    ?>
    
    結果竟然是空的? 也確定瀏覽器有開啟cookie...

    最後發現竟然是因為hostname含有'_' (底線, underscore)
    e.g. http://test_tool.myhome.com.tw

    改了名字後就沒事了...

    2011/05/15

    SSH免密碼登入

    使用ssh登入遠端server時會要求輸入密碼,有些時候想要略過此步驟(如執行auto script時),建立免密碼登入就可以派上用場。

    1. 在local端建立public key與private key
    $ ssh-keygen -t rsa
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/test/.ssh/id_rsa):     (按enter)
    Enter passphrase (empty for no passphrase):                       (按enter)
    Enter same passphrase again:                                      (按enter)
    Your identification has been saved in /home/test/.ssh/id_rsa.     (按enter)
    Your public key has been saved in /home/test/.ssh/id_rsa.pub.     (按enter)
    The key fingerprint is:
    fe:3f:63:3d:7d:34:06:53:a3:53:22:73:c3:c4:a3:f3 test@test-desktop.local
    andomart image is:
    +--[ RSA 2048]----+
    |           o+  . |
    |          .  +.E+|
    |         .   ..=.|
    |   . o  ..= . oo.|
    |  . + = So.=. o. |
    |   . . o  ...+   |
    |           .     |
    |                 |
    |                 |
    +-----------------+
    
    在~/.ssh目錄下產生了id_rsa與id_rsa.pub

    2. 將public key放到server端
    $ cd ~/.ssh
    $ scp id_rsa.pub username@server_host_name:~/.ssh
    

    3. 進到server端~/.ssh目錄,將public key加到authorized_keys的結尾(authorized_keys可以放置多組),並修改權限為600
    $ cd ~/.ssh
    $ cat id_rsa.pub >> authorized_keys
    $ chmod 600 authorized_keys
    

    下次登入server時就不需要輸入密碼了。

    Rsync - 聰明的資料備份

    之前在linux都用scp來備份檔案到其他地方,scp(secure copy)簡單來說就是透過ssh來copy檔案,不過在複製的過程中沒有辨認檔案新舊或是否修改過,有點浪費頻寬。所以rsync就是更好的選擇摟!

    例如我想把local端的/home/brian/program備份到遠端的/home/brian/backup
    $ rsync -avz --delete -e ssh /home/brian/program brian@10.10.0.1:/home/brian/backup
    
    參數說明:
    --delete: 刪除遠端多餘的檔案,也就是說今天你把local的某個檔案刪除,rsync的時候遠端的檔案也會被刪除。所以在使用這個參數時請確認您的來源是對的,否則會誤刪遠端的檔案
    -z: 傳輸過程壓縮,可以節省頻寬,但相對的壓縮也是需要時間的

    思考:
    這邊所介紹的只是單方向的備份,例如從local備份到remote,如果想從remote復原到local,只要把source跟destination交關即可。基本上remote端的資料不會手動去修改的,如果我想做像dropbox的樣子,local或remote修改時都會自動sync到對方,應該也是可以達到吧?

    2011/05/14

    SSH Reverse Tunnel - SSH反向通道

    有時候會遇到想從一台有public IP的server登入到一台位於NAT底下的server,這時候reverse tunnel就可以派上用場。

    e.g.
    如果公司的server位於防火牆or多層router底下,而回家後又想從家裡連到公司work.

    1. 在公司先建立company到home的reverse tunnel
    $ ssh -R 8080:localhost:22 -o TCPKeepAlive=yes home_user@(家裡電腦的ip)
    額外參數
    -f: run在背景
    -N: 連上後不能執行command
    

    2. 回到家後只要ssh連線localhost:8080就可以導向公司server了
    $ ssh -p 8080 com_user@localhost
    


    e.g.
    上述tunnel的目標位址為localhost:22,也就是公司當下那一台主機的sshd,如果今天是想連到公司其他台電腦的web服務,命令為
    $ ssh -R 8080:192.168.0.8:80 -o TCPKeepAlive=yes home_user@(家裡電腦的ip)
    
    回到家後只要開啓瀏覽器,在網址列打localhost:8080就能連到公司的192.168.0.8:80網頁了

    Macbook 多個作業系統共用磁區

    我的 Macbook pro 安裝了 MacOS X 還有 Windows 7, 另外還建立一個磁區用來交換兩邊的資料, 剛開始格式化成HFS, 不過 Windows 環境下無法做寫入的動作, 也沒找到相關的 freeware.
    之後格式化成NTFS, 在MacOS上可以安裝 NtfsMounter 或 NTFS-3G 來存取 NTFS, 但似乎存取的效能超差, 每秒大概 3MB, 不知道是不是 NTFS-3G 軟體效能的關係? 後來看到一種新格式exFAT(Fat64), 雙邊都不用特別安裝軟體就可以支援.

    後來就把共用磁區format為exFAT, 但進到Win7後發現沒有權限存取這個磁區, 相當奇怪. 想說重新format一次看看, 但Win7的格式化竟然只有NTFS一個選項, 這令我更不得其解, 抓了一套分割軟體format重開機, 更糟糕的事情來了, MacOS 開機選項竟然不見了.

    開始我的修復之路
    1. 開機時按住 shift 可以進入MacOS 安全模式進行磁碟修復, 問題是現在連磁區都讀不到了.
    2. 使用安裝光碟開機, 使用磁碟工具來做修復, 但進去後發現磁碟工具把MacOS磁區當成空白區了.
    3. 尋找多重開機軟體, 想說應該可以從Win7的開機選單導到MacOS, 裝了easyBCD還是沒用.

    最後還是被迫要走到重新安裝的路, 好在重裝MacOS後, Win7有自動加進開機選項 (先前我的Win7是用bootCamp安裝的).

    而那個共享磁區最後還是又回到NTFS格式.

    2011/05/13

    解决 ip_conntrack: table full, dropping packet

    當linux系統連線來源ip過多時, /var/log/message 會出現 ip_conntrack: table full, dropping packet

    解決方式:
    加大tracking table, 下面三種方式都可以
    $ echo 6553600 >/proc/sys/net/ipv4/netfilter/ip_conntrack_max
    
    $ sysctl -w net.ipv4.ip_conntrack_max=6553600
    
    $ vim /etc/sysctl.conf
      修改 netfilter/ip_conntrack_max 這一行
    

    2011/05/10

    PHP 只允許使用者輸入字母或數字

    在實作會員登入機制時,使用者輸入帳號密碼若只想允許“英文字母”or"數字"可以使用preg_match()
    if (preg_match("/^[A-Za-z0-9]*$/", $username) != 0) {
        // valid
    } else {
        // invalid
    }