A. WebSocketのはなぜ
HTTPプロトコルでは、すべての要求がクライアントによって開始され、サーバは、サーバがクライアントにメッセージをプッシュすることはできません、応答しますが、いくつかのインスタントメッセージングアプリケーションのニーズに、終了するサービスの必然的な要求がありますメッセージクライアントをプッシュし、従来のソリューションいくつかある
ポーリング
ポーリングは、クライアントが一定の時間間隔でサービス側を維持するためのリクエストを送信するという意味で、最も簡単な解決策、サービスの側面図であり、新しいデータがあり、サーバは新しいデータが、クライアントに返された新しいデータがない場合、サーバーは、空のJSONまたはXML文書が返され、開発者のためのポーリングを実装するために簡単ですが、欠点があれば明白:各クライアントは、そのため、真剣に、サーバの効率を遅くする、高度に同時シナリオでは、不正な多数の要求を処理するために、サーバーに新しいHTTPリクエストを作成する必要がありますが、サーバーのリソースが大幅に浪費されていますこのような望ましくないやり方と
2ロングポーリング
問題に対する部分的解の存在のための長いポーリングポーリングは、長いポーリングでは、サーバーで受信します これは、クライアントがクライアントに即座に応答しない場合は、要求に応答しますが、サーバーはすぐにクライアントからの新しいデータ要求を持つまで待つだろう、そうでない場合は、サーバーは新しいデータのみになるまで復帰せずに、この要求を開催しますリターン、ある程度この方法は、サーバーのリソースを節約できますが、のようないくつかの問題がある:
ブラウザがサービス応答の前に送信される新しいデータを持っている場合(1)は、唯一の新しい同時要求を作成することができ、または新しいリクエストを作成するには、現在の要求を遮断しようとする
(2)TCPとHTTP接続タイムアウト仕様が発言権を持っている、いわゆるロングポーリングが継続され、サーバ側とクライアントが定期的かつ緊密な接続を必要と接続することはできません、開発者の作業負荷を増加させる技術は、接続時間を延長することができ、これは主流の解決策ではない
3.AppletおよびFlash(棚脱落)
II。WebSocketの概要
W3Cの標準となっているとしてのWebSocketプロトコルは、単一のTCP接続を介して全二重通信である、そのような使用は、サーバーとクライアントのより容易な間のデータ交換をWebSocketのこと、それはクライアントにサーバーを積極的にプッシュすることができますデータは、WebSocketのプロトコルでは、ブラウザとサーバが唯一のハンドシェイクを完了する必要があり、次の2つの間の永続的な接続を作成し、双方向のデータ伝送できる
のWebSocketプロトコルHTTP / 1.1のアップグレード機能を使用して、WebSocketの要求が最初です特定のパターンでURLにアクセスするために、非通常のHTTPリクエストを使用して、URLは、それぞれ、2つのモードがあり、およびWSはWSS、対応するHTTPプロトコルHTTP及びHTTPS、接続要求ヘッダである:クライアントが望む示すフィールドアップグレード合意にアップグレードし、別のアップグレードがあります:WebSocketのフィールドは、要求がサーバーた場合、クライアントは、2つのフィールドが一緒にあなたは、このような全二重プロトコルへのWebSocket接続をアップグレードしているサーバーに伝えるために、WebSocketのプロトコル合意へのアップグレードを希望していることを示しています同意書のアップグレード、そして握手した後、テキストメッセージまたは他の バイナリのメッセージが同時にシャットダウンして再接続し、この時点で、クライアントは、サービスの関係は、彼らがそれぞれのイニシアチブを取る相手にメッセージを送ることができるようになっているエンド、そして伝統的な解決策もしなくても、両方向に送信することができます実施例と比較し、WebSocketのは、以下の特徴を有する:
(1)のWebSocketは、ステートフルプロトコルであることができるのWebSocket接続を使用する必要性を作成し、通路部の状態情報(例えば、認証、等)以降の処理を省略することができる
(2ポート80)のWebSocket接続(ファイアウォールの実質的に全てが用WebSocketの接続を遮断しないように同じポートに接続されているWS)または443(WSS)は、HTTPのために使用される
(3)HTTPプロトコル用WebSocketハンドシェイクを使用して、彼が直接統合することが可能ですWebブラウザとHTTPサーバに、余分なコスト
(4)ハートビートメッセージ(pingおよびポン)を繰り返し、一貫したのWebSocketを維持するために、アクティブ状態で、押し込まれていません
(5)プロトコルが開始またはメッセージが到着したときに、サーバとクライアントが知ることができる
(6)のWebSocket接続を閉鎖するために特別なメッセージを送る
(7)制限アヤックスを避けるために、クロスドメインをサポートするのWebSocket
( 8)HTTPの仕様では、ブラウザは、各ホスト名に対して2つの同時接続数の制限を接続する必要がありますが、我々はのWebSocketを使用する場合、及びハンドシェイクが完了したときに、接続がもはやHTTPであるので、制限は、存在しません。コネクタ
(9)のWebSocket拡張プロトコルをサポートし、ユーザは、カスタム・サブプロトコルの一部実装するプロトコルを拡張することができる
(10)バイナリより良いサポート、より良い圧縮
3つ。春ブートのWebSocketを統合
メッセージを送信する1.
以下の依存性を追加し、まず、春のブートプロジェクトを作成します。プロジェクトを作成します
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.webjars/webjars-locator-core -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
<version>0.35</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.webjars/sockjs-client -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.webjars/stomp-websocket -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3-1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.webjars/jquery -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1-1</version>
</dependency>
WebSocketの春ブーツ・スタータのWebSocketの時間依存相関依存性、他の人がフロントエンドライブラリは、これらのフロントエンドライブラリの一元管理の形でのjarパッケージを使用している、使用webjsrは春ブーツプロジェクトでデフォルトとなっているライブラリプロジェクトの先頭に追加しました加え「プラス静的リソースは、直接使用することができる
のWebSocket構成
プロトコルベースのスプリングフレームワークはSTOMPのWebSocketのサポートを提供し、ストンプは、単純な対話操作は、典型的には、中間サーバを介して非同期的にクライアントとの間で使用されます次のように構成されたメッセージング、WebSocketの:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
}
コードの説明:
(1)カスタムWebSocketConfigはWebSocketの構成のWebSocketMessageBrokerConfigurerから継承と@EnableWebSocketMessageBrokerのWebSocketメッセージ注釈プロキシによって開かれた
(2)registry.enableSimpleBrokerは(「/トピック 」) プレフィックスは、即ち、メッセージの接頭辞である場合、「/メッセージ・ブローカが提供されていることを示しトピック「プロキシ(ブローカー)にメッセージを転送し、次いで現在の接続でクライアントにメッセージ・ブローカによってブロードキャストメッセージであろう
(3)registry.setApplicationDestinationPrefixes(」/アプリ 「) 一つ以上のプレフィックスの構成を示し、これらのプレフィックスメッセージをフィルタリングする注釈付きメソッドを処理する必要がある
(4)registry.addEndpoint(「/チャット 」)。withSockJS()は『チャット/』 edPointプレフィックスの定義を表し、オープンsockjsサポート、sockjsブラウザ用WebSocketを解決することができ互換性、クライアントのWebSocket接続を確立するように構成されたURL
3.コントローラ定義
メッセージの処理を実装するために使用される定義されたコントローラ
@RestController
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Message greeting(Message message)throws Exception{
return message;
}
}
第二の構成部分によれば、この方法は、@メッセージを処理した後、送信された注釈メッセージ「/アプリ/ハロー」パスを受信するためのMessageMapping(「/ハロー」)、及び、注釈プロセスにメッセージを転送@ SendToパスを定義し、パスが@SendToパス「/トピック」であるので、メッセージは、メッセージブローカエージェントに委ね、その後、ブローカーによって放送される
。4.フロントページ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>单聊</title>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/chat.js"></script>
</head><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>群聊</title>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/app.js"></script>
</head>
<body>
<div>
<label for="name">请输入用户名:</label>
<input type="text" id="name" placeholder="用户名">
</div>
<div>
<button id="connect" type="button">连接</button>
<button id="disconnect" type="button" disabled="disabled">断开连接</button>
</div>
<div id="chat" style="display: none;">
<div>
<label for="name">请输入聊天内容:</label>
<input type="text" id="content" placeholder="聊天内容">
</div>
<button id="send" type="button">发送</button>
<div id="greetings">
<div id="conversation" style="display: none">群聊进行中...</div>
</div>
</div>
</body>
</html>
<body>
<div id="chat">
<div id="chatsContent"></div>
<div>
请输入聊天内容: <input type="text" id="content" placeholder="聊天内容">
目标用户: <input type="text" id="to" placeholder="目标用户">
<button id="send" type="button">发送</button>
</div>
</div>
</body>
</html>
5. JS論理ページとのWebSocket
var stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
$("#chat").show();
} else {
$("#conversation").hide();
$("#chat").hide();
}
$("#greetings").html("");
}
function connect() {
if (!$("#name").val()) {
return;
}
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
stompClient.subscribe('/topic/greetings', function(greeting) {
showGreeting(JSON.parse(greeting.body));
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
}
function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({
'name' : $("#name").val(),
'content' : $("#content").val()
}));
}
function showGreeting(message) {
$("#greetings").append(
"<div>" + message.name + ":" + message.content + "</div>");
}
$(function() {
$("#connect").click(function() {
connect();
});
$("#disconnect").click(function() {
disconnect();
});
$("#send").click(function() {
sendName();
});
});
符号の説明:
(1)接続方法は、WebSocketの接続を確立表すのWebSocket接続を確立するとき、ユーザは、ユーザ名を入力する必要があり、その後、接続を確立するために、
(2)メソッド本体の意味:使用SockJSが接続を確立し、その後、ストンプインスタンスを作成します要求を開始し、接続が成功したコールバックメソッド、最初の呼び出しsetConnected(真)であり、この方法は、ページを設定して、サブスクリプションサービスが戻ってメッセージを送信し、showGreetingを使用して(サーバーによって送信されたメッセージに見せ購読方法のSTOPMを呼び出します方法)
切断工程(3)はSTOMPが接続WebScoket切断でき呼び出さ
5エンティティクラスを
public class Message {
private String name;
private String content;
........getter,setter..............
}
6.改革メッセージコントローラ
コメントを@SendToを使用して送信されたメッセージは、コメントはメッセージの方法で処理された話すブローカーに転送され、その後、ブローカによってブロードキャスト、@SendTo注釈に加えて、春はまた、開発者はより柔軟にできるようにSimpMessagingTemplateクラスを提供していますメッセージを送信し、次のように変換した結果であり、コントローラ上に使用SimpMessagingTemlateために修飾することができます。
@RestController
public class GreetingController {
@Autowired
SimpMessagingTemplate simpMessagingTemplate;
@MessageMapping("/hello")
public void greeting(Message message)throws Exception{
simpMessagingTemplate.convertAndSend("/topic/greetings",message);
}
}
IV。ピアツーピアメッセージを送信します
。1. [追加の依存関係を
、それが送信をポイントツーポイントであるため、ユーザーの概念がなければならないので、次のように最初に、プロジェクト内の春のセキュリティを追加によって異なります。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
春のセキュリティの設定
、春のセキュリティを構成する二人のユーザーを追加し、すべてのアドレスを設定するには、次のコードにアクセスするための認証の後に、次のとおりです。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 密码加密过:123
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("齐**")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("admin")
.and()
.withUser("辛**")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("user")
.and()
.withUser("李**")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("user")
.and()
.withUser("岳**")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("user")
.and()
.withUser("尚**")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
}
}
3.変換用WebSocket構成コードは次の通りであります:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic","/queue");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
}
符号の説明:
ここで修飾registry.enableSimpleBroker(「/トピック」)は、ピアグループメッセージとメッセージ管理を容易にする、接頭辞「/キューを」追加
4.コントローラの構成
以下のように、コントローラのWebSocketの変換します:
@RestController
public class GreetingController {
@Autowired
SimpMessagingTemplate simpMessagingTemplate;
/**
* 消息群发
* @param message
* @return
* @throws Exception
*/
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Message greeting(Message message) throws Exception {
return message;
}
/**
* 点对点发送
* @param principal
* @param chat
* @throws Exception
*/
@MessageMapping("/chat")
public void chat(Principal principal, Chat chat) throws Exception {
String from=principal.getName();
chat.setFrom(from);
simpMessagingTemplate.convertAndSendToUser(chat.getTo(), "/queue/chat", chat);;
}
}
コードの説明:
(1)静止グループメッセージが達成され、ポイントはSimpMessagingTemplateによって達成される@SendTo使用
(2)(「/チャット」)@MessageMappingは「/アプリ/チャット」チャット処理経路からのメッセージは、メソッドになり、最初のパラメータが第二のパラメータ、ログオンしているユーザ電流に関する情報を取得するために使用される主要なチャット方法であってもよいことを示していますメッセージは、クライアントによって送信されます
(3)チャットの方法では、まず、現在ログオンしているユーザのユーザ名を取得チャットするオブジェクトのプロパティを設定し、メッセージを送信し、ターゲットは、チャット・プロパティに送られます。
方法(4)convertAndSendToUser、メソッドの内部を使用してメッセージがconvertAndSendメソッドを呼び出し、処理はメッセージパスを行います
メッセージのエンティティクラス:
public class Chat {
private String to;
private String from;
private String content;
........getter,setter省略............
}
コードの説明:
チャット属性ために、普通のJavaBeanですフォームは、コンテンツがメッセージの主な内容で、メッセージがどこから来たことを示し、対象ユーザのメッセージを示し
フロントページ6 - (onlinechat.htmlで作成)ページをチャット
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>单聊</title>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/chat.js"></script>
</head>
<body>
<div id="chat">
<div id="chatsContent"></div>
<div>
请输入聊天内容: <input type="text" id="content" placeholder="聊天内容">
目标用户: <input type="text" id="to" placeholder="目标用户">
<button id="send" type="button">发送</button>
</div>
</div>
</body>
</html>
5. JS論理ページとのWebSocket
var stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
$("#chat").show();
} else {
$("#conversation").hide();
$("#chat").hide();
}
$("#greetings").html("");
}
function connect() {
if (!$("#name").val()) {
return;
}
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
stompClient.subscribe('/topic/greetings', function(greeting) {
showGreeting(JSON.parse(greeting.body));
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
}
function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({
'name' : $("#name").val(),
'content' : $("#content").val()
}));
}
function showGreeting(message) {
$("#greetings").append(
"<div>" + message.name + ":" + message.content + "</div>");
}
$(function() {
$("#connect").click(function() {
connect();
});
$("#disconnect").click(function() {
disconnect();
});
$("#send").click(function() {
sendName();
});
});
コードの説明:
(1)接続が成功すると、加入者アドレス「/ユーザー/キュー/チャット」 、 アドレスより「/ユーザ」プレフィックスサーバ構成のアドレスよりも、SimpMessagingTemplateクラスが自動的にパスのプレフィックスを追加して、ため、
(2)チャットメッセージパス「/アプリケーションは/チャット」を送信する
(3)ユーザによって送信されたターゲット・メッセージを記述するために使用されるフィールドに送信されたメッセージの内容を