WebSocket集成XMPP網頁即時通信1:Java Web Project服務端/客戶端Jetty9開發初探

Web 應用的信息交互過程一般是客戶端經過瀏覽器發出一個請求,服務器端接收和審覈完請求後進行處理並返回結果給客戶端,而後客戶端瀏覽器將信息呈現出來,這種機制對於信息變化不是特別頻繁的應用尚能相安無事,可是對於那些實時要求比較高的應用來講,好比說在線遊戲、在線證券、設備監控、新聞在線播報、RSS 訂閱推送等等,當客戶端瀏覽器準備呈現這些信息的時候,這些信息在服務器端可能已通過時了。因此保持客戶端和服務器端的信息同步是實時 Web 應用的關鍵要素,對 Web 開發人員來講也是一個難題。在 WebSocket 規範出來以前,開發人員想實現這些實時的 Web 應用,不得不採用一些折衷的方案,其中最經常使用的就是輪詢 (Polling) 和 Comet 技術(AJAX)。html

但AJAX有顯著缺點:java

一、瀏覽器須要不斷的向服務器發出請求,然而HTTP request 的header是很是長的,裏面包含的有用數據可能只是一個很小的值,這樣會佔用不少的帶寬。web

二、客戶端和服務器端的編程實現都比較複雜,在實際的應用中,爲了模擬比較真實的實時效果,開發人員每每須要構造兩個 HTTP 鏈接來模擬客戶端和服務器之間的雙向通信,一個鏈接用來處理客戶端到服務器端的數據傳輸,一個鏈接用來處理服務器端到客戶端的數據傳輸。編程

WebSocket API是下一代客戶端-服務器的異步通訊方法。該通訊取代了單個的TCP套接字,使用ws或wss(ssl加密)協議,可用於任意的客戶端和服務器程序。WebSocket目前由W3C進行標準化。WebSocket已經受到Firefox 四、Chrome 四、Opera 10.70、Edge以及Safari 5等瀏覽器的支持。api

WebSocket API最偉大之處在於服務器和客戶端能夠在給定的時間範圍內(IdleTime)的任意時刻,相互推送信息。WebSocket並不限於以Ajax(或XHR)方式通訊,由於Ajax技術須要客戶端發起請求,而WebSocket服務器和客戶端能夠彼此相互推送信息;XHR受到域的限制,而WebSocket容許跨域通訊。跨域

爲方便開發,用了Jetty服務器,在網上不少例子都是基於jetty7.0的。但jetty9.0之後WebSocket的基類代碼變化不少,因而本身動手開發,參考jetty官網:瀏覽器

http://www.eclipse.org/jetty/documentation/current/websocket-jetty.html#jetty-websocket-api服務器

 

首先在Eclipse新建一個Dynamic web projectwebsocket

請自行下載安裝jetty,另外須要在Eclipse安裝Jetty工具,便於調試。session

 

工程目錄結構:

 

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 
 <context-param>
   <param-name>debug</param-name>
   <param-value>false</param-value>
 </context-param>
 
 <session-config>  <!--  10 minutes -->
   <session-timeout>10</session-timeout>
 </session-config>

<servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>MyServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/wsexample</url-pattern>
</servlet-mapping>
 
 <welcome-file-list>
   <welcome-file>index.html</welcome-file>
   <welcome-file>index.htm</welcome-file>
   <welcome-file>index.jsp</welcome-file>
   <welcome-file>default.html</welcome-file>
   <welcome-file>default.htm</welcome-file>
   <welcome-file>default.jsp</welcome-file>
 </welcome-file-list>
 
</web-app>

ant用慣了,懶得去查maven的dependencies了。

build.xml  

jetty.lib.dir、deploy.path請自行修改
<?xml version="1.0" encoding="UTF-8"?>

<project name="WebSocket" default="compile">
    
    <property name="name" value="ws"/>
    <property environment="env"/>
    <property name="src.dir" value="src"/>
    <property name="web.dir" value="WebContent" />
    <property name="build.dir" location="${web.dir}/WEB-INF/classes"/>
    <property name="jetty.lib.dir" location="D:/Software/jetty93/lib"/>
    <property name="dist.dir" location="dist"/>
    <property name="deploy.path" location="D:/Software/jetty93/webapps"/>
  
    <path id="compile.classpath">
        <fileset dir="${jetty.lib.dir}"/>
    </path>
  
    <target name="init">
        <mkdir dir="${build.dir}"/>
        <mkdir dir="${dist.dir}"/>
    </target>     
  
    <target name="compile" depends="init">
        <javac srcdir="${src.dir}" destdir="${build.dir}" 
               includeantruntime="false">
            <classpath refid="compile.classpath"/>
        </javac>
        <echo>Compilation completed</echo>
    </target>
  
    <target name="archive" depends="compile">
        <war destfile="${dist.dir}/${name}.war" needxmlfile="false">
            <fileset dir="${web.dir}"/>
        </war>
        <echo>Archive created</echo>
    </target> 
  
    <target name="clean" depends="init">
        <delete dir="${build.dir}"/>
        <delete dir="${dist.dir}"/>
        <echo>Cleaning completed</echo>
    </target>  
    
    <target name="deploy" depends="archive">
        <copy file="${dist.dir}/${name}.war" overwrite="true" 
              todir="${deploy.path}"/>
        <echo>Archive deployed</echo>
    </target>    
    
</project>

 

 

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>你好!websocket</title>
</head>
<body>
hello,WebSocket
<script src="index.js"></script>
</body>
</html>

 

index.js

var ws = new WebSocket("ws://localhost:8080/ws/wsexample");

ws.onopen = function() {
    document.write("WebSocket opened <br>");
    ws.send("Hello Server");
};

ws.onmessage = function(evt) {
    document.write("Message: " + evt.data);
};

ws.onclose = function() {
    document.write("<br>WebSocket closed");
};

ws.onerror = function(err) {
    document.write("Error: " + err);
};

其中 ws://localhost:8080/ws/wsexample 是WebSocket 服務器地址,是服務器端的Servelt,經過MyServlet.java實現

 

WebSocketServlet 類中 configure(WebSocketServletFactory factory) 用來註冊socket處理類和設置鏈接時長,在這個時長內,保持長鏈接。

官網的定義:
http://www.eclipse.org/jetty/documentation/current/jetty-websocket-server-api.html

MyServlet.java

暫不理會HTTP GET請求的處理.

 
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.annotation.WebServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

 
@WebServlet(name = "MyServlet", urlPatterns = { "/wsexample" })
public class MyServlet extends WebSocketServlet {

    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().println("HTTP GET method not implemented.");
    }

    @Override
    public void configure(WebSocketServletFactory factory) {
        factory.getPolicy().setIdleTimeout(10000);
        factory.register(MySocket.class);
    }
}

 

MySocket.java 是socket具體處理類,寫法在jetty7和jetty9有很大的不一樣。

import java.io.IOException;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;

@WebSocket
public class MySocket {

    Session WebSession;
    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {
        System.out.println("Close: " + reason);
    }

    @OnWebSocketError
    public void onError(Throwable t) {
        System.out.println("Error: " + t.getMessage());
    }

    //客戶端鏈接
    @OnWebSocketConnect
    public void onConnect(Session session) {
        WebSession = session;
        System.out.println("Connect: " + session.getRemoteAddress().getAddress());
        
        try {
            session.getRemote().sendString("你好!客戶端。我是服務端。<br>");
        } catch (IOException e) {
            System.out.println("IO Exception");
        }
    }

    //接收到客戶端的文本消息
    @OnWebSocketMessage
    public void onMessage(String message) {
        System.out.println("客戶端消息: " + message);
        try {
            WebSession.getRemote().sendString("客戶端,我收到這條消息了:" + message);
        } catch (IOException e) {
            System.out.println("IO Exception");
        }
    }
}

 

測試一下吧

 

客戶端運行結果:

 

完整源代碼下載 : source code

相關文章
相關標籤/搜索