<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

         摘要: http://autumnrain-zgq.iteye.com/blog/1743279蘋果平臺開發的應用程序,不支持后臺運行程序,所以蘋果有一個推送服務在軟件的一些信息推送給用戶。 JAVA中,有一個開源軟件,JavaPNS實現了Java平臺中連接蘋果服務器與推送消息的服務。但是在使用的過程中,有兩點需要使用者注意一下,希望后續使用的同志們能避免我走過的覆轍。 1、一是向蘋果...  閱讀全文
    posted @ 2014-07-22 18:44 小馬歌 閱讀(592) | 評論 (0)編輯 收藏
     
    http://blog.csdn.net/totogo2010/article/details/8048652

    iOS的應用程序的生命周期,還有程序是運行在前臺還是后臺,應用程序各個狀態的變換,這些對于開發者來說都是很重要的。 iOS系統的資源是有限的,應用程序在前臺和在后臺的狀態是不一樣的。在后臺時,程序會受到系統的很多限制,這樣可以提高電池的使用和用戶體驗。

    //開發app,我們要遵循apple公司的一些指導原則,原則如下:

    1、應用程序的狀態

    狀態如下:

    Not running  未運行  程序沒啟動

    Inactive          未激活        程序在前臺運行,不過沒有接收到事件。在沒有事件處理情況下程序通常停留在這個狀態

    Active             激活           程序在前臺運行而且接收到了事件。這也是前臺的一個正常的模式

    Backgroud     后臺           程序在后臺而且能執行代碼,大多數程序進入這個狀態后會在在這個狀態上停留一會。時間到之后會進入掛起狀態(Suspended)。有的程序經過特殊的請求后可以長期處于Backgroud狀態

    Suspended    掛起           程序在后臺不能執行代碼。系統會自動把程序變成這個狀態而且不會發出通知。當掛起時,程序還是停留在內存中的,當系統內存低時,系統就把掛起的程序清除掉,為前臺程序提供更多的內存。

    下圖是程序狀態變化圖:


    各個程序運行狀態時代理的回調:

    - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
          告訴代理進程啟動但還沒進入狀態保存
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
         告訴代理啟動基本完成程序準備開始運行
    - (void)applicationWillResignActive:(UIApplication *)application
        當應用程序將要入非活動狀態執行,在此期間,應用程序不接收消息或事件,比如來電話了
    - (void)applicationDidBecomeActive:(UIApplication *)application 
         當應用程序入活動狀態執行,這個剛好跟上面那個方法相反
    - (void)applicationDidEnterBackground:(UIApplication *)application
        當程序被推送到后臺的時候調用。所以要設置后臺繼續運行,則在這個函數里面設置即可
    - (void)applicationWillEnterForeground:(UIApplication *)application
    當程序從后臺將要重新回到前臺時候調用,這個剛好跟上面的那個方法相反。
    - (void)applicationWillTerminate:(UIApplication *)application
    當程序將要退出是被調用,通常是用來保存數據和一些退出前的清理工作。這個需要要設置UIApplicationExitsOnSuspend的鍵值。
    - (void)applicationDidFinishLaunching:(UIApplication*)application
    當程序載入后執行

    在上面8個方法對應的方法中鍵入NSLog打印。

    現在啟動程序看看執行的順序:

    啟動程序
    lifeCycle[40428:11303] willFinishLaunchingWithOptions
    lifeCycle[40428:11303] didFinishLaunchingWithOptions
    lifeCycle[40428:11303] applicationDidBecomeActive

    按下home鍵

    lifeCycle[40428:11303] applicationWillResignActive
    lifeCycle[40428:11303] applicationDidEnterBackground

    雙擊home鍵,再打開程序

    lifeCycle[40428:11303] applicationWillEnterForeground
    lifeCycle[40428:11303] applicationDidBecomeActive

    2、應用程序的生命周期

    2.1、加載應用程序進入前臺


    2.2、加載應用程序進入后臺



    2.3、關于main函數

    main函數是程序啟動的入口,在iOS app中,main函數的功能被最小化,它的主要工作都交給了UIKit framework

    1. #import <UIKit/UIKit.h>  
    2.    
    3. int main(int argc, char *argv[])  
    4. {  
    5.     @autoreleasepool {  
    6.         return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));  
    7.     }  
    8. }  

    UIApplicationMain函數有四個參數,你不需要改變這些參數值,不過我們也需要理解這些參數和程序是如何開始的

    argc 和argv參數包含了系統帶過來的啟動時間。  第三個參數確定了主要應用程序類的名稱,這個參數指定為nil,這樣UIKit就會使用默認的程序類UIApplication。第四個參數是程序自定義的代理類名,這個類負責系統和代碼之間的交互。它一般在Xcode新建項目時會自動生成。

    另外 UIApplicationMain函數加載了程序主界面的文件。雖然這個函數加載了界面文件,但是沒有放到應用程序的windows上,你需要在Delegate的 application:willFinishLaunchingWithOptions方法中加載它。

    一個應用程序可以有一個主的storyboard文件或者有一個主的nib文件,但不能同時有兩個存在。

    如果程序在啟動時沒有自動加載主要的故事版或nib文件,你可以在application:willFinishLaunchingWithOptions方法里準備windows的展示。

    3、響應中斷

    3.1 當一個基于警告式的中斷發生時,比如有電話打進來了,這是程序會臨時進入inactive狀態,這用戶可以選擇如何處理這個中斷,流程如下圖:


    在iOS5,通知不會把程序變成為激活狀態,通知會顯示在狀態欄上,如果你;拉下狀態欄,程序會變成inactive,把狀態欄放回去,程序變回active。

    按鎖屏鍵也是另外一種程序的中斷,當你按下鎖屏鍵,系統屏蔽了所有觸摸事件,把app放到了后臺,這時app狀態是 inactive,并進入后臺。

    3.2 當有這些中斷時,我們的app該怎么辦呢?我們應該在applicationWillResignActive:方法中:

    • 停止timer 和其他周期性的任務
    • 停止任何正在運行的請求
    • 暫停視頻的播放
    • 如果是游戲那就暫停它
    • 減少OpenGL ES的幀率
    • 掛起任何分發的隊列和不重要的操作隊列(你可以繼續處理網絡請求或其他時間敏感的后臺任務)。
    當程序回到active狀態 ,   applicationDidBecomeActive:   方法應該上面提到的任務重新開始,比如重新開始timer, 繼續分發隊列,提高OpenGL ES的幀率。不過游戲要回到暫停狀態,不能自動開始。

    4、轉到后臺運行

    4.1 如圖所示:

    PS:只有在IOS4以上系統或者支持多任務的設備才能后臺運行。不然會直接結束狀態。

    4.2 當應用程序進入后臺時,我們應該做寫什么呢?

    • 保存用戶數據或狀態信息,所有沒寫到磁盤的文件或信息,在進入后臺時,最后都寫到磁盤去,因為程序可能在后臺被殺死,
    • 釋放盡可能釋放的內存
    applicationDidEnterBackgound: 方法有大概5秒的時間讓你完成這些任務。如果超過時間還有未完成的任務,你的程序就會被終止而且從內存中清除。如果還需要長時間的運行任務,可以調用  beginBackgroundTaskWithExpirationHandler       方法去請求后臺運行時間和啟動線程來運行長時間運行的任務。
    4.3 應用程序在后臺時的內存使用
    在后臺時,每個應用程序都應該釋放最大的內存。系統努力的保持更多的應用程序在后臺同時 運行。不過當內存不足時,會終止一些掛起的程序來回收內存,那些內存最大的程序首先被終止。
    事實上,應用程序應該的對象如果不再使用了,那就應該盡快的去掉強引用,這樣編譯器可以回收這些內存。如果你想緩存一些對象提升程序的性能,你可以在進入后臺時,把這些對象去掉強引用。
    下面這樣的對象應該盡快的去掉強引用:
    • 圖片對象
    • 你可以重新加載的 大的視頻或數據文件
    • 任何沒用而且可以輕易創建的對象
    在后臺時,為了減少程序占用的內存,系統會自動在回收一些系統幫助你開辟的內存。比如:
    系統回收Core Animation的后備存儲。
    去掉任何系統引用的緩存圖片
    去掉系統管理數據緩存強引用

    5 、返回前臺運行

    流程如圖所示:
    當app處于掛起狀態時,它是不能執行任何代碼的。因此它不能處理在掛起期間發過來的通知,比如方向改變,時間改變,設置的改變還有其他影響程序展現的或狀態的通知。在程序返回后臺或前臺是,程序都要正確的處理這些通知。

    6、程序的終止

    程序只要符合以下情況之一,只要進入后臺或掛起狀態就會終止:
    iOS4.0以前的系統
    app是基于iOS4.0之前系統開發的。
    設備不支持多任務
    在Info.plist文件中,程序包含了 UIApplicationExitsOnSuspend  鍵。
    app如果終止了  ,系統會調用app的代理的方法 applicationWillTerminate:   這樣可以讓你可以做一些清理工作。你可以保存一些數據或app的狀態。這個方法也有5秒鐘的限制。超時后方法會返回程序從內存中清除。
    注意:用戶可以手工關閉應用程序。

    7、 The Main Run Loop  主運行循環

    Main Run Loop負責處理用戶相關的事件。UIApplication對象在程序啟動時啟動main run Loop,它處理事件和更新視圖的界面。看Main Run Loop就知道,它是運行在程序的主線程上的。這樣保證了接收到用戶相關操作的事件是按順序處理的。
    Main Run Loop  處理事件的架構圖:
    用戶操作設備,相關的操作事件被系統生成并通過UIKit的指定端口分發。事件在內部排成隊列,一個個的分發到Main run loop 去做處理。UIApplication對象是第一個接收到時間的對象,它決定事件如何被處理。觸摸事件分發到主窗口,窗口再分發到對應出發觸摸事件的View。其他的事件通過其他途徑分發給其他對象變量做處理。
    大部分的事件可以在你的應用里分發,類似于觸摸事件,遠程操控事件(線控耳機等)都是由app的 responder objects 對象處理的。Responder objects 在你的app里到處都是,比如:UIApplication 對象。view對象,view controller 對象,都是resopnder objects。大部分事件的目標都指定了resopnder object,不過事件也可以傳遞給其他對象。比如,如果view對象不處理事件,可以傳給父類view或者view controller。
    參考:https://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW20

    容芳志 (http://blog.csdn.net/totogo2010)

    本文遵循“署名-非商業用途-保持一致”創作公用協議


    posted @ 2014-07-22 18:01 小馬歌 閱讀(217) | 評論 (0)編輯 收藏
     
    1、概述

    Aho-Corasick自動機算法(簡稱AC自動機)1975年產生于貝爾實驗室。該算法應用有限自動機巧妙地將字符比較轉化為了狀態轉移。此算法有兩個特點,一個是掃描文本時完全不需要回溯,另一個是時間復雜度為O(n),時間復雜度與關鍵字的數目和長度無關。

    好了,我們先看下最原始的多模式匹配算法:

    主串T,n=strlen(T)。

    模式串Pi mi = strlen(pi)

     

    1. for(i=0;i<n-MIN(m);++i)  
    2.     for(j=0;j<k;++j)  
    3.         if(n-mk<=n-i &&memcmp(T[i],Pk,mk)==0)  
    4.            printf(“match/n”);  
     

     

     

     

    是O(mn)的時間復雜度。

    上面的算法很笨吧,下面看看聰明的AC算法是個啥意思。

    2、 AC算法思想

    AC算法思想:用多模式串建立一個確定性的樹形有限狀態機,以主串作為該有限狀態機的輸入,使狀態機進行狀態的轉換,當到達某些特定的狀態時,說明發生模式匹配。

    下圖是多模式he/ she/ his /hers構成的一個確定性有限狀態機,做幾點說明:

    wps_clip_image-531

    1、 該狀態機優先按照實線標注的狀態轉換路徑進行轉換,當所有實線標注的狀態轉換路徑條件不能滿足時,按照虛線的狀態轉換路徑進行狀態轉換。如:狀態0時,當輸入h,則轉換到狀態1;輸入s,則轉換到狀態3;否則轉換到狀態0。

    2、 匹配過程如下:從狀態0開始進行狀態轉換,主串作為輸入。如主串為:ushers,狀態轉換的過程是這樣的:

    wps_clip_image-720

    3、  當狀態轉移到2,5,7,9等紅色狀態點時,說明發生了模式匹配。

    如主串為:ushers,則在狀態5、2、9等狀態時發生模式匹配,匹配的模 式串有she、he、hers。

     

    定義:

    在預處理階段,AC自動機算法建立了三個函數,轉向函數goto,失效函數failure和輸出函數output,由此構造了一個樹型有限自動機。

     

    轉向函數,指的是一種狀態之間的轉向關系。g(pre, x)=next:狀態pre在輸入一個字符x后轉換為狀態next(上圖中的實線部分)。如果在模式串中不存在這樣的轉換,則next=failstate。

     

    失效函數,指的也是狀態和狀態之間一種轉向關系。f(per)=next:是在比較失配的情況下使用的轉換關系。在構造轉向函數時,把不存在的轉換用failstate表示,但是failstate不是一個具體的狀態,狀態機轉換轉換到failstate狀態的時候就不知道該往哪轉了。所以就要在狀態機中找到一個有意義的狀態代替failstate,當出現failstate狀態時,自動切換到那個狀態。

    這個狀態節點應該具有這樣的特征:從這個狀態節點向上直到樹根節點(狀態0)所經歷的輸入字符,和從產生failstate狀態的那個狀態節點向上所經歷的輸入字符串完全相同。而且這個狀態節點,是所有具備這些條件的節點中深度最大的那個節點。如果不存在滿足條件的狀態節點,則失效函數為0。

    累死了。舉例子說吧,對狀態9輸入任何一個字符都會產生failstate狀態,需要失效函數。狀態3向上到狀態0經過的輸入字符串為s;而由狀態9向上的輸入字符串為sreh。字符串s相同,并且狀態3是滿足此條件的唯一節點,則

    f(9)=3。

    說來說去,失效函數就是要干這么件事兒:

    wps_clip_image-1497

    意思就是說,在比較模式串1發生失配時,找一個模式串2,使得P2[0...j-1] = P1[i-j+1...i]。然后繼續比較模式串2。看上面那個圖,想起點兒什么東西沒有?對了,是KMP算法。有人說AC算法就是KMP算法在多模式匹配情況下的擴展。


    輸出函數
    ,指的是狀態和模式串之間的一種關系。output(i)={P},表示當狀
    態機到達狀態i時,模式串集合{P}中的所有模式串可能已經完成匹配。

    例:

    模式串為:he/ she/ hers/ his 時,如上圖所示:

    轉向函數:

    wps_clip_image-1758

    失效函數:

    wps_clip_image-1780

    輸出函數:

    wps_clip_image-1801

    3、 AC代碼分析

    下面的代碼參考snort入侵檢測系統開源軟件的acsmx.c文件。

    3.1數據結構分析

    所有狀態都被存儲在一個ACSM_STATETABLE類型的數組中。

    typedef struct  {   

        int      NextState[ ALPHABET_SIZE ]; 

        int      FailState;  

        ACSM_PATTERN *MatchList;

    }ACSM_STATETABLE;

    NextState對應轉向函數;FailState對應失效函數;MatchList對應輸出函數。

     

    3.2代碼分析

    代碼流程如下圖:

    wps_clip_image-2124

     

    https://github.com/robert-bor/aho-corasick
    posted @ 2014-07-16 09:24 小馬歌 閱讀(250) | 評論 (0)編輯 收藏
     
         摘要: 一 原理區別     一般在瀏覽器中輸入網址訪問資源都是通過GET方式;在FORM提交中,可以通過Method指定提交方式為GET或者POST,默認為GET提交 Http定義了與服務器交互的不同方法,最基本的方法有4種,分別是GET,POST,PUT,DELETE URL全稱是資源描述符,我們可以這樣認為:一個URL地址,它用于描述一個網...  閱讀全文
    posted @ 2014-07-15 17:22 小馬歌 閱讀(388) | 評論 (0)編輯 收藏
     

    先描述一下問題,多個服務器實現的負載均衡,每個服務器存儲在自己的硬盤里。但是現在需要對日志做統一的分析,在多個服務器上統計就麻煩了。思路是把日志統一到一臺日志服務器上,再統一做統計分析。怎么統一到一臺服務器上,說實話沒有特別好的思路,最后嘗試了log4j的SocketAppender。查了不少網絡資源,都說的有些不明了,還是得親自嘗試之后才見分曉。

    1、客戶端的配置

    客戶端的配置比較簡單,只需要告訴log4j需要監聽哪個遠程服務器的哪個端口即可。直接在log4j.properties里直接配置就好。

    1. <span style="font-size:12px;">log4j.appender.logs=org.apache.log4j.DailyRollingFileAppender  
    2. log4j.appender.logs.File = /data/logs/request/logs.log  
    3. log4j.appender.logs.layout = org.apache.log4j.PatternLayout  
    4. log4j.appender.logs.layout.ConversionPattern=%d [%t] - %m%n  
    5. log4j.appender.logs.DatePattern='.'yyyy-MM-dd'.log'  
    6.   
    7. log4j.appender.socket=org.apache.log4j.net.SocketAppender  
    8. log4j.appender.socket.RemoteHost=172.16.2.152  
    9. log4j.appender.socket.Port=4560  
    10. log4j.appender.socket.LocationInfo=true  
    11. #下面這兩句感覺沒用  
    12. log4j.appender.socket.layout=org.apache.log4j.PatternLayout  
    13. log4j.appender.socket.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%t%m%n  
    14.   
    15. #將日志寫入本地和遠程日志服務器  
    16. log4j.logger.com.test.core.filter =DEBUG,socket,logs</span>  
     

    2、日志服務器的配置

    日志服務器需要單獨啟動一個java進程,接收客戶端給自己發送的socket請求。Log4j提供了org.apache.log4j.net.SocketServer類,直接運行其main函數就行了(當然也可以自己寫啦)。

    java -cp /log4jsocket/serverConfig/log4j-1.2.16.jarorg.apache.log4j.net.SocketServer 4560 /log4jsocket/log4jserver.properties /log4jsocket/clientConfig

    /log4jsocket/serverConfig/log4j-1.2.16.jar是log4j jar包存放的位置,org.apache.log4j.net.SocketServer需要三個參數:

    1)4560 是監聽的端口號

    2)/log4jsocket/log4jserver.properties 是記錄日志服務器的日志的配置文件

    3)/log4jsocket/clientConfig 是客戶端配置文件所在的目錄(注意是目錄)。

    著重說一下org.apache.log4j.net.SocketServer的第三個參數,這個文件夾下配置的是各個客戶端的日志的配置。配置文件以.lcf結尾,文件名可以用客戶端的IP命名,log4j會自己找發送請求的客戶端IP對應的那個配置文件,如172.16.2.46服務器發送的socket請求會尋找172.16.2.46.lcf配置文件,并根據配置將日志寫入對應的文件。

    1. <span style="font-size:12px;">#注意logger后面的值要與client的值相同  
    2. log4j.logger.com.test.core.filter=DEBUG,localLogs  
    3.    
    4. log4j.appender.localLogs=org.apache.log4j.DailyRollingFileAppender  
    5. log4j.appender.localLogs.File=/data/logs/request/172.16.2.46/logs.log  
    6. log4j.appender.localLogs.layout=org.apache.log4j.PatternLayout  
    7. log4j.appender.localLogs.layout.ConversionPattern=%d [%t] - %m%n  
    8. log4j.appender.localLogs.DatePattern='.'yyyy-MM-dd'.log'  
    9. </span>  


    這樣做的好處是可以根據不同客戶端,將日志寫入不同的文件夾下的。

    其實,配置過程就這么簡單,但是當你這么做之后,你會發現運行org.apache.log4j.net.SocketServer后,客戶端向日志服務器發送請求時,會報找不到.lcf文件的錯誤,得不到想要的結果。原因出在org.apache.log4j.net.SocketServer代碼中的一個小bug。 

    1. <span style="font-size:12px;">LoggerRepository configureHierarchy(InetAddress inetAddress)  
    2.   {  
    3.     cat.info("Locating configuration file for " + inetAddress);  
    4.   
    5.     String s = inetAddress.toString();  
    6.     int i = s.indexOf("/");  
    7.     if (i == -1) {  
    8.       cat.warn("Could not parse the inetAddress [" + inetAddress + "]. Using default hierarchy.");  
    9.   
    10.       return genericHierarchy();  
    11.     }  
    12.     String key = s.substring(0,i);  
    13.   
    14.     File configFile = new File(this.dir, key + CONFIG_FILE_EXT);  
    15.     if (configFile.exists()) {  
    16.       Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));  
    17.       this.hierarchyMap.put(inetAddress, h);  
    18.   
    19.       new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h);  
    20.   
    21.       return h;  
    22.     }  
    23.     cat.warn("Could not find config file [" + configFile + "].");  
    24.     return genericHierarchy();  
    25.   }</span>  

    String key = s.substring(0, i);換成String key = s.substring(i+1);就好了。這段代碼是解析IP地址,然后尋找對應IP命名的.lcf配置文件;如果找不到,則解析默認的generic.lcf。由于截取的錯誤,導致找不到172.16.2.46.lcf,文件夾下又沒有generic.lcf,所以會拋異常。

    org.apache.log4j.net.SocketServer代碼中的另外一個bug是,只能接收來自一臺客戶端的日志請求,一旦客戶端停止運行,SocketServer也將關閉。查看代碼:

    1. public static void main(String[] argv)  
    2.   {     
    3.       if (argv.length == 3)  
    4.       init(argv[0], argv[1], argv[2]);  
    5.     else  
    6.       usage("Wrong number of arguments.");  
    7.     try  
    8.     {  
    9.         cat.info("Listening on port " + port);  
    10.         ServerSocket serverSocket = new ServerSocket(port);  
    11.         cat.info("Waiting to accept a new client.");  
    12.     Socket socket = serverSocket.accept();  
    13.     InetAddress inetAddress = socket.getInetAddress();  
    14.     cat.info("Connected to client at " + inetAddress);  
    15.       
    16.     LoggerRepository h = (LoggerRepository)server.hierarchyMap.get(inetAddress);  
    17.     if (h == null) {  
    18.             h = server.configureHierarchy(inetAddress);  
    19.     }  
    20.       
    21.     cat.info("Starting new socket node.");  
    22.     new Thread(new SocketNode(socket, h)).start();  
    23.       }  
    24.     catch (Exception e)  
    25.     {  
    26.       e.printStackTrace();  
    27.     }  
    28.   }  

    問題出在只建立了一個socket連接就不在accept了,加上while循環問題就解決了。

    1. ServerSocket serverSocket = new ServerSocket(port);  
    2. while(true){  
    3.  cat.info("Waiting to accept a new client.");  
    4.  Socket socket = serverSocket.accept();  
    5.  InetAddress inetAddress = socket.getInetAddress();  
    6.  cat.info("Connected to client at " + inetAddress);  
    7.   
    8.  LoggerRepository h = (LoggerRepository)server.hierarchyMap.get(inetAddress);  
    9.  if (h == null) {  
    10.    h = server.configureHierarchy(inetAddress);  
    11.  }  
    12.   
    13.  cat.info("Starting new socket node.");  
    14.  new Thread(new SocketNode(socket, h)).start();  
    15. }  



     

     

    好了。Log4j的配置到此結束。

    最后一個問題,日志服務器是linux,需要有一個統一的start、shutdown命令來啟動和關閉org.apache.log4j.net.SocketServer。那就需要些shell命令了,下面這段代碼參考了http://www.cnblogs.com/baibaluo/archive/2011/08/31/2160934.html

    catalina.sh

    1. <span style="font-size:12px;">#!/bin/bash  
    2. #端口  
    3. LISTEN_PORT=4560  
    4. #服務端log4j配置文件  
    5. SERVER_CONFIG=/log4jsocket/server.properties  
    6. #客戶端的配置  
    7. CLIENT_CONFIG_DIR=/log4jsocket/clientConfig  
    8.   
    9. #Java程序所在的目錄(classes的上一級目錄)  
    10. APP_HOME=/opt/log4jsocket/serverConfig   
    11. #需要啟動的Java主程序(main方法類)  
    12. APP_MAINCLASS=org.apache.log4j.net.SocketServer  
    13.    
    14. #拼湊完整的classpath參數,包括指定lib目錄下所有的jar  
    15. CLASSPATH=$APP_HOME  
    16. for i in "$APP_HOME"/*.jar; do     
    17.     CLASSPATH="$CLASSPATH":"$i"  
    18. done  
    19.   
    20. #JDK所在路徑  
    21. JAVA_HOME="/opt/jdk1.6.0_30"   
    22. #執行程序啟動所使用的系統用戶,考慮到安全,推薦不使用root帳號  
    23. RUNNING_USER=root  
    24.    
    25. #java虛擬機啟動參數  
    26. JAVA_OPTS="-ms512m -mx512m -Xmn256m -Djava.awt.headless=true -XX:MaxPermSize=128m"   
    27.   
    28. #初始化psid變量(全局)  
    29. psid=0  
    30.    
    31. checkpid() {  
    32.    javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAINCLASS`  
    33.    
    34.    if [ -n "$javaps" ]; then  
    35.       psid=`echo $javaps | awk '{print $1}'`  
    36.    else  
    37.       psid=0  
    38.    fi  
    39. }  
    40.   
    41. start() {  
    42.    checkpid  
    43.    
    44.    if [ $psid -ne 0 ]; then  
    45.       echo "================================"  
    46.       echo "warn: $APP_MAINCLASS already started! (pid=$psid)"  
    47.       echo "================================"  
    48.    else  
    49.       echo -n "Starting $APP_MAINCLASS ..."  
    50.       JAVA_CMD="nohup $JAVA_HOME/bin/java -classpath $CLASSPATH $APP_MAINCLASS $LISTEN_PORT $SERVER_CONFIG $CLIENT_CONFIG_DIR >/dev/null 2>&1 &"  
    51.       su - $RUNNING_USER -c "$JAVA_CMD"  
    52.       checkpid  
    53.       if [ $psid -ne 0 ]; then  
    54.          echo "(pid=$psid) [OK]"  
    55.       else  
    56.          echo "[Failed]"  
    57.       fi  
    58.    fi  
    59. }  
    60.   
    61. stop() {  
    62.    checkpid  
    63.    
    64.    if [ $psid -ne 0 ]; then  
    65.       echo -n "Stopping $APP_MAINCLASS ...(pid=$psid) "  
    66.       su - $RUNNING_USER -c "kill -9 $psid"  
    67.       if [ $? -eq 0 ]; then  
    68.          echo "[OK]"  
    69.       else  
    70.          echo "[Failed]"  
    71.       fi  
    72.    
    73.       checkpid  
    74.       if [ $psid -ne 0 ]; then  
    75.          stop  
    76.       fi  
    77.    else  
    78.       echo "================================"  
    79.       echo "warn: $APP_MAINCLASS is not running"  
    80.       echo "================================"  
    81.    fi  
    82. }  
    83.   
    84. status() {  
    85.    checkpid  
    86.    
    87.    if [ $psid -ne 0 ];  then  
    88.       echo "$APP_MAINCLASS is running! (pid=$psid)"  
    89.    else  
    90.       echo "$APP_MAINCLASS is not running"  
    91.    fi  
    92. }  
    93. info() {  
    94.    echo "System Information:"  
    95.    echo "****************************"  
    96.    echo `head -n 1 /etc/issue`  
    97.    echo `uname -a`  
    98.    echo  
    99.    echo "JAVA_HOME=$JAVA_HOME"  
    100.    echo `$JAVA_HOME/bin/java -version`  
    101.    echo  
    102.    echo "APP_HOME=$APP_HOME"  
    103.    echo "APP_MAINCLASS=$APP_MAINCLASS"  
    104.    echo "****************************"  
    105. }  
    106. case "$1" in  
    107.   
    108.    'start')  
    109.       start  
    110.       ;;  
    111.    'stop')  
    112.      stop  
    113.      ;;  
    114.    'restart')  
    115.      stop  
    116.      start  
    117.      ;;  
    118.    'status')  
    119.      status  
    120.      ;;  
    121.    'info')  
    122.      info  
    123.      ;;  
    124.   *)  
    125.      echo "Usage: $0 {start|stop|restart|status|info}"   
    126.      exit 0   
    127. esac  
    128. </span>  
    startup.sh

    1. <span style="font-size:12px;">#!/bin/sh  
    2. EXECUTABLE=/log4jsocket/catalina.sh  
    3. exec "$EXECUTABLE" start "$@"</span>  
    shutdown.sh

    1. <span style="font-size:12px;">EXECUTABLE=/log4jsocket/catalina.sh  
    2. exec "$EXECUTABLE" stop "$@"</span>  
    posted @ 2014-07-09 15:31 小馬歌 閱讀(2813) | 評論 (0)編輯 收藏
     

    遇到如題的這么一個場景:需要在MySQL的一張innodb引擎的表(tableA)上添加一個唯一索引(idx_col1_u)。但是表中已經有大量重復數據,對于每個key(col1),有的重復2行,有的重復N行。

    此時,做數據的手工清理,或者SQL處理無疑是非常耗時的。

     

    1. Alter ignore table come to help

    印象中MySQL有一個獨有的 alter ignore add unique index的語法。

    語法如下:

    ALTER [ONLINE | OFFLINE] [IGNORE] TABLE tbl_name

     

    行為類似于insert ignore,即遇到沖突的unique數據則直接拋棄而不報錯。對于加唯一索引的情況來說就是建一張空表,然后加上唯一索引,將老數據用insert ignore語法插入到新表中,遇到沖突則拋棄數據。

    文檔中對于alter ignore的注釋:詳見:http://dev.mysql.com/doc/refman/5.1/en/alter-table.html

    IGNORE is a MySQL extension to standard SQL. It controls how ALTER TABLE works if there are duplicates on unique keys in the new table or if warnings occur when strict mode is enabled. If IGNORE is not specified, the copy is aborted and rolled back if duplicate-key errors occur. If IGNORE is specified, only the first row is used of rows with duplicates on a unique key. The other conflicting rows are deleted. Incorrect values are truncated to the closest matching acceptable value.

     

    2.  #1062 - Duplicate entry 

     然而在執行了 alter ignore table tableA add unique index idx_col1_u (col1) 后,還是報了以下錯誤:

     #1062 - Duplicate entry '111' for key 'col1'.

    不是會自動丟棄重復數據么?世界觀被顛覆了。查了下資料原來是alter ignore的語法不支持innodb。

    得知alter ignore的實現完全取決于存儲引擎的內部實現,而不是server端強制的,具體描述如下:

    For ALTER TABLE with the IGNORE keyword, IGNORE is now part of the information provided to the storage engine. It is up to the storage engine whether to use this when choosing between the in-place or copy algorithm for altering the table. For InnoDB index operations, IGNORE  is not used if the index is unique, so the copy algorithm is used

     詳見:http://bugs.mysql.com/bug.php?id=40344

     

    3. 解決方案

    當然解決這個問題的tricky的方法還是有的,也比較直白粗暴。具體如下:

    ALTER TABLE tableA ENGINE MyISAM;
    ALTER IGNORE TABLE tableA ADD UNIQUE INDEX idx_col1_u (col1)
    ALTER TABLE table ENGINE InnoDB;

     

    updated in 2013-09-26:

    @jyzhou 分享提到,可以不用改成MyISAM,而直接使用set old_alter_table = 1; 的方法。具體做法如下:

    set old_alter_table = 1;

    ALTER IGNORE TABLE tableA ADD UNIQUE INDEX idx_col1_u (col1) 

    具體原理:http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_old_alter_table

    posted @ 2014-07-07 13:52 小馬歌 閱讀(265) | 評論 (0)編輯 收藏
     
    url:http://www.myexception.cn/operating-system/1525825.html
    使用IOS企業版證書發布應用

            蘋果的企業開發證書,可以不經app store,直接發布到自己的網站上。其他人可以直接下載安裝。但前提要用蘋果自帶的瀏覽器(safari)才能下載,其他瀏覽器不能識別該協議。

            一、制作證書

           打開Keychain Access工具,把Keychain的配置改為如下圖:


    并從菜單中選擇Keycahin Access->Certificate Assistant->Request a Certificate From a Certificate Authority...如下圖所示:


     填寫必要的信息,生成證書保存在桌面或其他地方,等會要用到:




     
     二、生成并安裝證書

            用企業版的apple id登陸蘋果開發者中心: https://developer.apple.com, 登陸后選擇對應的選項,上傳之前生成的證書文件,如下圖:


     

     生成證書文件后,下載下來,雙擊打開即安裝。

    三、增加APP ID

    四、生成Profiles文件

    在開發中心點擊Provisioning Profiles中的Distribution, 點擊“+”,增加Profiles。APP ID選擇剛才增加的。如圖
     

    添加好之后,就可以下載下來,把它拖到Xcode中?;蚴峭系絆rganizer中。如圖:



     

    五、發布應用

    在XCode的Targets中選擇簽名:



     在XCode的菜單Product->Archive中選擇打包。打包時要把iPad或是iphone接到電腦上,并選擇目標是iPad或是iphone才能成功。



     

     



    Required的信息填寫即可,save,則會同時生成ipa和plist 文件,如下

     

    apple文檔地址:http://developer.apple.com/library/ios/#featuredarticles/FA_Wireless_Enterprise_App_Distribution/Introduction/Introduction.html


    以無線方式安裝應用程序

    iOS 支持以無線方式安裝企業級應用程序,這可讓您在不使用 iTunes 的情況下將內部軟件分發給用戶。

    要求

    • 已鑒定的用戶可訪問的安全 Web 服務器

    • .ipa 格式的 iOS 應用程序,經構建用于發布/生產(使用了企業級預置描述文件)

    • 本文稿中稍后描述的 XML 清單文件

    • 可讓設備訪問 Apple iTunes 服務器的網絡配置

    安裝應用程序很簡單。用戶可以將清單文件從您的網站上下載到他們的 iOS 設備上。該清單文件會指示設備下載和安裝該清單文件中所引用的應用程序。

    您可以分發 URL 以便通過短信或電子郵件來下載清單文件,也可以將它嵌入您所創建的其他企業級應用程序中。

    由您設計和主管用于分發應用程序的網站。確定用戶已被鑒定(可能是使用基本鑒定或基于目錄的鑒定),并確定網站可通過內聯網或互聯網進行訪問。您可以將應用程序和清單文件放入隱藏的目錄中,或任何可使用 HTTP 或 HTTPS 來讀取的其他位置中。

    準備企業級應用程序進行無線分發

    若要準備企業級應用程序進行無線分發,您應該構建歸檔的版本(.ipa 文件),以及構建清單文件以啟用應用程序的無線分發和安裝。

    使用 Xcode 來創建應用程序歸檔。使用您的分發證書給應用程序簽名并在歸檔中包括您的企業級開發預置描述文件。有關清單文件的信息,請參閱以下內容。有關構建和歸檔應用程序的更多信息,請訪問 iOS Dev Center(iOS 開發中心)或參閱《Xcode User Guide》(Xcode 使用手冊),可通過 Xcode 中的“Help”(幫助)菜單來訪問該使用手冊。

    關于無線清單文件

    清單文件是 XML plist 格式的。iOS 設備使用它在 Web 服務器上查找應用程序,以及從 Web 服務器上下載和安裝應用程序。清單文件是由 Xcode 創建的,使用您在共享歸檔的應用程序以進行企業級分發時所提供的信息。請參閱上一節準備應用程序進行分發。

    以下欄是必填的:

    項目

    描述

    URL

    應用程序 (.ipa) 文件的完整合格的 HTTP 或 HTTPS URL。

    display-image

    下載和安裝過程中顯示的 57 x 57 像素 PNG 圖像。指定圖像的完整合格的 URL。

    full-size-image

    用來在 iTunes 中表示應用程序的 512 x 512 像素 PNG 圖像。

    bundle-identifier

    您應用程序的包標識符,與 Xcode 項目中指定的完全一樣。

    bundle-version

    您應用程序的包版本,在 Xcode 項目中指定。

    title

    下載和安裝過程中顯示的應用程序的名稱。

    僅對于“報刊雜志”應用程序,需要填寫以下欄位:

    項目

    描述

    newsstand-image

    完整大小的 PNG 圖像,用于顯示在“報刊雜志”書架上。

    UINewsstandBindingEdge

    UINewsstandBindingType

    這些鍵必須與“報刊雜志”應用程序中的 info.plist 中的鍵相符。

    UINewsstandApp

    指示該應用程序是“報刊雜志”應用程序。

    您可以使用的一些可選鍵如示例清單文件所述。例如,如果應用程序文件太大并且您想要在執行錯誤檢驗(TCP 通信通常會執行該操作)的基礎上確保下載的完整性,則可以使用 MD5 鍵。

    您可以使用單個清單文件安裝多個應用程序,方法是指定 items 數組的附加成員。

    本文稿末尾列出了示例清單文件。

    構建網站

    將這些項目上傳到您網站上已鑒定的用戶可以訪問的區域:

    • 應用程序 (.ipa) 文件

    • 清單 (.plist) 文件

    您的網站設計可以像用來鏈接到清單文件的單個頁面那么簡單。當用戶輕按 Web 鏈接時,清單文件會被下載,并觸發它所描述的應用程序的下載和安裝。

    以下是一個示例鏈接:

    <a href="itms-services://?action=download-manifest&url=http://example.com/
manifest.plist">Install App</a>

    請勿添加歸檔的應用程序 (.ipa) 的 Web 鏈接。載入清單文件時,設備會下載該 .ipa。雖然 URL 的協議部分是 itms-services,但 iTunes Store 并不參與此過程。

    設定服務器 MIME 類型

    您可能需要配置您的 Web 服務器以便正確地傳輸清單文件和應用程序文件。

    對于 OS X Server,將以下 MIME 類型添加到 Web 服務的“MIME Types”(MIME 類型)設置中:

    application/octet-stream ipa

    text/xml plist

    對于 IIS,使用 IIS Manager 在服務器的“屬性”頁面中添加 MIME 類型:

    .ipa application/octet-stream

    .plist text/xml

    openssl使用的是macos系統自帶的版本,關鍵點是不同直接使用ios設備打開https的鏈接,需要將證書發到系統的mail里,安裝到設備,
    如果命令執行不成功,用sudo執行。

    1.生成服務器的私鑰
    openssl genrsa -out server.key 1024

    2.生成簽署申請(注意除Common Name以外可以為空,Common Name必須為服務器的ip或域名)
    openssl req -new -key server.key -out server.csr

    3.生成CA私鑰
    openssl genrsa  -out ca.key 1024 

    4.利用CA的私鑰產生CA的自簽署證書
    openssl req  -new -x509 -days 365 -key ca.key -out ca.crt

    5.在當前目錄創建demoCA,里面創建文件index.txt和serial,serial內容為01,index.txt為空,以及文件夾newcerts
    openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key
    將ca.crt文件通過郵件發送到ios設備的Mail上,進行證書的安裝
    nginx https配置:

     

     

        server {

            listen       443;
            server_name  ios.xxx.com;
            error_log  /dev/null;
            ssl                  on;
            ssl_certificate      server.crt;
            ssl_certificate_key  server.key;
            ssl_session_timeout  5m;
            #ssl_protocols  SSLv2 SSLv3 TLSv1;
            ssl_protocols  SSLv3 TLSv1;
            ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
            ssl_prefer_server_ciphers   on;
            root   /workspace/ios;
            #add_header Content-Disposition: 'attachment;';
        }
    posted @ 2014-06-26 21:42 小馬歌 閱讀(22900) | 評論 (2)編輯 收藏
     
    ylbtech-DatabaseDesgin:ylbtech-QQ(騰訊)-群空間-數據庫設計

    DatabaseName:QQ-群空間

    Model:群相冊、群共享、群論壇、群成員、留言板、公告。6個模塊。

    Type:空間-群空間、論壇

    Url:http://qun.qzone.qq.com/

    1.A,數據庫關系圖(Database Diagram)

    1.B,數據庫設計腳本(Database Design Script)-第一版
    use master
    go
    -- =============================================
    -- DatabaseName:QQ-群空間
    -- pubdate:16:50 2013-09-26
    -- author:Yuanbo
    -- http://qun.qzone.qq.com/
    -- =============================================
    IF EXISTS (SELECT * 
           FROM   master..sysdatabases 
           WHERE  name = N'qq_qun')
        DROP DATABASE qq_qun
    GO
    CREATE DATABASE qq_qun
    GO
    use qq_qun
    go
    -- =============================================
    -- ylb:1,賬戶表
    -- 
    -- =============================================
    create table account
    (
    account_id int identity(100000,1) primary key,    --編號【PK】
    nickname varchar(20) not null,    --昵稱
    pwd varchar(20) not null,        --密碼
    [type] int,        --類型 0:QQ號;1:QQ群號
    [enable] bit --狀態 0:正常;1:禁用
    )
    -- =============================================
    -- ylb: 3.1.1 相冊表
    -- =============================================
    create table album
    (
    album_id int primary key identity(1,1),    --編號【PK】
    album_name varchar(30) not null,        --相冊名稱
    album_desc varchar(80),        --相冊描述
    pubdate datetime default(getdate()),        --創建時間
    album_url varchar(100),                        --封面圖片
    account_qq int references account(account_id),    --相冊創建者的QQ號
    account_qun_id int references account(account_id),    --QQ群號
    )
    GO
    -- =============================================
    -- ylb: 3.2.1 相片表
    -- =============================================
    create table photo
    (
    photo_id int primary key identity(100,1),    --編號【PK】
    photo_name varchar(30) not null,        --相片名稱
    --photo_desc varchar(100),                --描述
    photo_url varchar(100),                --保存地址
    pubdate datetime default(getdate()),        --上傳時間
    album_id int references Album(album_id),    --相冊編號[FK]
    account_qq int references account(account_id),    --相冊創建者的QQ號
    account_qun_id int references account(account_id),    --QQ群號
    )
    GO
    -- =============================================
    -- ylb: 3.2.2 相片評論表
    -- =============================================
    create table replyphoto
    (
    replyphoto_id int primary key identity(100,1),--編號
    content varchar(200) not null,            --評論內容
    pubdate datetime default(getdate()),        --評論時間
    baseId int default(0),                --評論級次 0:發表;其他:回復|跟貼
    photo_id int references photo(photo_id),    --照片編號[FK]
    account_qq int references account(account_id),    --相冊創建者的QQ號
    account_qun_id int references account(account_id),    --QQ群號
    )
    -- =============================================
    -- ylb:1,群共享
    -- 
    -- =============================================
    create table share
    (
    [filename] varchar(20),    --文件名
    ttl datetime,    --有效期【14天】
    filesize int,        --文件大小【8.65KB】
    uploaded_author varchar(20),    --上傳者
    pubdate datetime default(getdate()),    --上傳時間
    download_cnt int,    --下載次數
    account_id int references account(account_id), --上傳者QQ號
    account_qun_id    int references account(account_id) --群編號
    )
    go
    -- =============================================
    -- ylb:1,群論壇
    -- 
    -- =============================================
    create table bbs
    (
    bbs_id int primary key identity(100,1),    --編號【PK】
    [subject] varchar(20),    --主題
    content varchar(400),    --內容
    pubdate datetime default(getdate()),        --創建時間
    lock_enable bit,    --鎖帖|解鎖
    stick_enable bit,    --0:不頂置;1:頂置
    tags_enable bit,    --0:;1:精華
    lightbox_enable bit, --1:高亮
    account_qq int references account(account_id),    --相冊創建者的QQ號
    account_qun_id int references account(account_id)    --QQ群號
    )
    go
    -- =============================================
    -- ylb:1,回復主題
    -- 
    -- =============================================
    create table replaybbs
    (
    replaybbs_id int primary key identity(100,1),    --編號【PK】
    content varchar(400),    --內容
    pubdate datetime default(getdate()),        --創建時間
    bbs_id int references bbs(bbs_id),    --主題編號
    account_qq int references account(account_id),    --相冊創建者的QQ號
    account_qun_id int references account(account_id)    --QQ群號
    )
    go
    -- =============================================
    -- ylb:1,群成員
    -- 
    -- =============================================
    create table member
    (
    member_id int primary key identity(100,1),--編號
    group_nikename varchar(30),    --群昵稱
    sex varchar(2),        --性別
    phone varchar(13),    --電話
    email varchar(60),    --郵箱
    remark varchar(200),--備注
    pubdate datetime default(getdate()),        --創建時間
    alow_admin_edit_enable bit,    --允許管理員協助修改我的群名片
    [role] int,    --角色:群主|管理員|成員【power】
    account_id int references account(account_id), --上傳者QQ號
    account_qun_id    int references account(account_id)--群編號
    )
    go
    -- =============================================
    -- ylb:1,留言板
    -- 
    -- =============================================
    create table messageboard
    (
    messageboard_id int primary key identity(100,1),--編號
    content varchar(30),    --內容
    pubdate datetime default(getdate()),       --創建時間
    account_id int references account(account_id), --上傳者QQ號
    account_qun_id    int references account(account_id)--群編號
    )
    go
    -- =============================================
    -- ylb:1,公告
    -- 
    -- =============================================
    create table notice
    (
    notice_id int primary key identity(100,1),--編號
    content varchar(30),    --內容
    pubdate datetime default(getdate()),       --創建時間
    account_id int references account(account_id), --上傳者QQ號
    account_qun_id    int references account(account_id)--群編號
    )
    go
    -- =============================================
    -- ylb:1,標簽【公共】
    -- 
    -- =============================================
    create table tag
    (
    tag_id uniqueidentifier,    --guid
    tag_name varchar(30),    --標簽名稱
    pubdate datetime default(getdate())       --創建時間
    )
    go
    print 'QQ 群空間數據創建成功!'
    1.C,數據庫設計腳本(Database Design Script)-第二版
    posted @ 2014-06-19 20:02 小馬歌 閱讀(400) | 評論 (0)編輯 收藏
     

    1.推送過程簡介

         (1)App啟動過程中,使用UIApplication::registerForRemoteNotificationTypes函數與蘋果的APNS服務器通信,發出注冊遠程推送的申請。若注冊成功,回調函數application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 會被觸發,App可以得到deviceToken,該token就是一個與設備相關的字符串.
         (2)App獲取到DeviceToken后,將DeviceToken發送給自己的服務端。
         (3)服務端拿到DeviceToken以后,使用證書文件,向蘋果的APNS服務器發起一個SSL連接。連接成功之后,發送一段JSON串,該JSON串包含推送消息的類型及內容。
        (4)蘋果的APNS服務器得到JSON串以后,向App發送通知消息,使得App的回調函數application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo被調用,App從userInfo中即可得到推送消息的內容。

     

    2. 用到的證書文件及生成過程

       (1)certSigningRequest文件,該文件在MAC系統中生成,用于在Apple網站上申請推送證書文件。
             生成過程:
             打開應用程序中的“鑰匙串訪問”軟件,從菜單中選擇 “鑰匙串訪問”-》“證書助理”-》“從證書頒發機構請求證書”,郵箱和名稱隨便填寫,然后選擇保存到磁盤,就可以在本地生成一個CertificateSigningRequest.certSigningRequest文件。

       (2)注冊一個支持push的app id,后面會用到。
            生成過程:
           進入developer.apple.com,選擇member center - Certificates, Identifiers & Profiles  -  Identifiers- App Ids,然后選擇注冊app id,設置appid名稱,同時,app id suffix一欄必須選擇explicit app id,然后設置bundle id,最后勾選 App Services中的 Push Notifications,這樣就可以注冊一個支持push的aphid。

        
      (3) 推送證書cer文件,該文件在developer.apple.com中生成,用于生成服務端需要的文件。
            生成過程:
            進入developer.apple.com,選擇member center - Certificates, Identifiers & Profiles  -  Certificates,然后選擇創建certificate,類型分為Development和Product。這里以Development為例,選擇Apple Push Notification service SSL (Sandbox) ,然后下一步,選擇之前生成的支持push的AppId,然后下一步,提交之前創建的CSR文件,再下一步就可以生成cer文件,然后保存到本地。

      (4)生成服務端使用的證書文件。如果是使用網上的mac 版PushMeBaby工具,在mac機器上進行推送消息的發送,那么有上面的cer文件就夠了。如果是使用PHP、java/c#開發自己的服務端,那么還需要將上面的cer文件做一個轉換,生成pem文件或者p12文件。

           生成php用的pem文件過程為
            首先雙擊前面保存的cer文件,此時會打開“鑰匙串訪問”軟件,里面會出現一個Apple Development  IOS push services證書,一個公用密鑰和一個專用秘鑰,秘鑰的名稱與證書助理中填寫的名稱一致。
          選中證書,導出為 apns-dev-cert.p12 文件
          選中專有秘鑰,導出為apns-dev-key.p12文件
          通過終端命令將這些文件轉換為PEM格式:
          openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12
          openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12
          最后, 需要將兩個pem文件合并成一個apns-dev.pem文件,此文件在連接到APNS時需要使用:
          cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem

           生成java/c#用的p12文件過程為

           openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12
           openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12
           openssl pkcs12 -export -in apns-dev-cert.pem -inkey apns-dev-key.pem -certfile CertificateSigningRequest.certSigningRequest -name "push" -out push.p12      

          
      (5)生成XCODE使用的provisioning文件,該文件用于真機調試。
             生成過程:
             進入developer.apple.com,選擇member center - Certificates, Identifiers & Profiles  -  Provisioning Profiles,然后選擇創建Provisioning  file,接著選擇iOS App Development ,下一步選擇AppId,選中之前建立的支持push的appid,接著下一步選擇支持push的certificate,下一步勾選需要支持的device id,最后一步設置provisioning文件的文件名,這樣provisioning文件就生成了。

        3. 服務端的開發
         (1)如果只是希望在mac電腦上測試一下消息的推送,可以使用PushMeBaby工具,使用起來比較簡單。該工具是開源的,可以從https://github.com/stefanhafeneger/PushMeBaby 下載,代碼的執行過程實際上就是設置一下SSL證書,然后連接APNS,接著發送JSON數據。由于要處理SSL邏輯,因此代碼稍微多點。在使用工具時,將工程資源中的cer文件替換成自己的cer文件,然后將代碼中的deviceToken替換成自己設備的deviceToken即可。


         (2)使用php開發服務端
           由于php已經內置了ssl模塊,因此使用php連接APNS服務器來發送json的過程實際上是很簡單的,代碼如下:

    該文件可以放到服務器中通過瀏覽器來訪問,也可以通過命令行的方式來解釋執行,代碼為:$ php -f Pusher.php

         

    復制代碼
    <?php $deviceToken= ‘自己的deviceToken'; //沒有空格
    $body = array("aps" => array("alert" => 'message',"badge" => 2,"sound"=>'default')); //推送方式,包含內容和聲音$$ctx = stream_context_create();

    //如果在Windows的服務器上,尋找pem路徑會有問題,路徑修改成這樣的方法:
    //$pem = dirname(__FILE__) . '/' . 'apns-dev.pem';
    //linux 的服務器直接寫pem的路徑即可
    stream_context_set_option($ctx,"ssl","local_cert","apns-dev.pem");
    $pass = "xxxxxx";stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);//
    //此處有兩個服務器需要選擇,如果是開發測試用,選擇第二名sandbox的服務器并使用Dev的pem證書,如果是正式發布,使用Product的pem并選用正式的服務器
    $fp = stream_socket_client("ssl://gateway.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
    $fp = stream_socket_client("ssl://gateway.sandbox.push.apple.com:2195", $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
    if (!$fp)
    {echo "Failed to connect $err $errstrn";return;}
    print "Connection OK\n";
    $payload = json_encode($body);$msg = chr(0) . pack("n",32) . pack("H*", str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
    echo "sending message :" . $payload ."\n";
    fwrite($fp, $msg); fclose($fp);
    ?>
    復制代碼

        4. 客戶端的開發
         (1)下載前面建立的cer文件和provisioning文件,雙擊,導入到xcode中,在build setting中code signing一欄里選擇這兩個文件的名稱,這樣就可以將支持push的app部署到真機中。
         

         (2)處理推送消息
               客戶端對推送消息的處理分兩種情況:
              一. 在App沒有運行的情況下,系統收到推送消息,用戶點擊推送消息,啟動App。此時,不會執行前面提到的          didReceiveRemoteNotification函數,而是在App的applicationDidFinishLaunching函數中處理推送,通過以下代碼可以獲取推送消息中的數據: NSDictionary *userInfo =[launchOptionsobjectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];


              二 . 當APP處于前臺時,系統收到推送消息,此時系統不會彈出消息提示,會直接觸發application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo函數,推送數據在userInfo字典中。

               當App處于后臺時,如果系統收到推送消息,當用戶點擊推送消息時,會執行application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo函數,
              此時AppDelegate中函數執行的順序為:
              applicationWillEnterForeground 
              application:didReceiveRemoteNotification

              applicationDidBecomeActiveI
    posted @ 2014-06-18 16:53 小馬歌 閱讀(5618) | 評論 (0)編輯 收藏
     

    下面內容來源于Quora上的一個提問,問題是使用Redis需要避免的五個問題。而回答中超出了五個問題的范疇,描述了五個使用Redis的注意事項。如果你在使用或者考慮使用Redis,可能你可以學習一下下面的一些建議,避免一下提到的問題。

    1.使用key值前綴來作命名空間

    雖然說Redis支持多個數據庫(默認32個,可以配置更多),但是除了默認的0號庫以外,其它的都需要通過一個額外請求才能使用。所以用前綴作為命名空間可能會更明智一點。

    另外,在使用前綴作為命名空間區隔不同key的時候,最好在程序中使用全局配置來實現,直接在代碼里寫前綴的做法要嚴格避免,這樣可維護性實在太差了。

    2.創建一個類似 ”registry” 的key用于標記key使用情況

    為了更好的管理你的key值的使用,比如哪一類key值是屬于哪個業務的,你通常會在內部wiki或者什么地方創建一個文檔,通過查詢這個文檔,我們能夠知道Redis中的key都是什么作用。

    與之結合,一個推薦的做法是,在Redis里面保存一個registry值,這個值的名字可以類似于 __key_registry__ 這樣的,這個key對應的value就是你文檔的位置,這樣我們在使用Redis的時候,就能通過直接查詢這個值獲取到當前Redis的使用情況了。

    3.注意垃圾回收

    Redis是一個提供持久化功能的內存數據庫,如果你不指定上面值的過期時間,并且也不進行定期的清理工作,那么你的Redis內存占用會越來越大,當有一天它超過了系統可用內存,那么swap上場,離性能陡降的時間就不遠了。所以在Redis中保存數據時,一定要預先考慮好數據的生命周期,這有很多方法可以實現。

    比如你可以采用Redis自帶的過期時間為你的數據設定過期時間。但是自動過期有一個問題,很有可能導致你還有大量內存可用時,就讓key過期去釋放內存,或者是內存已經不足了key還沒有過期。

    如果你想更精準的控制你的數據過期,你可以用一個ZSET來維護你的數據更新程度,你可以用時間戳作為score值,每次更新操作時更新一下score,這樣你就得到了一個按更新時間排序序列串,你可以輕松地找到最老的數據,并且從最老的數據開始進行刪除,一直刪除到你的空間足夠為止。

    4.設計好你的Sharding機制

    Redis目前并不支持Sharding,但是當你的數據量超過單機內存時,你不得不考慮Sharding的事(注意:Slave不是用來做Sharding操作的,只是數據的一個備份和讀寫分離而已)。

    所以你可能需要考慮好數據量大了后的分片問題,比如你可以在只有一臺機器的時候就在程序上設定一致性hash機制,雖然剛開始所有數據都hash到一臺機器,但是當你機器越加越多的時候,你就只需要遷移少量的數據就能完成了。

    5.不要有個錘子看哪都是釘子

    當你使用Redis構建你的服務的時候,一定要記住,你只是找了一個合適的工具來實現你需要的功能。而不是說你在用Redis構建一個服務,這是很不同的,你把Redis當作你很多工具中的一個,只在合適使用的時候再使用它,在不合適的時候選擇其它的方法。

    來源:www.quora.com

    posted @ 2014-06-18 11:17 小馬歌 閱讀(293) | 評論 (0)編輯 收藏
    僅列出標題
    共95頁: First 上一頁 18 19 20 21 22 23 24 25 26 下一頁 Last 
     
    主站蜘蛛池模板: 人成电影网在线观看免费| 四虎影在线永久免费四虎地址8848aa | 67194成是人免费无码| 久青草视频97国内免费影视| 欧洲 亚洲 国产图片综合| 亚洲V无码一区二区三区四区观看| 免费国产真实迷j在线观看| 波多野结衣在线免费视频 | 亚洲AV成人一区二区三区AV| 亚洲国产专区一区| 日本无卡码免费一区二区三区| 最近2019年免费中文字幕高清| 中文精品人人永久免费| 免费VA在线观看无码| 亚洲国产成人综合精品| 亚洲入口无毒网址你懂的| 亚洲电影免费在线观看| 国产亚洲成av人片在线观看| 亚洲中久无码不卡永久在线观看| 国产成人在线免费观看| 四虎在线免费播放| 最近最新中文字幕完整版免费高清| 最近2019免费中文字幕6| 在线观看免费av网站| 日韩在线不卡免费视频一区| 日本免费在线观看| 中文成人久久久久影院免费观看| 丰满人妻一区二区三区免费视频| 四虎成人精品国产永久免费无码| 美女露100%胸无遮挡免费观看| 亚洲av无一区二区三区| 亚洲第一综合天堂另类专 | 四虎影视免费永久在线观看| 四虎永久成人免费| vvvv99日韩精品亚洲| 又粗又大又长又爽免费视频| 又大又硬又爽免费视频| 亚洲国产综合久久天堂| 伊人亚洲综合青草青草久热| 亚洲午夜福利717| 亚洲国产美国国产综合一区二区 |