摘要: http://autumnrain-zgq.iteye.com/blog/1743279苹果平台开发的应用程序,不支持后台运行程序,所以苹果有一个推送服务在软件的一些信息推送给用户。 JAVA中,有一个开源软件,JavaPNS实现了Java平台中连接苹果服务器与推送消息的服务。但是在使用的过程中,有两点需要使用者注意一下,希望后续使用的同志们能避免我走过的覆辙。 1、一是向苹果...  阅读全文
posted @ 2014-07-22 18:44 小马歌 阅读(565) | 评论 (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 小马歌 阅读(203) | 评论 (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 小马歌 阅读(241) | 评论 (0)编辑 收藏
 
     摘要: 一 原理区别     一般在浏览器中输入网址访问资源都是通过GET方式;在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认为GET提交 Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网...  阅读全文
posted @ 2014-07-15 17:22 小马歌 阅读(369) | 评论 (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 小马歌 阅读(2797) | 评论 (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 小马歌 阅读(248) | 评论 (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中。或是拖到Organizer中。如图:



 

五、发布应用

在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 小马歌 阅读(22875) | 评论 (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 小马歌 阅读(381) | 评论 (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 小马歌 阅读(5603) | 评论 (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 小马歌 阅读(279) | 评论 (0)编辑 收藏
仅列出标题
共95页: First 上一页 18 19 20 21 22 23 24 25 26 下一页 Last