

<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>websocket &#8211; Max的程式語言筆記</title>
	<atom:link href="https://stackoverflow.max-everyday.com/tag/websocket/feed/" rel="self" type="application/rss+xml" />
	<link>https://stackoverflow.max-everyday.com</link>
	<description>我要當一個豬頭，快樂過每一天</description>
	<lastBuildDate>Tue, 31 Oct 2017 01:35:26 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/max-stackoverflow-256.png</url>
	<title>websocket &#8211; Max的程式語言筆記</title>
	<link>https://stackoverflow.max-everyday.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Notify only specific user(s) through WebSockets, when something is modified in the database</title>
		<link>https://stackoverflow.max-everyday.com/2017/10/notify-only-specific-users-through-websockets-when-something-is-modified-in-the-database/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/10/notify-only-specific-users-through-websockets-when-something-is-modified-in-the-database/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 31 Oct 2017 01:35:26 +0000</pubDate>
				<category><![CDATA[Java筆記]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[websocket]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1556</guid>

					<description><![CDATA[WebSocket lifecycle The ...]]></description>
										<content:encoded><![CDATA[<div class="content">
<div class="one-answer acptd-answer">
<h2>WebSocket lifecycle</h2>
<p>The WS session is tied to the context represented by the HTML document. The client is basically the JavaScript code. The WS session starts when JavaScript does <code>new WebSocket(url)</code>. The WS session stops when JavaScript explicitly invokes <code>close()</code>function on the <code>WebSocket</code> instance, or when the associated HTML document gets unloaded as result of a page navigation (clicking a link/bookmark or modifying URL in browser&#8217;s address bar), or a page refresh, or a browser tab/window close. Do note that you can create multiple <code>WebSocket</code> instances within the very same DOM, usually each with different URL path or query string parameters.</p>
<p>Each time when a WS session starts (i.e. each time when JavaScript does <code>var ws = new WebSocket(url);</code>), then this will fire a handshake request wherein you thus have access to the associated HTTP session via the below <a href="http://docs.oracle.com/javaee/7/api/javax/websocket/server/ServerEndpointConfig.Configurator.html" target="_blank" rel="nofollow noopener"><code>Configurator</code></a> class as you already found out:</p>
<pre class="hljs java"><code><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServletAwareConfigurator</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Configurator</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">modifyHandshake</span><span class="hljs-params">(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response)</span> </span>{
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        config.getUserProperties().put(<span class="hljs-string">"httpSession"</span>, httpSession);
    }

}
</code></pre>
<p>This is thus not called only once per HTTP session or HTML document as you seemed to expect. This is called every time a <code>new WebSocket(url)</code> is created.</p>
<p>Then a brand new instance of the <a href="http://docs.oracle.com/javaee/7/api/javax/websocket/server/ServerEndpoint.html" target="_blank" rel="nofollow noopener"><code>@ServerEndpoint</code></a> annotated class will be created and its <a href="http://docs.oracle.com/javaee/7/api/javax/websocket/OnOpen.html" target="_blank" rel="nofollow noopener"><code>@OnOpen</code></a> annotated method will be invoked. If you&#8217;re familiar with JSF/CDI managed beans, just treat that class as if it&#8217;s a <code>@ViewScoped</code> and the method as if it&#8217;s a <code>@PostConstruct</code>.</p>
<pre class="hljs java"><code><span class="hljs-meta">@ServerEndpoint</span>(value=<span class="hljs-string">"/push"</span>, configurator=ServletAwareConfigurator.class)
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PushEndpoint</span> </span>{

    <span class="hljs-keyword">private</span> Session session;
    <span class="hljs-keyword">private</span> EndpointConfig config;

    <span class="hljs-meta">@OnOpen</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onOpen</span><span class="hljs-params">(Session session, EndpointConfig config)</span> </span>{
        <span class="hljs-keyword">this</span>.session = session;
        <span class="hljs-keyword">this</span>.config = config;
    }

    <span class="hljs-meta">@OnMessage</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onMessage</span><span class="hljs-params">(String message)</span> </span>{
        <span class="hljs-comment">// ...</span>
    }

    <span class="hljs-meta">@OnError</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onError</span><span class="hljs-params">(Throwable exception)</span> </span>{
        <span class="hljs-comment">// ...</span>
    }

    <span class="hljs-meta">@OnClose</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClose</span><span class="hljs-params">(CloseReason reason)</span> </span>{
        <span class="hljs-comment">// ...</span>
    }

}
</code></pre>
<p>Note that this class is unlike e.g. a servlet not application scoped. It&#8217;s basically WS session scoped. So each new WS session gets its own instance. That&#8217;s why you can safely assign <code>Session</code> and <code>EndpointConfig</code> as an instance variable. Depending on the class design (e.g. abstract template, etc), you could if necessary add back <code>Session</code> as 1st argument of all those other <code>onXxx</code> methods. This is also supported.</p>
<p>The <a href="http://docs.oracle.com/javaee/7/api/javax/websocket/OnMessage.html" target="_blank" rel="nofollow noopener"><code>@OnMessage</code></a> annotated method will be invoked when JavaScript does <code>webSocket.send("some message")</code>. The <a href="http://docs.oracle.com/javaee/7/api/javax/websocket/OnClose.html" target="_blank" rel="nofollow noopener"><code>@OnClose</code></a> annotated method will be called when the WS session is closed. The exact close reason can if necessary be determined by close reason codes as available by <a href="http://docs.oracle.com/javaee/7/api/javax/websocket/CloseReason.CloseCodes.html" target="_blank" rel="nofollow noopener"><code>CloseReason.CloseCodes</code></a> enum. The <a href="http://docs.oracle.com/javaee/7/api/javax/websocket/OnError.html" target="_blank" rel="nofollow noopener"><code>@OnError</code></a>annotated method will be called when an exception is thrown, usually as an IO error on the WS connection (broken pipe, connection reset, etc).</p>
<h2>Collect WS sessions by logged-in user</h2>
<p>Coming back to your concrete functional requirement of notifying only specific users, you should after the above explanation understand that you can safely rely on <code>modifyHandshake()</code> to extract the logged-in user from the associated HTTP session, every time, provided that <code>new WebSocket(url)</code> is created <em>after</em> the user is logged-in.</p>
<pre class="hljs java"><code><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserAwareConfigurator</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Configurator</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">modifyHandshake</span><span class="hljs-params">(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response)</span> </span>{
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        User user = (User) httpSession.getAttribute(<span class="hljs-string">"user"</span>);
        config.getUserProperties().put(<span class="hljs-string">"user"</span>, user);
    }

}
</code></pre>
<p>Inside the WS endpoint class with a <code>@ServerEndpoint(configurator=UserAwareConfigurator.class)</code>, you can get hand of it in <code>@OnOpen</code> annotated method as below:</p>
<pre class="hljs cs"><code>@<span class="hljs-function">OnOpen
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onOpen</span>(<span class="hljs-params">Session session, EndpointConfig config</span>) </span>{
    User user = (User) config.getUserProperties().<span class="hljs-keyword">get</span>(<span class="hljs-string">"user"</span>);
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>You should collect them in the application scope. You can collect them in a <code>static</code>field of the endpoint class. Or, better, if CDI support in WS endpoint is not broken in your environment (works in WildFly, not in Tomcat+Weld, not sure about GlassFish), then simply collect them in an application scoped CDI managed bean which you in turn <code>@Inject</code> in the endpoint class.</p>
<p>When <code>User</code> instance is not <code>null</code> (i.e. when an user is logged in), then remember that an user can have multiple WS sessions. So, you&#8217;d basically need to collect them in a <code>Map&lt;User, Set&lt;Session&gt;&gt;</code> structure, or perhaps a more fine grained mapping which maps them by user ID or group/role instead, which after all allows easier finding specific users. It all depends on the final requirements. Here&#8217;s at least a kickoff example using an application scoped CDI managed bean:</p>
<pre class="hljs java"><code><span class="hljs-meta">@ApplicationScoped</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PushContext</span> </span>{

    <span class="hljs-keyword">private</span> Map&lt;User, Set&lt;Session&gt;&gt; sessions;

    <span class="hljs-meta">@PostConstruct</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">init</span><span class="hljs-params">()</span> </span>{
        sessions = <span class="hljs-keyword">new</span> ConcurrentHashMap&lt;&gt;();
    }

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">(Session session, User user)</span> </span>{
        sessions.computeIfAbsent(user, v -&gt; ConcurrentHashMap.newKeySet()).add(session);
    }

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">remove</span><span class="hljs-params">(Session session)</span> </span>{
        sessions.values().forEach(v -&gt; v.removeIf(e -&gt; e.equals(session)));
    }

}
</code></pre>
<pre class="hljs java"><code><span class="hljs-meta">@ServerEndpoint</span>(value=<span class="hljs-string">"/push"</span>, configurator=UserAwareConfigurator.class)
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PushEndpoint</span> </span>{

    <span class="hljs-meta">@Inject</span>
    <span class="hljs-keyword">private</span> PushContext pushContext;

    <span class="hljs-meta">@OnOpen</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onOpen</span><span class="hljs-params">(Session session, EndpointConfig config)</span> </span>{
        User user = (User) config.getUserProperties().get(<span class="hljs-string">"user"</span>);
        pushContext.add(session, user);
    }

    <span class="hljs-meta">@OnClose</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClose</span><span class="hljs-params">(Session session)</span> </span>{
        pushContext.remove(session);
    }

}
</code></pre>
<p>Finally you can send a message to specific user(s) as below in <code>PushContext</code>:</p>
<pre class="hljs java"><code><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">send</span><span class="hljs-params">(Set&lt;User&gt; users, String message)</span> </span>{
    Set&lt;Session&gt; userSessions;

    <span class="hljs-keyword">synchronized</span>(sessions) {
        userSessions = sessions.entrySet().stream()
            .filter(e -&gt; users.contains(e.getKey()))
            .flatMap(e -&gt; e.getValue().stream())
            .collect(Collectors.toSet());
    }

    <span class="hljs-keyword">for</span> (Session userSession : userSessions) {
        <span class="hljs-keyword">if</span> (userSession.isOpen()) {
            userSession.getAsyncRemote().sendText(message);
        }
    }
}
</code></pre>
<p>The <code>PushContext</code> being a CDI managed bean has the additional advantage that it&#8217;s injectable in any other CDI managed bean, allowing easier integration.</p>
<h2>Fire CDI event with associated users</h2>
<p>In your <code>EntityListener</code>, where you fire the CDI event most likely as per your previous related question <a href="https://stackoverflow.com/questions/25947790/real-time-updates-from-database-using-jsf-java-ee" target="_blank" rel="nofollow noopener">Real time updates from database using JSF/Java EE</a>, you already have the changed entity at hands and thus you should be able to find the users associated with it via their relationships in the model.</p>
<blockquote><p><em>Notify only the user who is responsible for modifying the entity in question (it may be an admin user or a registered user who can modify something only after their successful login)</em></p></blockquote>
<pre class="hljs java"><code><span class="hljs-meta">@PostUpdate</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onChange</span><span class="hljs-params">(Entity entity)</span> </span>{
    Set&lt;User&gt; editors = entity.getEditors();
    beanManager.fireEvent(<span class="hljs-keyword">new</span> EntityChangeEvent(editors));
}
</code></pre>
<blockquote><p><em>Notify only specific user/s (not all). &#8220;Specific&#8221; means for example, when a post is voted up on this site, only the post owner is notified (the post may be voted up by any other user with sufficient privileges).</em></p></blockquote>
<pre class="hljs java"><code><span class="hljs-meta">@PostUpdate</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onChange</span><span class="hljs-params">(Entity entity)</span> </span>{
    User owner = entity.getOwner();
    beanManager.fireEvent(<span class="hljs-keyword">new</span> EntityChangeEvent(Collections.singleton(owner)));
}
</code></pre>
<p>And then in the CDI event observer, pass it forth:</p>
<pre class="hljs cs"><code><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onEntityChange</span>(<span class="hljs-params">@Observes EntityChangeEvent <span class="hljs-keyword">event</span></span>) </span>{
    pushContext.send(<span class="hljs-keyword">event</span>.getUsers(), <span class="hljs-string">"message"</span>);
}
</code></pre>
<h3>See also:</h3>
<ul>
<li><a href="https://tools.ietf.org/html/rfc6455" target="_blank" rel="nofollow noopener">RFC6455 &#8211; The WebSocket Protocol</a> (describes <code>ws://</code> protocol)</li>
<li><a href="http://www.w3.org/TR/websockets/" target="_blank" rel="nofollow noopener">W3 &#8211; The WebSocket API</a> (describes JS <code>WebSocket</code> interface)</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications" target="_blank" rel="nofollow noopener">MDN &#8211; Writing WebSocket client application</a> (describes how to use WS API in client)</li>
<li><a href="https://docs.oracle.com/javaee/7/tutorial/websocket.htm#GKJIQ5" target="_blank" rel="nofollow noopener">Java EE 7 tutorial &#8211; WebSocket</a> (describes <code>javax.websocket</code> API and how to use it)</li>
</ul>
</div>
<blockquote><p>Recommend：<a href="http://www.itgo.me/a/5663091189026225635/real-time-updates-from-database-using-jsf-java-ee" target="_blank" rel="noopener">Real time updates from database using JSF/Java EE</a></p>
<p>ucts in the database. These menus are populated on every page load which is completely unnecessary. Some of these menus require complex/expensive JPA criteria queries. Currently the JSF managed beans that populate these menus are view scope</p></blockquote>
</div>
<div class="intro">
<p>oriUlr：<a href="http://stackoverflow.com/questions/32426674/notify-only-specific-users-through-websockets-when-something-is-modified-in-t">http://stackoverflow.com/questions/32426674/notify-only-specific-users-through-websockets-when-something-is-modified-in-t</a></p>
</div>
<hr />
<h4>相關文章：</h4>
<p>環境與關係注入：新的Java EE工具箱<br />
<a href="http://spirit-blog.logdown.com/posts/566157-contexts-and-dependency-injection-the-new-java-ee-toolbox">http://spirit-blog.logdown.com/posts/566157-contexts-and-dependency-injection-the-new-java-ee-toolbox</a></p>
<p>Java EE 7: Building Web Applications with WebSocket, JavaScript and HTML5<br />
<a href="http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/HomeWebsocket/WebsocketHome.html">http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/HomeWebsocket/WebsocketHome.html</a></p>
<p>Integrating CDI and WebSockets<br />
<a href="https://abhirockzz.wordpress.com/2015/02/10/integrating-cdi-and-websockets/">https://abhirockzz.wordpress.com/2015/02/10/integrating-cdi-and-websockets/</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/10/notify-only-specific-users-through-websockets-when-something-is-modified-in-the-database/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>在Android應用中使用哪個WebSocket套件？</title>
		<link>https://stackoverflow.max-everyday.com/2017/10/android-websocket-package/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/10/android-websocket-package/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 27 Oct 2017 02:21:08 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[Java筆記]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[websocket]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1543</guid>

					<description><![CDATA[我Google 了一下，找到很多個套件可以使用。...]]></description>
										<content:encoded><![CDATA[<p>我Google 了一下，找到很多個套件可以使用。</p>
<h1 class="public "><span class="author"><a class="url fn" href="https://github.com/koush" rel="author">koush</a></span><span class="path-divider">/</span><strong><a href="https://github.com/koush/AndroidAsync" data-pjax="#js-repo-pjax-container">AndroidAsync</a></strong></h1>
<p>目前這套件在 2017-10-27 的星星數目： 4673</p>
<hr />
<h1 class="public "><span class="author"><a class="url fn" href="https://github.com/TakahikoKawasaki" rel="author">TakahikoKawasaki</a></span><span class="path-divider">/</span><strong><a href="https://github.com/TakahikoKawasaki/nv-websocket-client" data-pjax="#js-repo-pjax-container">nv-websocket-client</a></strong></h1>
<p>目前這套件在 2017-10-27 的星星數目： 698</p>
<hr />
<h1 class="public "><span class="author"><a class="url fn" href="https://github.com/TooTallNate" rel="author">TooTallNate</a></span><span class="path-divider">/</span><strong><a href="https://github.com/TooTallNate/Java-WebSocket" data-pjax="#js-repo-pjax-container">Java-WebSocket</a></strong></h1>
<p>目前這套件在 2017-10-27 的星星數目： 3317</p>
<hr />
<p>上面 3個，照星星數來排，應該用 4000星的，我試了一下，範例沒辦法快速地套用上去，所以改用 3000星星的，3000星星的有附範例，是很快就可以用了，但問題是遇到自簽的SSL憑證就卡關了，雖然官方有附SSL範例，但弄了好幾個小時還是卡住，改用 700星星的，立馬就解決，也可以輕鬆地連到self-signed wss 伺服器，結論，星星多不一定比較好用，或著可能是功力太弱，無法快速地參悟別人設計上的用法。</p>
<p>&nbsp;</p>
<p class="p1"><span class="s1">現在似乎有一堆Java WebSocket庫，我不知道應該使用哪一個：</span></p>
<hr />
<p class="p1"><span class="s1"><a href="https://github.com/TooTallNate/Java-WebSocket"><span class="s2">TooTallNate/Java-WebSocket</span></a></span></p>
<p class="p1"><span class="s1">GitHub的描述：</span></p>
<blockquote><p>This repository contains a barebones WebSocket server and client implementation written in 100% Java. The underlying classes are implemented <code>java.nio</code>, which allows for a non-blocking event-driven model (similar to the<a href="http://dev.w3.org/html5/websockets/">WebSocket API</a> for web browsers).</p>
<p>Implemented WebSocket protocol versions are:</p>
<ul>
<li><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a></li>
</ul>
<p><a href="https://github.com/TooTallNate/Java-WebSocket/wiki/Drafts">Here</a> some more details about protocol versions/drafts.</p></blockquote>
<hr />
<p class="p1"><span class="s1"><a href="https://github.com/koush/AndroidAsync"><span class="s2">koush/AndroidAsync</span></a></span></p>
<p class="p1">GitHub的描述：</p>
<blockquote><p>AndroidAsync is a low level network protocol library. If you are looking for an easy to use, higher level, Android aware, http request library, check out <a href="https://github.com/koush/ion">Ion</a> (it is built on top of AndroidAsync). The typical Android app developer would probably be more interested in Ion.</p>
<p>But if you&#8217;re looking for a raw Socket, HTTP client/server, WebSocket, and Socket.IO library for Android, AndroidAsync is it.</p>
<h4><a id="user-content-features" class="anchor" href="https://github.com/koush/AndroidAsync#features" aria-hidden="true"></a>Features</h4>
</blockquote>
<ul>
<li>
<blockquote><p>Based on NIO. One thread, driven by callbacks. Highly efficient.</p></blockquote>
</li>
<li>
<blockquote><p>All operations return a Future that can be cancelled</p></blockquote>
</li>
<li>
<blockquote><p>Socket client + socket server</p></blockquote>
</li>
<li>
<blockquote><p>HTTP client + server</p></blockquote>
</li>
<li>
<blockquote><p>WebSocket client + server</p></blockquote>
</li>
<li>
<blockquote><p>Socket.IO client</p></blockquote>
</li>
</ul>
<hr />
<p class="p1"><span class="s1"><span class="s2">Jetty WebSocket Client API<br />
<a href="https://www.eclipse.org/jetty/documentation/9.3.x/index.html">https://www.eclipse.org/jetty/documentation/9.3.x/index.html</a></span></span></p>
<p class="p1"><span class="s1">Jetty還提供了一個Jetty WebSocket客戶端庫來編寫與WebSocket服務器交談更容易。</span></p>
<hr />
<p class="p1"><span class="s1"><a href="https://github.com/Atmosphere/wasync"><span class="s2">Atmosphere/wasync</span></a></span></p>
<p><span class="s1">GitHub的描述：</span></p>
<blockquote><p>wAsync is a Java based library allowing asynchronous communication with any WebServer supporting the WebSocket or Http Protocol. wAsync can be used with Node.js, Android, Atmosphere or any WebSocket Framework. To get started, read this super simple <a href="https://github.com/Atmosphere/wasync/wiki/Getting-Started-with-wAsync">Tutorial</a> or read the <a href="https://github.com/Atmosphere/wasync/wiki/FAQ">FAQ</a></p>
<p>You can browse the <a href="http://atmosphere.github.com/wasync/apidocs/">javadoc</a> or browse our <a href="https://github.com/Atmosphere/atmosphere-samples/tree/master/wasync-samples">samples</a>.</p></blockquote>
<hr />
<p class="p1"><span class="s1"> <a href="https://github.com/TakahikoKawasaki/nv-websocket-client"><span class="s2">TakahikoKawasaki/nv-websocket-client</span></a></span></p>
<p>GitHub的描述：</p>
<blockquote><p>High-quality WebSocket client implementation in Java which</p></blockquote>
<ul>
<li>
<blockquote><p>complies with <a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> (The WebSocket Protocol),</p></blockquote>
</li>
<li>
<blockquote><p>works on Java SE 1.5+ and Android,</p></blockquote>
</li>
<li>
<blockquote><p>supports all the frame types (continuation, binary, text, close, ping and pong),</p></blockquote>
</li>
<li>
<blockquote><p>provides a method to send a fragmented frame in addition to methods for unfragmented frames,</p></blockquote>
</li>
<li>
<blockquote><p>provides a method to get the underlying raw socket of a WebSocket to configure it,</p></blockquote>
</li>
<li>
<blockquote><p>provides a method for <a href="http://tools.ietf.org/html/rfc2617">Basic Authentication</a>,</p></blockquote>
</li>
<li>
<blockquote><p>provides a factory class which utilizes javax.net.SocketFactory interface,</p></blockquote>
</li>
<li>
<blockquote><p>provides a rich listener interface to hook WebSocket events,</p></blockquote>
</li>
<li>
<blockquote><p>has fine-grained error codes for fine-grained controllability on errors,</p></blockquote>
</li>
<li>
<blockquote><p>allows to disable validity checks on RSV1/RSV2/RSV3 bits and opcode of frames,</p></blockquote>
</li>
<li>
<blockquote><p>supports HTTP proxy, especially &#8220;Secure WebSocket&#8221; (<code>wss</code>) through &#8220;Secure Proxy&#8221; (<code>https</code>),</p></blockquote>
</li>
<li>
<blockquote><p>and supports <a href="http://tools.ietf.org/html/rfc7692">RFC 7692</a> (Compression Extensions for WebSocket), also known as <em>permessage-deflate</em> (not enabled by default).</p></blockquote>
</li>
</ul>
<hr />
<p class="p1"><span class="s1"><a href="https://github.com/square/okhttp"><span class="s2">square/okhttp</span></a></span></p>
<p>GitHub的描述：</p>
<blockquote><p>An HTTP &amp; HTTP/2 client for Android and Java applications. For more information see <a href="http://square.github.io/okhttp">the website</a> and <a href="https://github.com/square/okhttp/wiki">the wiki</a>.</p></blockquote>
<hr />
<p class="p1"><span class="s1"><a href="https://github.com/firebase/TubeSock"><span class="s2">firebase/TubeSock</span></a></span></p>
<p>GitHub的描述：</p>
<blockquote><p>TubeSock is a Java implementation of the client side of the WebSocket Protocol for use in Java applications.</p></blockquote>
<hr />
<p class="p1"><span class="s1"><a href="http://autobahn.ws/android/"><span class="s2">Autobahn|Android</span></a>(<a href="https://github.com/crossbario/autobahn-android"><span class="s2">GitHub</span></a>)</span></p>
<p class="p1">GitHub的描述：</p>
<blockquote><p>Client library providing <a href="http://wamp-proto.org/">WAMP</a> on Java 8 (<a href="https://netty.io/">Netty</a>) and Android, plus (secure) WebSocket for Android.</p>
<p><a href="https://hub.docker.com/r/crossbario/autobahn-java/"><img decoding="async" src="https://camo.githubusercontent.com/2339383c51e6dc70773bd95044b17f14d24e1db6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646f636b65722d72656164792d626c75652e737667" alt="Docker Hub" data-canonical-src="https://img.shields.io/badge/docker-ready-blue.svg" /></a> | <a href="https://travis-ci.org/crossbario/autobahn-java"><img decoding="async" src="https://camo.githubusercontent.com/f353593d9392cc0153c1825bc7bcf43a92113686/68747470733a2f2f7472617669732d63692e6f72672f63726f7373626172696f2f6175746f6261686e2d6a6176612e7376673f6272616e63683d6d6173746572" alt="Travis" data-canonical-src="https://travis-ci.org/crossbario/autobahn-java.svg?branch=master" /></a> <a href="https://javadoc.io/doc/io.crossbar.autobahn/autobahn-android"><img decoding="async" src="https://camo.githubusercontent.com/396ede1f3052c9b0c8fc311d4d7193635e06ee6f/68747470733a2f2f6a617661646f632e696f2f62616467652f696f2e63726f73736261722e6175746f6261686e2f6175746f6261686e2d616e64726f69642e7376673f6c6162656c3d646f6373" alt="Docs" data-canonical-src="https://javadoc.io/badge/io.crossbar.autobahn/autobahn-android.svg?label=docs" /></a></p>
<hr />
<p>Autobahn|Java is a subproject of the <a href="http://crossbar.io/autobahn/">Autobahn project</a> and provides open-source client implementations for</p>
<ul>
<li><strong><a href="http://tools.ietf.org/html/rfc6455">The WebSocket Protocol</a></strong></li>
<li><strong><a href="http://wamp-proto.org/">The Web Application Messaging Protocol (WAMP)</a></strong></li>
</ul>
<p>running on Android and Netty/Java8/JVM.</p>
<p>The WebSocket layer is using a callback based user API, and is specifically written for Android. Eg it does not run any network stuff on the main (UI) thread.</p>
<p>The WAMP layer is using Java 8 <strong><a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html">CompletableFuture</a></strong> for WAMP actions (call, register, publish and subscribe) and the <strong><a href="https://en.wikipedia.org/wiki/Observer_pattern">Observer pattern</a></strong> for WAMP session, subscription and registration lifecycle events.</p>
<p>The library is MIT licensed, maintained by the Crossbar.io Project, tested using the AutobahnTestsuite and published as a JAR to Maven and as a Docker toolchain image to Dockerhub.</p></blockquote>
<hr />
<p class="p1"><span class="s1"><a href="https://github.com/nkzawa/socket.io-client.java"><span class="s2">nkzawa/socket.io-client.java</span></a></span></p>
<p class="p1"><span class="s1">GitHub的描述：</span></p>
<blockquote><p>This is the Socket.IO v1.x Client Library for Java, which is simply ported from the <a href="https://github.com/socketio/socket.io-client">JavaScript client</a>.</p>
<p>See also:</p>
<ul>
<li><a href="https://github.com/nkzawa/socket.io-android-chat">Android chat demo</a></li>
<li><a href="https://github.com/socketio/engine.io-client-java">engine.io-client-java</a></li>
</ul>
</blockquote>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/10/android-websocket-package/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WebSocket &#8211; Tomcat  簡單的聊天室</title>
		<link>https://stackoverflow.max-everyday.com/2017/10/websocket-tomcat-chatroom/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/10/websocket-tomcat-chatroom/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 26 Oct 2017 03:41:50 +0000</pubDate>
				<category><![CDATA[Java筆記]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[websocket]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1533</guid>

					<description><![CDATA[在 Tomcat 裡實作 websocket 滿...]]></description>
										<content:encoded><![CDATA[<p>在 Tomcat 裡實作 websocket 滿簡單的。</p>
<p>WebSocket可以在伺服器和瀏覽器之間建立雙向通訊，今天要來用WebSocket來實現一個簡單的聊天室做範例，Tomcat 伺服器的最小版本要求是 Tomcat 7, 目前我是使用 Tomcat 7.</p>
<p>附註1：如果你是走 ssl 需要把 ws:// 修改為 wss://<br />
附註2：如果是 html 去存取別台伺服器的 wss, 如果是自簽的憑證會有問題。<br />
附註3：不需要額外修改 tomcat 的 server.xml 或 web.xml</p>
<p>&nbsp;</p>
<hr />
<p>範例1：</p>
<p>==================<br />
webSocketTest.html ：</p>
<pre>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;WebSocket Test&lt;/title&gt;
        &lt;meta charset="UTF-8"&gt;
        &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
        &lt;script src="js/webSocket.js"&gt;&lt;/script&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div&gt;
            &lt;form id="chatRoomForm" onsubmit="return false;"&gt;
                聊天室
                名字: &lt;input type="text" id="userNameInput" /&gt; 
 
                &lt;input type="button" id="loginBtn" value="登入" /&gt; &lt;span id="infoWindow"&gt;&lt;/span&gt;
 
                &lt;input type="text" id="userinput" /&gt; 
  
                &lt;input type="submit" value="送出訊息" /&gt;
            &lt;/form&gt;
        &lt;/div&gt;
        &lt;div id="messageDisplay"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>
<hr />
<p>webSocket.js ：</p>
<pre>window.onload = function () {
    //獲取DOM元件
    var loginBtn = document.getElementById("loginBtn");
    var userNameInput = document.getElementById("userNameInput");
    var infoWindow = document.getElementById("infoWindow");
    var userinput = document.getElementById("userinput");
    var chatRoomForm = document.getElementById("chatRoomForm");
    var messageDisplay = document.getElementById("messageDisplay");
 
    var webSocket;
    var isConnectSuccess = false;
 
    //設置登入鈕的動作，沒有登出，登入才可發言
    loginBtn.addEventListener("click", function () {
        //檢查有無輸入名稱
        if (userNameInput.value &amp;&amp; userNameInput.value !== "") {
            setWebSocket();  //設置WebSocket連接
        } else {
            infoWindow.innerHTML = "請輸入名稱";
        }
 
    });
    //Submit Form時送出訊息
    chatRoomForm.addEventListener("submit", function () {
        sendMessage();
        return false;
    });
    //使用webSocket擁有的function, send(), 送出訊息
    function sendMessage() {
        //檢查WebSocket連接狀態
        if (webSocket &amp;&amp; isConnectSuccess) {
            var messageInfo = {
                userName: userNameInput.value,
                message: userinput.value
            }
            webSocket.send(JSON.stringify(messageInfo));
        } else {
            infoWindow.innerHTML = "未登入";
        }
    }
 
    //設置WebSocket
    function setWebSocket() {
        //開始WebSocket連線
        webSocket = new WebSocket('ws://localhost:8080/WebSocketTest/websocket');
        //以下開始偵測WebSocket的各種事件
         
        //onerror , 連線錯誤時觸發  
        webSocket.onerror = function (event) {
            loginBtn.disabled = false;
            userNameInput.disabled = false;
            infoWindow.innerHTML = "登入失敗";
        };
 
        //onopen , 連線成功時觸發
        webSocket.onopen = function (event) {
            isConnectSuccess = true;
            loginBtn.disabled = true;
            userNameInput.disabled = true;
            infoWindow.innerHTML = "登入成功";
             
            //送一個登入聊天室的訊息
            var firstLoginInfo = {
                userName : "系統",
                message : userNameInput.value + " 登入了聊天室"
            };
            webSocket.send(JSON.stringify(firstLoginInfo));
        };
 
        //onmessage , 接收到來自Server的訊息時觸發
        webSocket.onmessage = function (event) {
            var messageObject = JSON.parse(event.data);
            messageDisplay.innerHTML += "
" + messageObject.userName + " 說 : " + messageObject.message;
        };
    }
};
</pre>
<hr />
<p>WebSocketEndpointTest.java：</p>
<pre>import java.io.IOException;
import java.util.ArrayList;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
 
@ServerEndpoint("/websocket")
public class WebSocketEndpointTest {
    //用來存放WebSocket已連接的Socket
    static ArrayList&lt;Session&gt; sessions;
 
    @OnMessage
    public void onMessage(String message, Session session) throws IOException,
            InterruptedException, EncodeException {
        System.out.println("User input: " + message);
        //session.getBasicRemote().sendText("Hello world Mr. " + message);
        //for (Session s : session.getOpenSessions()) {
        for (Session s : sessions) {    //對每個連接的Client傳送訊息
            if (s.isOpen()) {
                s.getBasicRemote().sendText(message);
            }
        }
    }
 
    @OnOpen
    public void onOpen(Session session) {
        //紀錄連接到sessions中
        System.out.println("Client connected");        
        if (sessions == null) {
            sessions = new ArrayList&lt;Session&gt;();
        }
        sessions.add(session);
        System.out.println("Current sessions size: " + sessions.size());
    }
 
    @OnClose
    public void onClose(Session session) {
        //將連接從sessions中移除
        System.out.println("Connection closed");
        if (sessions == null) {
            sessions = new ArrayList&lt;Session&gt;();
        }
        sessions.remove(session);
        System.out.println("Current sessions size: " + sessions.size());
    }
}</pre>
<hr />
<p>範例 2號，我是用這組來修改。</p>
<p>&nbsp;</p>
<p>我们需要2个jar包：</p>
<ul>
<li>tomcat7-websocket.jar</li>
<li>websocket-api.jar</li>
</ul>
<p>這两个jar包是在tomcat7及以上版本的lib目录下获得的。主要是封装了websocket的API方法类以及实现了其中的方法。</p>
<p>当跳转到chat.jsp页面的时候，会加载其中的js文件，js中会开启一个通信管道。页面加载判断是否已经开启了这个通道，如果没有开启，就开启。当管道开启的时候就会触发onopen事件。触发的方法在ChatSocket类中。</p>
<p>chat.jsp：</p>
<pre>&lt;body&gt;
  &lt;script src="../js/jquery-3.2.1.min.js"&gt;&lt;/script&gt;
  &lt;script src="../js/popper.min.js"&gt;&lt;/script&gt;
  &lt;script src="../js/bootstrap.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
        var ws; //一个ws对象就是一个通信管道！！，只要不关浏览器，不关闭服务器就一直开着
        var target="wss://192.168.1.215:8443/Core/WsPushServer?username=max32002";
        $().ready(function connect() {

                 //页面加载判断是否已经开启了target这个通道，如果没有开启，就开启
                 if ('WebSocket' in window) {
                     ws = new WebSocket(target);
                 } else if ('MozWebSocket' in window) {
                     ws = new MozWebSocket(target);
                 } else {
                     alert('WebSocket is not supported by this browser.');
                     return;
                 }


                //onerror , 連線錯誤時觸發  
                ws.onerror = function (event) {
                    alert("登入失敗");
                };

                 //接收消息 
                 ws.onmessage = function (event) {
                    eval("var result="+event.data);

                    if(result.alert!=undefined){
                        $("#content").append(result.alert+"&lt;br/&gt;");
                    }

                    if(result.names!=undefined){
                        $("#userList").html("");
                        $(result.names).each(function(){
                            $("#userList").append(this+"&lt;br/&gt;");
                        });
                    }

                    if(result.from!=undefined){
                        $("#content").append(result.from+" "+result.date+
                                " 说：&lt;br/&gt;"+result.sendMsg+"&lt;br/&gt;");
                    }

                 };
             });


        //点击发送消息触发事件
        function send(){
            var msg = $("#msg").val();
            ws.send(msg);
            $("#msg").val("");
        }
    &lt;/script&gt;


    &lt;h3&gt;欢迎 max32002 使用本系统！！&lt;/h3&gt;

    &lt;div id="content"
        style="
        border: 1px solid black; width: 400px; height: 300px;
        float: left;
    "&gt;&lt;/div&gt;
    &lt;div id="userList"
        style="
        border: 1px solid black; width: 100px; height: 300px;
        float:left;
    "&gt;&lt;/div&gt;

    &lt;div style="clear: both;"&gt;
        &lt;input id="msg" /&gt;
        &lt;button onclick="send();"&gt;send&lt;/button&gt;
    &lt;/div&gt;

&lt;/body&gt;
</pre>
<hr />
<p>Java:</p>
<pre>import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.google.gson.Gson;

@ServerEndpoint("/WsPushServer")
public class WsPushServer {
    private  static  Set&lt;WsPushServer&gt;  sockets=new HashSet&lt;WsPushServer&gt;();
    private  static  List&lt;String&gt;   nameList=new ArrayList&lt;String&gt;();
    private  Session  session;
    private String username;
    private Gson  gson=new Gson();
    
    //只要有人连接这个服务，就会打开，执行下面的方法。
    @OnOpen
    public void open(Session session){
        //一个session就代表一个通信会话
        System.out.println("sessionid:"+session.getId()+"通道开启了。。。。");
        //把session添加到容器中
        this.session=session;
        sockets.add(this);

        //getQueryString把url中？后面的所有的串儿都取出来
        String QueryString = session.getQueryString();
        System.out.println(QueryString);
        
        //获取用户名
        this.username = QueryString.substring(QueryString.indexOf("=")+1);
        nameList.add(username);

        String message = (username+"进入聊天室！！");

        broadcast(sockets, gson.toJson(message) );
    }
    
    //退出
    @OnClose
    public void close(Session session){
        //1.清理退出的session
        sockets.remove(this);
        //2.清理列表用户名
        nameList.remove(this.username);
        //3.更新消息信息
        String message=(this.username+"退出聊天室！！");
        //4.广播消息信息
        broadcast(sockets, gson.toJson(message));
    }
    
    @OnError
    public void onError(Throwable e){
            e.printStackTrace();
    }

    //收
    @OnMessage
    /** * * @param session * @param msg 从客户端接收的消息 */
    public void message(Session session,String msg){
        //接收消息

        String  message=(this.username + ":" + msg + ":" + new Date().toString());
        broadcast(sockets, gson.toJson(message));

    }
    
    /** * 广播消息 * @param ss 用户session * @param msg 广播消息 */
    public void broadcast(Set&lt;WsPushServer&gt;  ss ,String msg ){

        for (Iterator&lt;WsPushServer&gt; iterator = ss.iterator(); iterator.hasNext();) {
                WsPushServer chatSocket = (WsPushServer) iterator.next();
            try {
                chatSocket.session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}</pre>
<hr />
<p>&nbsp;</p>
<h4>相關文章：</h4>
<ul>
<li><a href="http://blog.csdn.net/kisscatforever/article/details/72417091">http://blog.csdn.net/kisscatforever/article/details/72417091</a></li>
<li><a href="http://hklifenote.blogspot.tw/2017/02/websocket-tomcat-v80.html">http://hklifenote.blogspot.tw/2017/02/websocket-tomcat-v80.html</a></li>
<li><a href="https://github.com/yuechang/webSocket">https://github.com/yuechang/webSocket</a></li>
<li><a href="http://www.itread01.com/articles/1478584824.html">http://www.itread01.com/articles/1478584824.html</a></li>
<li><a href="https://examples.javacodegeeks.com/enterprise-java/tomcat/apache-tomcat-websocket-tutorial/">https://examples.javacodegeeks.com/enterprise-java/tomcat/apache-tomcat-websocket-tutorial/</a></li>
<li><a href="http://www.jianshu.com/p/c57b0055dc20">http://www.jianshu.com/p/c57b0055dc20</a></li>
<li><a href="http://colobu.com/2015/02/27/WebSockets-tutorial-on-Wildfly-8/">http://colobu.com/2015/02/27/WebSockets-tutorial-on-Wildfly-8/</a></li>
</ul>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/10/websocket-tomcat-chatroom/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
