版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/popboy100/article/details/85049569
Kurento V6.6.1-dev
本文档描述了Kurento项目中JSON-RPC客户端与服务端的实现。对于WebSocket协议的详细介绍超出了本文档的范围。但是,至少必须了解,HTTP仅用于初始握手阶段,而初始握手阶段依赖于HTTP的内置机制,去请求协议升级(或在本例中称为协议切换),如果服务器同意,则服务器响应的HTTP状态为101(正在切换协议)。假设握手成功,HTTP升级请求的底层TCP Socket将保持打开状态,客户端和服务端都可以通过Socket彼此发送消息。有关协议本身的信息,请参阅项目文档中的此页面。
如JSON-RPC v2.0规范所描述的那样,该协议意味着存在一个发出请求的客户端,并且存在一个处理这些请求的服务端。这与v1.0截然相反,后者使用的是Peer-to-Peer架构,其中两个Peer既是客户端又是服务端。
Kurento已经实现了Java版本的JSON-RPC服务端,还有使用Java和Javascript实现的客户端。所有实现都托管在GitHub上:
Java实现包含具有以下模块的Maven项目:
Javascript实现包括:
这是JAVA实现的JSON-RPC服务端。它只支持v2.0,这意味着可以使用通知。然而,唯一可用的传输协议是WebSocket。它作为Maven artifact发布,允许开发人员简单易用的管理它作为依赖项,通过在其项目的pom中包含以下依赖项:
<dependency>
<groupId>org.kurento</groupId>
<artifactId>kurento-jsonrpc-server</artifactId>
<version>6.6.1-SNAPSHOT</version>
</dependency>
服务端基于Spring Boot 1.3.0.RELEASE,使用非常简单,类似于Spring中WebSocketHandler的创建和配置。它基本上由服务器的配置和一个实现接收请求的处理类组成。下面的代码为JSON-RPC请求实现了一个处理,其中包含一个JsonObject作为参数数据类型。此处理将接收到的参数发回给客户端。由于请求处理总是返回一个响应,如果程序员没有故意这样做,库将自动发送一个空响应。在下面的示例中,如果请求没有调用echo方法,它将返回一个空响应:
import org.kurento.jsonrpc.DefaultJsonRpcHandler;
import org.kurento.jsonrpc.Transaction;
import org.kurento.jsonrpc.message.Request;
import com.google.gson.JsonObject;
public class EchoJsonRpcHandler extends DefaultJsonRpcHandler<JsonObject> {
@Override
public void handleRequest(Transaction transaction,
Request<JsonObject> request) throws Exception {
if ("echo".equalsIgnoreCase(request.getMethod())) {
transaction.sendResponse(request.getParams());
}
}
}
该方法的第一个参数是Transaction,它表示客户端和服务端之间的消息交换。Transaction可用的方法(不包括重载),以及不同用法如下:
在方法handleRequest中,开发人员可以访问JSON-RPC请求(method、params、id或jsonrpc)的任何字段。这就是管理调用方法的地方。除了在该类中处理的方法外,服务端还处理以下特殊method值:
***DefaultJsonRpcHandler***类是根据请求附带的有效负载进行泛化的。在前面的代码中,预期的有效负载是JsonObject,但也可以是普通字符串或任何其他对象。
为了使用Handler去配置基于WebSocket的JSON-RPC服务端,开发人员可以使用JsonRpcConfiguration,将上述WebSocket处理映射到特定的URL(本例中为http://localhost:8080/echo):
import org.kurento.jsonrpc.internal.server.config.JsonRpcConfiguration;
import org.kurento.jsonrpc.server.JsonRpcConfigurer;
import org.kurento.jsonrpc.server.JsonRpcHandlerRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
@Import(JsonRpcConfiguration.class)
public class EchoServerApplication implements JsonRpcConfigurer {
@Override
public void registerJsonRpcHandlers(JsonRpcHandlerRegistry registry) {
registry.addHandler(new EchoJsonRpcHandler(), "/echo"); // “/echo” is the path relative to the server’s URL
}
}
连接到此服务器每个客户端都将被分配一个唯一的sessionId。这提供了session会话概念,可用通过该方式扩展为多个WebSocket会话。JSON-RPC会话,允许将一组属性绑定到一个特定的会话上。这使开发人员能够实现具有状态服务会话服务端的能力,这样用户可以在重连后恢复会话。在会话对象中有如下可用的方法:
注册handler时,有许多特性是可以配置的。这些可以通过DefaultJsonRpcHandler中的API进行设置:
该handler重写某些与连接事件相关的方法,现有的方法如下:
import org.kurento.jsonrpc.DefaultJsonRpcHandler;
import com.google.gson.JsonObject;
public class EchoJsonRpcHandler extends DefaultJsonRpcHandler<JsonObject> {
// ...
@Override
public void afterConnectionEstablished(Session session) throws Exception {
// Do something useful here
}
@Override
public void afterConnectionClosed(Session session, String status)
throws Exception {
// Do something useful here
}
@Override
public void handleTransportError(Session session, Throwable exception)
throws Exception {
// Do something useful here
}
@Override
public void handleUncaughtException(Session session, Exception exception) {
// Do something useful here
}
}
Notification是一个没有“id”成员的Request对象。作为通知的Request对象表示发送方对相应的Response对象不感兴趣,因此不需要返回响应对象。
根据定义,Notification是不可确认的,因为它们没有要返回的Response对象。因此,发送者不会发现任何错误(比如:“无效参数”、“内部错误”)。
服务器可以通过持有的session对象向连接的客户端发送通知。为此,在连接时需要存储每个客户端的Session对象。这可以通过重写handler的***afterConnectionEstablished ***方法来实现。
public class EchoJsonRpcHandler extends DefaultJsonRpcHandler<JsonObject> {
public final Map<String, Session> sessions = new HashMap<>();
@Override
public void afterConnectionEstablished(Session session) {
String clientId = (String) session.getAttributes().get("clientId");
sessions.put(clientId, session);
}
@Override
public void afterConnectionClosed(Session session, String status)
throws Exception {
String clientId = (String) session.getAttributes().get("clientId");
sessions.remove(clientId);
}
// Other methods
}
会话如何与每个客户端配对取决于应用程序的业务逻辑。在本例中,我们假设会话包含一个clientId属性,该属性可用于唯一标识每个客户端。还可以使用sessionId,库将:term:UUID作为会话标识符,但它们对使用该库的应用程序没有意义。当客户端断开连接时,最好不要遗留已注册的会话,因此我们重写PostConnectionClosed方法,并在那里删除存储的Session对象。
通知通过已建立的会话发送给连接的客户端。同样,如何将会话映射到客户端,这超出了本文档的范围,因为它取决于应用程序的业务逻辑。假设handler对象位于相同的范围内,下面的代码片段将显示如何向特定客户端发送通知:
public void sendNotification(String clientId, String method, Object params)
throws IOException {
handler.sessions.get(clientId).sendNotification(method, params);
}
这是kurento-jsonrpc-server的Java客户端,或者其他任何实现JSON-RPC协议的WebSocket服务器。它允许Java程序通过JSON-RPC调用kurento-jsonrpc-server。也被发布为Maven的依赖项,可以将其添加到项目的pom:
<dependency>
<groupId>org.kurento</groupId>
<artifactId>kurento-jsonrpc-server</artifactId>
<version>6.6.1-SNAPSHOT</version>
</dependency>
与服务器相反,客户端与框架无关,因此它可以用于常规的java应用程序、java EE、Spring…。创建一个向服务端发送请求的客户端是非常简单的。将服务器的URI传递给JsonRpcClientWebSocket的构造函数,这里假设它部署在同一台机器上:
JsonRpcClient client = new JsonRpcClientWebSocket("ws://localhost:8080/echo");
JSON-RPC调用是通过向服务器发送Request对象来表示的。该对象具有以下成员:
以上这些成员中,用户只需设置“method”和“params”,因为其他两个由底层库管理。
上一节中定义的服务端需要一个JsonObject对象,并且只响应echo方法,返回请求中的“params”。对client.sendRequest(request)响应的***params***被包裹在Response对象中发送回客户端:
Request<JsonObject> request = new Request<>();
request.setMethod("echo");
JsonObject params = new JsonObject();
params.addProperty("some property", "Some Value");
request.setParams(params);
Response<JsonElement> response = client.sendRequest(request);
Notification是一个没有“id”成员的Request对象。作为Notfication的Request对象表示客户端对相应的Response对象不感兴趣,因此不需要将Response对象返回给客户端。根据定义,通知是不可确认的,因为它们没有要返回的Response对象。因此,客户端不会发现到任何错误(例如:“无效参数”、“内部错误”):
client.sendNotification("echo");
当服务器接收到RPC调用时,它将回应一个Response,但Notification除外。Response表示为单个JSON对象,成员如下:
Response将有“result”或“error”成员,但两者不能同时存在。
当RPC调用遇到错误时,Response对象包含Error成员对象,该成员对象具有以下成员:
客户端提供了为某些连接事件设置侦听器的功能。用户可以定义一个JsonRpcWSConnectionListener,它提供对某些方法的重写。一旦定义了连接侦听器,就可以传递给客户端的构造函数,一旦产生相应的事件,客户端将调用这些方法:
JsonRpcWSConnectionListener listener = new JsonRpcWSConnectionListener() {
@Override
public void reconnected(boolean sameServer) {
// ...
}
@Override
public void disconnected() {
// ...
}
@Override
public void connectionFailed() {
// ...
}
@Override
public void connected() {
// ...
}
} ;
JsonRpcClient client = new JsonRpcClientWebSocket("ws://localhost:8080/echo", listener);
正如服务端中所指出的,有一种心跳机制会定期发送ping消息。客户端可以通过以下方法进行控制:
不仅ping消息的间隔时间是可配置的。其他超时时间也是配置的:
这是kurento-jsonrpc-server的Javascript客户端,或者任何其他实现JSON-RPC协议的WebSocket服务器。它允许Javascript程序对任何jsonrpc-server进行JSON-RPC调用。被发布为一个浏览器依赖。
要创建发送请求的客户端,需要创建一个配置对象,如下所示:
var configuration = {
hearbeat: 5000,
sendCloseMessage : false,
ws : {
uri : ws_uri,
useSockJS: false,
onconnected : connectCallback,
ondisconnect : disconnectCallback,
onreconnecting : disconnectCallback,
onreconnected : connectCallback
},
rpc : {
requestTimeout : 15000,
treeStopped : treeStopped,
iceCandidate : remoteOnIceCandidate,
}
};
var jsonRpcClientWs = new JsonRpcClient(configuration);
这个配置对象有几个选项:一方面是关于传输的配置,另一方面是关于客户端在获得响应时必须调用的方法的配置。此外,它还可以配置心跳间隔,还可以配置在关闭连接之前是否发送消息。
{
heartbeat: interval in ms for each heartbeat message,
sendCloseMessage: true / false, before closing the connection, it sends a close_session message,
ws: {
uri: URItoconntectto,
useSockJS: true(useSockJS)/false(useWebSocket)bydefault,
onconnected: callback method to invoke when connection is successful,
ondisconnect: callback method to invoke when the connection is lost,
onreconnecting: callback method to invoke when the client is reconnecting,
onreconnected: callback method to invoke when the client succesfully reconnects
},
rpc: {
requestTimeout: timeoutforarequest,
sessionStatusChanged: callback method for changes in session status,
mediaRenegotiation: mediaRenegotiation
...
[Other methods you can add on rpc field are:
treeStopped : treeStopped
iceCandidate : remoteOnIceCandidate]
}
}
如果定义了心跳,客户端每隔x毫秒就向服务器发送ping以保持连接。
JSON-RPC调用就是通过发送方法发送一个Request对象给服务器。Request对象有以下成员:
var params = {
interval: 5000
};
jsonrpcClient.send(“ping”, params , function(error, response){
if(error) {
...
} else {
...
}
});
当服务器接收到RPC调用时,返回一个Response,Notification除外。Respone表示为JSON对象,成员如下:
Response包括“result”或“error”成员,但两者不能同时存在。
当RPC调用遇到错误时,Response对象包含Error成员对象,该成员对象具有以下成员:
jsonrpc客户端使用的webSocket具有重连功能,允许连接始终处于活动状态。
当以下任何情况发生时,将触发相应状态对应的回调方法:
下面示例为相关的配置对象,这个对象是jsonrpc客户端的配置对象的一部分:
{
uri: URItoconntectto,
useSockJS: true(useSockJS)/false(useWebSocket)bydefault,
onconnected: callback method to invoke when connection is successful,
ondisconnect: callback method to invoke when the connection is lost,
onreconnecting: callback method to invoke when the client is reconnecting,
onreconnected: callback method to invoke when the client succesfully reconnects
}
从Chrome M47开始,只允许安全源(HTTPS或来自本地主机的HTTP)对getUserMedia进行调用请求。由于Kurento严重依赖JSON-RPC库作为应用的信令部分,因此需要JSON-RPC服务器提供安全的WebSocket连接(WSS),否则客户端将接收mixed content错误,因为不安全的WS连接可能不会通过安全的HTTPS连接进行初始化。
在Spring中,启用安全的WebSocket连接相当简单。唯一的要求是拥有一个证书,或者是自签名的,或者是由证书颁发机构颁发的。证书必须存储在keystore中,以便以后可以被:term:JVM使用。根据你是否已获得了一个证书或希望生成自己的证书,你将需要执行不同的操作。
keytool -importcert -file certificate.cer -keystore keystore.jks -alias "Alias"
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048
keystore.jks文件必须放在项目的根路径下,application.properties文件必须放在*src/main/resources/*中,其内容如下:
server.port: 8443
server.ssl.key-store: keystore.jks
server.ssl.key-store-password: yourPassword
server.ssl.keyStoreType: JKS
server.ssl.keyAlias: yourKeyAlias
你可以自定义properties文件存放的位置。当启动基于Spring-Boot的应用时,需要定义一个宏*-Dspring.config.location=<path-to-properties>*。更改keystore.jks文件的位置,只需更改键server.ssl.key-store即可。Spring项目的完整正式文档可以在这里找到。
JSON-RPC客户端可以连接到暴露安全连接的服务端。默认情况下,使用的WebSocket库将尝试验证服务端证书。在使用自签名证书的情况下,必须指示客户端跳过此验证步骤。客户端可以通过创建SslContextFactory对象进行设置实现。
SslContextFactory contextFactory = new SslContextFactory();
contextFactory.setValidateCerts(false);
JsonRpcClientWebSocket client = new JsonRpcClientWebSocket(uri, contextFactory);
这是一个术语词汇表,经常出现在关于多媒体传输的讨论中。大多数术语都被描述并链接到维基百科、RFC或W3C相关文档。有些术语是kurento特有的。
HTTP
超文本传输协议是一种适用于分布式、协同、超媒体信息系统的应用协议。http是万维网数据通信的基础。
另外可以参考:RFC 2616
JAVA
Java是一种通用的计算机编程语言,它是并发的、基于类的、面向对象的,并且专门设计成尽可能少的实现依赖项。
JSON
JSON(JavaScript对象表示法)是一种轻量级的数据交换格式。它被设计成易于为人类理解和编写,也易于对机器进行解析。
JSON-RPC
JSON-RPC是一个简单的用JSON编码的远程过程调用协议。JSON-RPC允许将通知和多个调用发送到服务器,这些调用可能会被无序地响应。
Kurento
Kurento是一个开发多媒体应用程序的平台。Kurento是英语“stream”的世界语。我们之所以选择这个名字,是因为我们相信世界语原则对多媒体社区所需要的东西是鼓舞人心的:简单、开放和普遍性。Kurento是开源的,在Apache2.0下发布,有几个组件,为大多数多媒体公共服务需求提供解决方案。这些组件包括:Kurento Media Server、Kurento API、Kurento Protocol和Kurento Client。
Kurento API
Kurento API是面向对象的API,用于创建控制媒体的媒体管道。它可以看作是Kurento Media Server的接口。它可以在Kurento Protocol或Kurento Client中使用。
Kurento Client
Kurento Client是编程库(Java或JavaScript),在应用中它被用于控制Kurento Media Server。例如,有了这个库,任何开发人员都可以创建一个Web应用程序,该应用程序使用Kurento Media Server从用户Web浏览器接收音频和视频,并将其处理并通过Internet再次发送回来。Kurento Client向应用程序开发人员公开Kurento API。
Kurento Protocol
通过JSON-RPC消息在KMS和客户端之间进行通信。它基于WebSocket,它使用JSON-RPCV2.0消息来发出请求和发送响应。
KMS
Kurento Media Server
Kurento Media Server是Kurento的核心部件,因为它负责媒体的传输、处理、加载和记录。
Maven
Maven是一个主要用于Java项目的构建自动化工具。
Sphinx
用于Brandtalk文档的文档生成系统。
Spring Boot
Spring Boot是Spring的常规配置解决方案,用于创建独立的、基于产品级的基于Spring的应用程序,你可以“只运行”。[17]它对Spring平台和第三方库采取了自己独特的方式,这样你可以最小配置的情况下使用Spring。
TCP
可靠的IP传输协议。TCP通信确保在传输过程中不会丢失任何数据包。因此,它在低带宽或不可靠的环境中是最有用的。例如慢宽带网络或分组无线网。
UUID
通用唯一标识符,也称为全局唯一标识符(GUID)。在分布式计算环境中,唯一只是尽可能唯一,它不能保证是绝对唯一的,因为标识符集是有限的大小(16个字节)。
WebSocket
WebSockets
WebSocket规范(作为HTML 5计划的一部分开发)定义了一个全双工单套接字连接,通过它可以在客户端和服务器之间发送消息。
WSS
WebSockets Secure
WebSocket规范(作为HTML 5计划的一部分开发)定义了一个全双工单套接字连接,通过它可以在客户端和服务器之间发送消息。