androidpn server部署-server 最大能支持多大的并发量

(文中部分内容来自网络)
&XMPP : The Extensible Messaging andPresence Protocol.
&中文全称:可扩展通讯和表示协议.
& & & & 简介:可扩展通讯和表示协议 (XMPP) 可用于服务类实时通讯、表示和需求响应服务中的XML数据元流式传输。
XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线探测。
它在促进服务器之间的准即时操作。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即
使其操作系统和浏览器不同。
XMPP主要显著的优点主要有以下几个方面:
1、分布式& 任何人都可以运行自己的XMPP服务器,它没有主服务器,客户端简单
2、安全性很高。使用SASL及TLS等技术的可靠安全性
3、开发性 它是开源的,易于进行学习和了解
4、跨平台& 毋庸置疑,使用的XML数据格式进行传输的
Androidpn:
Androidpn :android pushnotification(中文名称:Android消息推送)。
基于XMPP协议的java开发有一个开源框架,那就是smack,它主要封装了一些XMPP的实现。在引用smack的基础上
实现和服务器端的持久连接,以实现服务器对客户端的推送,那就是Androidpn。Androidpn在客户端集成了smack。
这样就可以很容易的简立一个和服务器端的基于xmpp协议的socket连接。所以Androidpn可以说是使用了smack框架
的开源项目。
Androidpn使用好处:
用Androidpn好处有以下方面:采用完全开放的XMPP协议进行数据传输(QQ,MSN,GTalk等都是采用的这种协议)。
&良好的框架支持(专门为android&而产生的推送框架smack,以及很好的管理socket的框架MINA,都是很成熟的产品);
完全开放的源代码(我们可以在Androidpn的基础上进行修改,来满足我们的任何需求变更);大大的减少了客户端的代
码,降低了android的开发难度。
Androidpn技术实现:
(这里就不说下载源码后怎么操作了,网上教程颇多)
客户端与服务器建立socket连接。
Androidpn客户端,进行管理连接的信息,比如XMPP的端口、IP(域名)、登录的用户名密码,以及对连接的维护(定时的
发送连接请求保持连接不失效)。整个服务器端和客户端的通信是基于一个session(会话)过程,会话开始,首先会
指定服务器的端口号,然后把信息发送到服务器端;而向服务器发送消息:&stream&根节点的方式开始传递,只有在服
务器和客户端关闭的时候才会发送它的结束标记&/stream&。以客户端通过XMPP协议只用做的就是接收消息,而所有
其它的操作都交给服务器,比如管理连接、消息保存等等,这样就很大程度的减轻了客户端的负担。
Androidpn服务器端,Androidpn主要用到的技术是Spring和Hibernate。保存用户信息到数据库并做验证,主要是用来
展示用户状态和发送信息用的。在服务器关闭的时候,发送一个&/stream&标记到客户端,告知连接断开,客户端需重
新连接。管理Socket连接的时候,Androidpn采用了MINA框架来进行管理,MINA的优点就是改变了我们传统的管理socket的方式,比如每建立一个socket开一个线程,而MINA
可以实现多个线程管理N多个用户(这句不是太明白,
谁知道给留个言说下,万分感谢)。在处理高并发的推送上无疑是有巨大的好处的。
& & & & &在数据传输过程中,信息处理采用XML节的方式传递信息,消息封装通常采用IQ(Info/Query)、Message(消息)、
Presence(在线状态探测)。
& & & & &但是Androippn中好像没有用到Message(也许我理解错误),从Server到Client和Client到Server都用的IQ封装。
除此之外,xml的传递过程还有type可为set、get、result、error四种类型,我个人理解是:set指请求更新(改变)处理
(如从Server推送消息到client、注册用户),get指获取数据(如取好友列表),result是指请求更新后给client的应答
(如登录是否成功得告诉Client,见ClientSession中的createSession()方法),error就明显了指错误,一般是指告诉Client断开
长连接和重连:
1.&&&&&&&&&&&&&&客户端第一次连接服务器建立的是一个socket连接,如果服务器需要关闭返回&/stream&或者一个表示ERROR的
流返回给客户端,客户端将启动定时重连线程。
2.&&&&&&&&&&&&&&本地连接服务器失败后将启动定时重连线程。
3.&&&&&&&&&&&&&&本地监听网络信息,如果网络不可用,也将启动重连线程。
4.&&&&&&&&&&&&&&连接成功后,监听socket连接状态,如果断开,启动重连线程。
注:1)据说防火墙可能会关闭非活动状态的连接,所以又据说建立连接后会向服务器发送心跳(keepLive)。
主要文件作用:
DemoAppActivity&&&&程序入口Activity
ServiceManager &&&&&&&& 服务管理器,控制后台服务的启动和停止
NotificationService&&&&& 初始化广播,并注册广播; 初始化线程发送开始连接广播等其它
XmppManager&&&&&&&&&&&& 定义调用asmack库的方法,做连接及其它。
ConnectivityReceiver&连接广播接收者,调用XmppManager的连接方法
PersistentConnectionListener& 重连监听,由asmack调用,在连接时被注册
PhoneStateChangeListener&&& 手机连接状态监听,服务启动时被注册
NotificationIQProvider 通知消息提供者,由asmack库调用,并接收到服务器返回的消息(xml)
NotificationPacketListener&&&& 通知消息监听器,在连接后被注册,由asmack库调用,当NotificationIQProvider接收
到服务器消息后,会调用监听器的回调方法processPacket,再发送广播给NotificationReceiver
NotificationReceiver&&& 通知广播接收者,调用Notifier中的通知方法
Notifier&&&&&&&&&&&&调用notify方法界面通知。
注:NotificationIQProvider中parseIQ()方法解析服务器发送的xml数据并封装成实体。
org.androidpn.server.xmpp 包里面定义了一些异常类型,主要是包含有入口类XmppServer,这个类用来启动和停止server程序。
org.androidpn.server.xmpp.auth 包里面是认证的一些类,我们自己的认证模块可以在这里与androidpn进行结合。
org.androidpn.server.xmpp.codec 是XMPP协议的XML文件解析包,server收到和发送的消息都要通过这个包来进行xmpp协议编码和解码。
org.androidpn.server.xmpp.handler 包主要是对消息的处理,我们可以针对不同的消息类型定义自己的handler,可在这包在类中处理客户端发送的数据
org.androidpn.server.xmpp.net 包负责维护与client之间的持久连接,并实现了一些传输方式供发送xmpp消息时使用。
org.androidpn.server.xmpp.presence 里面只包含PresenceManager类,用来维护client的在线状态。
org.androidpn.server.xmpp.push 里面只有一个NotificationManager类包含有向client发送消息的接口,可自己封装需要的数据发送到客户端。
org.androidpn.server.xmpp.session包定义了用来表示持久链接的session,每个session包含一条连接的状态信息。用户管理所有会话,比如连接断开,删除session以及建立连接,添加session等等。&
org.androidpn.server.xmpp.ssl是对连接进行ssl认证的工具包。
注: 以上红色包中,NotificationManager(org.androidpn.server.xmpp.push)带着username和message
到Session(org.androidpn.server.xmpp.session)类中找到相应的session,&并封装成xml格式,再调用
Connection(org.androidpn.server.xmpp.net)中的deliver()方法给相应用户发送数据达到通信。
Views(...) Comments()源码分析(2)
郭霖 Android消息推送教学视频
ApacheMINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可扩展性的网络应用程序。它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的事件驱动的异步API。
public class MinaTimeServer
public static void main( String[] args ) throws IOException
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new
TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler(
new TimeServerHandler() );
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
TimeServerHandler
public class TimeServerHandler extends IoHandlerAdapter
public void exceptionCaught( IoSession session, Throwable cause ) throws
cause.printStackTrace();
public void messageReceived( IoSession session, Object message ) throws Exception
String str = message.toString();
if( str.trim().equalsIgnoreCase("quit") ) {
session.close();
Date date = new Date();
session.write( date.toString() );
System.out.println("Message written...");
public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
System.out.println( "IDLE " + session.getIdleCount( status ));
sessionOpen()...
sessionCreated()...
sessionClose()...
messageSent()...
自定义编解码
(以下代码出处 郭霖消息推送视频)
XmppDecoder
public class XmppDecoder extends CumulativeProtocolDecoder {
public boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
int startPosition = in.position();
while(in.hasRemaing()){
byte b = in.get();
if(b=='\n'){
int currentPosition = in.position();
int limit = in.limit();
in.position(startPosition);
in.limit(currentPosition);
IoBuffer buf= in.slince();
byte[] dest = new byte[buf.limit()];
buf.get(dest);
String str = new String(dest);
out.write(str);
in.position(currentPosition);
in.limit(limit);
XmppEncoder
public class XmppEncoder implements ProtocolEncoder {
public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
String s =null;
if(message instanceof String){
s = (String)
if(s!=null){
CharsetEncoder charsetEncoder =(CharsetEncoder)
session.getAttribute("encoder");
if(charsetEncoder==null){
charsetEncoder
= Charset.defaultCharset().newEncoder();
session.setAttribute("encoder",charsetEncoder);
IoBuffer ioBuffer = IoBuffer.allocate(s.length);
ioBuffer.setAutoExpanded(true);
ioBuffer.putString(s,charsetEncoder);
ioBuffer.flip();
out.write(ioBuffer);
public void dispose(IoSession session) throws Exception {
XmppFactory
public class XmppCodecFactory implements ProtocolCodecFactory {
private final XmppE
private final XmppD
* Constructor.
public XmppCodecFactory() {
encoder = new XmppEncoder();
decoder = new XmppDecoder();
* Returns a new (or reusable) instance of ProtocolEncoder.
public ProtocolEncoder getEncoder(IoSession session) throws Exception {
* Returns a new (or reusable) instance of ProtocolDecoder.
public ProtocolDecoder getDecoder(IoSession session) throws Exception {
Spring MVC + Mina
AndroidPN服务器端采用Spring MVC架构,那么是如何配置Mina的呢?
spring-config
id="xmppHandler" class="org.androidpn.server.xmpp.net.XmppIoHandler" /&
id="filterChainBuilder"
class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder"&
name="filters"&
key="executor"&
class="org.apache.mina.filter.executor.ExecutorFilter" /&
key="codec"&
class="org.apache.mina.filter.codec.ProtocolCodecFilter"&
class="org.androidpn.server.xmpp.codec.XmppCodecFactory" /&
id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor"
init-method="bind" destroy-method="unbind"&
name="defaultLocalAddress" value=":5222" /&
name="handler" ref="xmppHandler" /&
name="filterChainBuilder" ref="filterChainBuilder" /&
name="reuseAddress" value="true" /&
id="getSessionConfig" factory-bean="ioAcceptor" factory-method="getSessionConfig"&
name="readerIdleTime" value="30"&&
AndroidPN项目结构
conf.security、config.properties、config存keystore、truststore等信息
使用hibernate框架配置数据库ORM
spring-config
spring mvc架构 配置bean
dispatcher-servlet
spring mvc架构 负责责任分配
decorators
用户在线状态项目流程
页面采用decorators
WEB-INF decorators.xml
defaultdir="/decorators"&
&/includes/*&
&/40*.jsp&
name="default" page="default.jsp"&
excludes不需要装饰的页面
name装饰器名字
page装饰器文件
pattern 需要使用装饰器的访问地址,可以配置多个
此处为/* 即WEB-INF pages下的所有
default.jsp
位置WebRoot下的decorates内
id="page"&
id="header"&
page="/includes/header.jsp"/&
id="content"&
id="tabmenu"&
& href="/index.do"
class="&c:if test="${topMenu eq 'home'}"& value="current" /&
& href="/user.do"
class="&c:if test="${topMenu eq 'user'}"& value="current" /&
&"&Users&&
& href="/session.do"
class="&c:if test="${topMenu eq 'session'}"& value="current" /&
&"&Sessions&&
& href="/notification.do"
class="&c:if test="${topMenu eq 'notification'}"& value="current" /&
&"&Notifications&&
id="tabcontent"&
id="footer"&
page="/includes/footer.jsp"/&
四个选项卡index、 user、 session 、notification,点击各个选项卡会发生什么呢?
dispatcher-servlet.xml
id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"&
name="mappings"&
/user_api.do=userapiController
/notification_api.do=notificationapiController
/index.do=filenameController
/user.do=userController
/session.do=sessionController
/notification.do=notificationController
name="order" value="1" /&
id="userapiController" class="org.androidpn.server.console.api.UserApiController"&
name="methodNameResolver" ref="paramResolver" /&
id="notificationapiController"
class="org.androidpn.server.console.api.NotificationApiController"&
name="methodNameResolver" ref="paramResolver" /&
id="filenameController"
class="org.springframework.web.servlet.mvc.UrlFilenameViewController" /&
id="userController" class="org.androidpn.server.console.controller.UserController"&
name="methodNameResolver" ref="paramResolver" /&
id="sessionController"
class="org.androidpn.server.console.controller.SessionController"&
name="methodNameResolver" ref="paramResolver" /&
id="notificationController"
class="org.androidpn.server.console.controller.NotificationController"&
name="methodNameResolver" ref="paramResolver" /&
index.do UrlFilenameViewController检查URL,查找到文件请求的文件名,并为之视图的名字,直接跳转到index.jsp页面
user.do 交由UserController进行逻辑控制处理
UserController
public class UserController extends MultiActionController {
private UserService userS
public UserController() {
userService = ServiceLocator.getUserService();
public ModelAndView list(HttpServletRequest request,
HttpServletResponse response) throws Exception {
PresenceManager presenceManager = new PresenceManager();
List&User& userList = userService.getUsers();
for (User user : userList) {
if (presenceManager.isAvailable(user)) {
user.setOnline(true);
user.setOnline(false);
ModelAndView mav = new ModelAndView();
mav.addObject("userList", userList);
mav.setViewName("user/list");
UserService
获取userservice bean实例
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
userService = context.getBean("userService");
spring-config.xml
id="userDao" class="org.androidpn.server.dao.hibernate.UserDaoHibernate"&
name="sessionFactory" ref="sessionFactory" /&
id="userService" class="org.androidpn.server.service.impl.UserServiceImpl"&
name="userDao" ref="userDao" /&
UserServiceImpl继承UserService,对UserDao进行数据操作再封装;UserDaoHibernate继承UserDao,对User进行数据操作
UserDaoHibernate
public class UserDaoHibernate extends HibernateDaoSupport implements UserDao {
public User getUser(Long id) {
return (User) getHibernateTemplate().get(User.class, id);
public User saveUser(User user) {
getHibernateTemplate().saveOrUpdate(user);
getHibernateTemplate().flush();
public void removeUser(Long id) {
getHibernateTemplate().delete(getUser(id));
public boolean exists(Long id) {
User user = (User) getHibernateTemplate().get(User.class, id);
return user != null;
@SuppressWarnings("unchecked")
public List&User& getUsers() {
return getHibernateTemplate().find(
"from User u order by u.createdDate desc");
@SuppressWarnings("unchecked")
public List&User& getUsersFromCreatedDate(Date createDate) {
return getHibernateTemplate()
.find("from User u where u.createdDate &= ? order by u.createdDate desc",
createDate);
@SuppressWarnings("unchecked")
public User getUserByUsername(String username) throws UserNotFoundException {
List users = getHibernateTemplate().find("from User where username=?",
username);
if (users == null || users.isEmpty()) {
throw new UserNotFoundException("User '" + username + "' not found");
return (User) users.get(0);
User对象需要再hibernate.cfg.xml中配置
class="org.androidpn.server.model.User" /&
服务器与客户端数据交互
服务器接收客户端数据(使用Mina框架)
XmppIoHandler
Mina逻辑处理器Handler
public void sessionOpened(IoSession session) throws Exception {
log.debug("sessionOpened()...");
log.debug("remoteAddress=" + session.getRemoteAddress());
XMLLightweightParser parser = new XMLLightweightParser("UTF-8");
session.setAttribute(XML_PARSER, parser);
Connection connection = new Connection(session);
session.setAttribute(CONNECTION, connection);
session.setAttribute(STANZA_HANDLER, new StanzaHandler(serverName,
connection));
public void messageReceived(IoSession session, Object message)
throws Exception {
log.debug("messageReceived()...");
log.debug("RCVD: " + message);
StanzaHandler handler = (StanzaHandler) session
.getAttribute(STANZA_HANDLER);
int hashCode = Thread.currentThread().hashCode();
XMPPPacketReader parser = parsers.get(hashCode);
if (parser == null) {
parser = new XMPPPacketReader();
parser.setXPPFactory(factory);
parsers.put(hashCode, parser);
handler.process((String) message, parser);
} catch (Exception e) {
log.error(
"Closing connection due to error while processing message: "
+ message, e);
Connection connection = (Connection) session
.getAttribute(CONNECTION);
connection.close();
StanzaHandler
public void process(String stanza, XMPPPacketReader reader)
throws Exception {
boolean initialStream = stanza.startsWith("&stream:stream");
if (!sessionCreated || initialStream) {
if (!initialStream) {
if (!sessionCreated) {
sessionCreated = true;
MXParser parser = reader.getXPPParser();
parser.setInput(new StringReader(stanza));
createSession(parser);
} else if (startedTLS) {
startedTLS = false;
tlsNegotiated();
if (stanza.equals("&/stream:stream&")) {
session.close();
if (stanza.startsWith("&?xml")) {
Element doc = reader.read(new StringReader(stanza)).getRootElement();
if (doc == null) {
String tag = doc.getName();
if ("starttls".equals(tag)) {
if (negotiateTLS()) {
startedTLS = true;
connection.close();
session = null;
} else if ("message".equals(tag)) {
processMessage(doc);
} else if ("presence".equals(tag)) {
log.debug("presence...");
processPresence(doc);
} else if ("iq".equals(tag)) {
log.debug("iq...");
processIQ(doc);
log.warn("Unexpected packet tag (not message, iq, presence)"
+ doc.asXML());
session.close();
createSession 创建session会话
private void createSession(XmlPullParser xpp)
throws XmlPullParserException, IOException {
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
String namespace = xpp.getNamespace(null);
if ("jabber:client".equals(namespace)) {
session = ClientSession.createSession(serverName, connection, xpp);
if (session == null) {
StringBuilder sb = new StringBuilder(250);
sb.append("&?xml version='1.0' encoding='UTF-8'?&");
sb.append("&stream:stream from=\"").append(serverName);
sb.append("\" id=\"").append(randomString(5));
sb.append("\" xmlns=\"").append(xpp.getNamespace(null));
sb.append("\" xmlns:stream=\"").append(
xpp.getNamespace("stream"));
sb.append("\" version=\"1.0\"&");
StreamError error = new StreamError(
StreamError.Condition.bad_namespace_prefix);
sb.append(error.toXML());
connection.deliverRawText(sb.toString());
connection.close();
.warn("Closing session due to bad_namespace_prefix in stream header: "
+ namespace);
Connection的deliverRawText
服务器流写出
private void deliverRawText(String text, boolean asynchronous) {
log.debug("SENT: " + text);
if (!isClosed()) {
IoBuffer buffer = IoBuffer.allocate(text.length());
buffer.setAutoExpand(true);
boolean errorDelivering = false;
buffer.put(text.getBytes("UTF-8"));
buffer.flip();
if (asynchronous) {
ioSession.write(buffer);
boolean ok = ioSession.write(buffer).awaitUninterruptibly(
Config.getInt("connection.ack.timeout", 2000));
if (!ok) {
log.warn("No ACK was received when sending stanza to: "
+ this.toString());
} catch (Exception e) {
log.debug("Connection: Error delivering raw text" + "\n"
+ this.toString(), e);
errorDelivering = true;
if (errorDelivering && asynchronous) {
processIQ处理IQ类型的请求数据(IQ类型的数据集成Packet)。
IQRouter路由将IQ数据根据namespace交由给指定IQHandler
private void handle(IQ packet) {
IQHandler handler = getHandler(namespace);
if (handler == null) {
sendErrorPacket(packet,
PacketError.Condition.service_unavailable);
handler.process(packet);
用户信息注册User,添加新User,命名空间是jabber:iq:register,选择的IQHandler是IQRegisterHandler
IQRegisterHandler handleIQ
userService.saveUser(user);
此文结束,其余细节可自行分析了,XMPP协议需要继续分析学习。
望共勉之。O(∩_∩)O谢谢
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:7033次
排名:千里之外
原创:11篇
评论:20条
(1)(2)(2)(2)(1)(1)(2)(1)androidpn-server使用说明_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
androidpn-server使用说明
上传于||暂无简介
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩1页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&

我要回帖

更多关于 androidpn server部署 的文章

 

随机推荐