利用websocket实现android消息推送

jopen 10年前

    前段时间做一个项目,需要android客户端作为管理工具与web服务器后台实时交互,想了很多方法,包括androidpn、openfire+smack、xmpp协议,要么太繁琐,要么无法满足实时性。想来还是用socket,经人提醒想到了websocket。

    websocket协议是近些年随html5发展而诞生的,主要用于解决web服务器与客户端无法双向交互的问题。如今已经被W3C收入标准协议。
    服务器支持:tomcat、jetty的最新版本都已支持websocket;如果不想更换现有服务器,也可用spring4.0作为替代。据说新版本的jre将收入websocket类,没具体接触。
</div>
    客户端支持:目前的主流浏览器都已经实现了websocket,但由于前期协议版本变化太快,很多厂商没有跟上。android默认浏览器就不支持websocket,IE也直到IE10才支持。

    网上已有通过html实现websocket client的例子,这里我们用java实现客户端连接。废话不说,上Demo:

1.服务器端
服务器用了tomcat 7.0,直接使用tomcat的websocket实现
1)连接管理类
import java.io.IOException;  import java.nio.CharBuffer;  import java.util.ArrayList;  import java.util.List;  import org.apache.catalina.websocket.MessageInbound;  import org.apache.catalina.websocket.WsOutbound;    public class MessageCenter  {   private static MessageCenter        instance           = new MessageCenter();     private List<MessageInbound>        socketList;     private MessageCenter()   {    this.socketList = new ArrayList<MessageInbound>();   }     public static MessageCenter getInstance()   {    return instance;   }     public void addMessageInbound(MessageInbound inbound)   {    socketList.add(inbound);   }     public void removeMessageInbound(MessageInbound inbound)   {    socketList.remove(inbound);   }     public void broadcast(CharBuffer msg) throws IOException   {    for (MessageInbound messageInbound : socketList)    {     CharBuffer buffer = CharBuffer.wrap(msg);     WsOutbound outbound = messageInbound.getWsOutbound();     outbound.writeTextMessage(CharBuffer.wrap("broadcasting:" + msg));     // outbound.writeTextMessage(buffer);     outbound.flush();    }   }  }

2)消息入口类
import java.io.IOException;  import java.nio.ByteBuffer;  import java.nio.CharBuffer;  import java.util.Date;    import org.apache.catalina.websocket.MessageInbound;  import org.apache.catalina.websocket.WsOutbound;    public class MyMessageInbound extends MessageInbound {     @Override   protected void onBinaryMessage(ByteBuffer arg0) throws IOException {    // TODO Auto-generated method stub       }     @Override   protected void onTextMessage(CharBuffer msg) throws IOException {    System.out.println("Received:"+msg);    MessageCenter.getInstance().broadcast(msg);       }     @Override   protected void onClose(int status) {    System.out.println("close:"+new Date());    MessageCenter.getInstance().removeMessageInbound(this);    super.onClose(status);   }     @Override   protected void onOpen(WsOutbound outbound) {    System.out.println("open :"+new Date());    super.onOpen(outbound);    MessageCenter.getInstance().addMessageInbound(this);   }  }

3)Websocket servlet
import javax.servlet.http.HttpServletRequest;    import org.apache.catalina.websocket.StreamInbound;  import org.apache.catalina.websocket.WebSocketServlet;    public class MeWebSocketServlet extends WebSocketServlet  {     private static final long serialVersionUID = -7178893327801338294L;     @Override   protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request)   {    System.out.println("##########client login#########");    return new MeMessageInbound();   }    }

4)添加servlet到web.xml
        < servlet>                 < servlet-name> android</ servlet-name >                 < servlet-class> MyWebSocketServlet </servlet-class >          </ servlet>          < servlet-mapping>                 < servlet-name> android</ servlet-name >                 < url-pattern> *.do</ url-pattern >          </ servlet-mapping>


2.客户端
    客户端使用java实现websocket client,
    网上有人实现了Java-websocket:https://github.com/TooTallNate/Java-WebSocket 可以取得源码,用maven编译。
    最新jar包下载地址:

    引用jar包后,实现简单消息连接:
import java.net.URI;  import java.net.URISyntaxException;    import org.java_websocket.client.WebSocketClient;  import org.java_websocket.drafts.Draft;  import org.java_websocket.drafts.Draft_10;  import org.java_websocket.drafts.Draft_17;  import org.java_websocket.framing.Framedata;  import org.java_websocket.handshake.ServerHandshake;    /** This example demonstrates how to create a websocket connection to a server. Only the most important callbacks are overloaded. */  public class ExampleClient extends WebSocketClient {            public ExampleClient( URI serverUri , Draft draft ) {                  super( serverUri, draft );          }            public ExampleClient( URI serverURI ) {                  super( serverURI );          }            @Override          public void onOpen( ServerHandshake handshakedata ) {                  System.out.println( "opened connection" );                  // if you plan to refuse connection based on ip or httpfields overload: onWebsocketHandshakeReceivedAsClient          }            @Override          public void onMessage( String message ) {                  System.out.println( "received: " + message );          }            @Override          public void onFragment( Framedata fragment ) {                  System.out.println( "received fragment: " + new String( fragment.getPayloadData().array() ) );          }            @Override          public void onClose( int code, String reason, boolean remote ) {                  // The codecodes are documented in class org.java_websocket.framing.CloseFrame                  System.out.println( "Connection closed by " + ( remote ? "remote peer" : "us" ) );          }            @Override          public void onError( Exception ex ) {                  ex.printStackTrace();                  // if the error is fatal then onClose will be called additionally          }            public static void main( String[] args ) throws URISyntaxException {                  ExampleClient c = new ExampleClient( new URI( "ws://localhost:8080/myweb/android.do" ), new Draft_17() );                   c.connect();          }    }
注意,连接中使用的new Draft_17()就是使用的协议version,Tomcat 7.0的协议版本需要高于Draft_17。

总结websocket的利弊
优点:
与socket相比,可以节省额外的端口占用,直接使用一个公网域名访问。另外协议对报文的流量消耗做了优化。

缺点:
毕竟websocket底层也是socket连接,因而当大并发用户连接时目测会消耗较多资源。

参考:

来自:http://blog.csdn.net/chifengxin/article/details/14521093