# 实现收发消息 (/zh-CN/realtime-media/rtm/get-started/quick-start/web)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

本文将指导你如何利用 RTM JavaScript SDK 构建简单的应用程序，内容涵盖了集成和开发的基础知识：开通声网账号、获取 SDK、发送消息、接收消息等。
## 准备工作 [#准备工作-2]

开始构建项目前，你需要完成[开通 RTM 服务](/zh-CN/realtime-media/rtm/build/rtm-initialization/enable-service)，并检查你的浏览器是否满足[平台支持](/zh-CN/realtime-media/rtm/reference/capabilities-and-compatibility/platform-support)中的最低版本要求。

## 获取 SDK [#获取-sdk]

你可以通过以下任意一种方式获取最新的 RTM JavaScript SDK。

#### 方式一：使用 CDN 添加 SDK [#方式一使用-cdn-添加-sdk]

[点击此处](https://download.agora.io/rtm2/release/Agora_RTM_JavaScript_SDK_for_Web_v2.3.0.zip)下载最新版本的 JavaScript SDK，并在你的项目中添加以下代码引用它：

```javascript
// 使用 CDN 直接将以下代码添加到你的 Web 应用中，或下载 SDK 文件后在本地引用
// 将 x.y.z 替换为具体的 SDK 版本号，如 2.3.0
// 可通过发版说明获取最新版本号
<script src="your_path_to_sdk/agora-rtm.x.y.z.min.js"></script>
```

#### 方式二：使用包管理器引入 SDK [#方式二使用包管理器引入-sdk]

1. 根据你的应用场景和开发需要，选择一种方式和工具安装 SDK。

   **场景一**：项目仅使用 RTM 产品，且没有集成声网任何版本的 RTC 产品。

   <Tabs defaultValue="npm" groupId="package" persist>
   <TabsList>
     <TabsTrigger value="npm">使用 npm</TabsTrigger>
     <TabsTrigger value="pnpm">使用 pnpm</TabsTrigger>
     <TabsTrigger value="yarn">使用 Yarn</TabsTrigger>
   </TabsList>

   <TabsContent value="npm">

   ```shell
   npm install agora-rtm
   ```

   </TabsContent>

   <TabsContent value="pnpm">

   ```shell
   pnpm add agora-rtm
   ```

   </TabsContent>

   <TabsContent value="yarn">

   ```shell
   yarn add agora-rtm
   yarn add agora-rtc-sdk-ng@4.22.0 --save-exact
   ```

   </TabsContent>
   </Tabs>

   **场景二**：项目中已经集成了 RTC 产品，需要新接入 RTM 产品。

   <Tabs defaultValue="npm" groupId="package" persist>
   <TabsList>
     <TabsTrigger value="npm">使用 npm</TabsTrigger>
     <TabsTrigger value="pnpm">使用 pnpm</TabsTrigger>
     <TabsTrigger value="yarn">使用 Yarn</TabsTrigger>
   </TabsList>

   <TabsContent value="npm">

   ```shell
   npm install agora-rtm
   ```

   </TabsContent>

   <TabsContent value="pnpm">

   ```shell
   pnpm add agora-rtm
   ```

   如果项目使用的 RTC 版本与 RTM 中依赖的 RTC 版本不一致，会收到提示：`unmet peer agora-rtc-sdk-ng@4.22.0: found 4.16.0`。此时你需要升级 RTC 的版本：

   ```shell
   pnpm update agora-rtc-sdk-ng@4.22.0
   ```

   或者通过以下命令同时保留两个版本的 RTC 库：

   ```shell
   pnpm add agora-rtc-sdk-ng-4.16@npm:agora-rtc-sdk-ng@4.16.0 --save-exact
   pnpm add agora-rtc-sdk-ng@4.22.0 --save-exact
   ```

   然后将代码中原来的 `import AgoraRTC from "agora-rtc-sdk-ng"` 替换为如下行：

   ```javascript
   import AgoraRTC from "agora-rtc-sdk-ng-4.16"
   ```

   :::warning[注意]
   保留两个版本的 RTC 库会增加项目的包体积。
   :::

   </TabsContent>

   <TabsContent value="yarn">

   ```shell
   yarn add agora-rtm
   ```

   如果项目使用的 RTC 版本与 RTM 中依赖的 RTC 版本不一致，会收到提示：`unmet peer agora-rtc-sdk-ng@4.22.0: found 4.16.0`。此时你需要升级 RTC 的版本：

   ```shell
   yarn upgrade agora-rtc-sdk-ng@4.22.0
   ```

   或者通过以下命令同时保留两个版本的 RTC 库：

   ```shell
   yarn add agora-rtc-sdk-ng-4.16@npm:agora-rtc-sdk-ng@4.16.0 --save-exact
   yarn add agora-rtc-sdk-ng@4.22.0 --save-exact
   ```

   然后将代码中原来的 `import AgoraRTC from "agora-rtc-sdk-ng"` 替换为如下行：

   ```javascript
   import AgoraRTC from "agora-rtc-sdk-ng-4.16"
   ```

   :::warning[注意]
   保留两个版本的 RTC 库会增加项目的包体积。
   :::

   </TabsContent>
   </Tabs>

   **场景三**：你的项目是全新的，且需要同时集成 RTM 和 RTC，以利用其共频道、数据优先级控制等能力。这种场景下建议你集成融合版本的 SDK。

   <Tabs defaultValue="npm" groupId="package" persist>
   <TabsList>
     <TabsTrigger value="npm">使用 npm</TabsTrigger>
     <TabsTrigger value="pnpm">使用 pnpm</TabsTrigger>
     <TabsTrigger value="yarn">使用 Yarn</TabsTrigger>
   </TabsList>

   <TabsContent value="npm">

   ```shell
   npm install agora-rtx
   ```

   </TabsContent>

   <TabsContent value="pnpm">

   ```shell
   pnpm add agora-rtx --shamefully-hoist
   ```

   </TabsContent>

   <TabsContent value="yarn">

   ```shell
   # 若版本为 2.2.0
   # yarn add agora-rtx
   # yarn add agora-rtc-sdk-ng@4.22.0 --save-exact

   # 2.2.0 及之后版本
   yarn add agora-rtx
   ```

   </TabsContent>
   </Tabs>

2. 完成安装后，在你的 `app.js` 中引入 RTM：

    ```javascript
    // 如果安装的是 agora-rtm
    import AgoraRTM from 'agora-rtm';

    // 如果安装的是融合版 agora-rtx
    // import AgoraRTM from 'agora-rtx';
    // import AgoraRTC from 'agora-rtc-sdk-ng';
    ```

## 构建项目 [#构建项目-2]

### 1. 项目配置 [#1-项目配置-2]

新建一个文件夹存放项目文件，进入文件夹中为你的 App 创建文件并安装其他依赖项。以下为在终端操作的命令行示例：

  ```shell showLineNumbers
  mkdir HelloWorld
  cd HelloWorld
  # Your app is in index.html.
  touch index.html
  ```

### 2. 初始化 RTM [#2-初始化-rtm-2]

初始化 `rtm` 时，你需要提供如下信息：

- 从控制台获取的 App ID（`appId`）。
- 你自行定义的用户 ID（`userId`），你需要保证其全局唯一性。

```html showLineNumbers
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Text Display App</title>
  <style>
    #container {
      width: 800px;
      margin: 0 auto;
      padding: 20px;
    }
    #textDisplay {
      width: 100%;
      height: 800px;
      border: 1px solid #b0b0b0;
      margin-bottom: 20px;
      overflow: auto;
      text-align: left;
      padding: 10px;
      box-sizing: border-box;
    }
    #inputContainer {
      display: flex;
      align-items: center;
    }
    #textInput {
      width: calc(100% - 100px);
      padding: 5px;
      margin-right: 10px;
    }
    #submitButton {
      width: 90px;
      padding: 5px;
    }
  </style>
  <script src="./AgoraRTM-production.js"></script>
</head>
<body>
  <script>
    const { RTM } = AgoraRTM;
    // Fill in the App ID of your project.
    const appId = "your_appId";
    // Fill in your user ID.
    const userId = "your_userId";
    // Fill in your channel name.
    let rtm;
    const msChannelName = "Chat_room";
    const buttonClick = () => {
      var input = document.getElementById("textInput");
      publishMessage(input.value);
      input.value = '';
    }
    const setupRTM = async () => {
      // Initialize the RTM client.
      try {
        rtm = new RTM(appId, userId);
      } catch (status) {
        console.log("Error");
        console.log(status);
      }

      // Add the event listener.

      // Log in the RTM server.

      // Subscribe to a channel.

    }
    // Publish a message.
    const showMessage = (user, msg) => {
      // Get text from the text box.
      const inputText = textInput.value;
      const newText = document.createTextNode(user + ": " + msg);
      const newLine = document.createElement("br");
      textDisplay.appendChild(newText);
      textDisplay.appendChild(newLine);
    }
    window.onload = setupRTM;
  </script>
  <div id="container">
    <h1>Hello RTM !</h1>
    <div>
      <div id="textDisplay"></div>
    </div>
    <div id="inputContainer">
      <input type="text" id="textInput" placeholder="Enter text">
      <button id="submitButton" onclick="buttonClick()"> Send </button>
    </div>
  </div>
</body>
</html>
```

:::info[信息]
你需要在初始化时填入项目的 App ID 和 Token。在测试阶段，为快速验证功能，我们建议你在创建声网项目时将鉴权模式设置为调试模式，然后在初始化时只需填入项目的 App ID。
:::

### 3. 添加事件监听 [#3-添加事件监听-2]

事件监听程序帮助你实现频道中消息、事件到达后的处理逻辑，添加以下代码到你的程序中以显示收到的消息或事件通知：

```javascript showLineNumbers
// Paste the following code snippet below "Add the event listener" comment
// Message event handler.
rtm.addEventListener("message", event => {
  showMessage(event.publisher, event.message);
});
// Pressence event handler.
rtm.addEventListener("presence", event => {
  if (event.eventType === "SNAPSHOT") {
    showMessage("INFO", "I Join");
  }
  else {
    showMessage("INFO", event.publisher + " is " + event.eventType);
  }
});
// Link state changed event handler.
rtm.addEventListener("linkState", event => {
  const currentState = event.currentState;
  const previousState = event.previousState;
  const serviceType = event.serviceType;
  const operation = event.operation;
  const reason = event.reason;
  const affectedChannels = event.affectedChannels;
  const timestamp = event.timestamp;
  const isResumed = event.isResumed;
  showMessage("INFO", JSON.stringify(event));
});
```

### 4. 登录服务 [#4-登录服务-2]

你需要执行登录操作才能建立与 RTM 服务器的连接，然后才能调用 SDK 的其他 API。将以下代码添加到程序中，并传入你在服务端自行生成的 Token：

```javascript showLineNumbers
// Paste the following code snippet below "Log in the RTM server." comment
try {
  const result = await rtm.login({ token: 'your_token' });
  console.log(result);
} catch (status) {
  console.log(status);
}
```

### 5. 收发消息 [#5-收发消息-2]

调用 `publish` 方法向 Message Channel 发送消息后，RTM 会把该消息分发给该频道的所有订阅者，以下代码演示如何发送字符串类型的消息，将此代码片段添加到程序中：

:::info[信息]
你需要先对消息负载进行字符串序列化，才能调用 `publish` 方法发送消息。
:::

```javascript showLineNumbers
// Paste the following code snippet below "Publish a message" comment
const publishMessage = async (message) => {
  const payload = { type: "text", message: message };
  const publishMessage = JSON.stringify(payload);
  const publishOptions = { channelType: 'MESSAGE'}
  try {
    const result = await rtm.publish(msChannelName, publishMessage, publishOptions);
    showMessage(userId, publishMessage);
    console.log(result);
  } catch (status) {
    console.log(status);
  }
}
```

调用 `subscribe` 方法订阅此频道以接收此频道中的消息。将以下代码添加到程序中：

```javascript showLineNumbers
// Paste the following code snippet below "Subscribe to a channel." comment
try {
  const result = await rtm.subscribe(msChannelName);
  console.log(result);
} catch (status) {
  console.log(status);
}
```

如需了解更多收发消息的信息，查看[消息](/zh-CN/api-reference/rtm/web/message)。

### 6. 组合到一起 [#6-组合到一起]

经过上述步骤，你程序中的代码应该如下所示：

```html showLineNumbers
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Text Display App</title>
  <style>
    #container {
      width: 800px;
      margin: 0 auto;
      padding: 20px;
    }
    #textDisplay {
      width: 100%;
      height: 800px;
      border: 1px solid #b0b0b0;
      margin-bottom: 20px;
      overflow: auto;
      text-align: left;
      padding: 10px;
      box-sizing: border-box;
    }
    #inputContainer {
      display: flex;
      align-items: center;
    }
    #textInput {
      width: calc(100% - 100px);
      padding: 5px;
      margin-right: 10px;
    }
    #submitButton {
      width: 90px;
      padding: 5px;
    }
  </style>
  <script src="./AgoraRTM-production.js"></script>
</head>
<body>
  <script>
    const { RTM } = AgoraRTM;
    // Fill in the App ID of your project.
    const appId = "your_appId";
    // Fill in your user ID.
    const userId = "your_userId";
    // Fill in your channel name.
    let rtm;
    const msChannelName = "Chat_room";
    const buttonClick = () => {
      var input = document.getElementById("textInput");
      publishMessage(input.value);
      input.value = '';
    }
    const setupRTM = async () => {
      // Initialize the RTM client.
      try {
        rtm = new RTM(appId, userId);
      } catch (status) {
        console.log("Error");
        console.log(status);
      }
      // Add the event listener.
      // Message event handler.
      rtm.addEventListener("message", event => {
        showMessage(event.publisher, event.message);
      });
      // Pressence event handler.
      rtm.addEventListener("presence", event => {
        if (event.eventType === "SNAPSHOT") {
          showMessage("INFO", "I Join");
        }
        else {
          showMessage("INFO", event.publisher + " is " + event.eventType);
        }
      });
      // Link state changed event handler.
      rtm.addEventListener("linkState", event => {
        const currentState = event.currentState;
        const previousState = event.previousState;
        const serviceType = event.serviceType;
        const operation = event.operation;
        const reason = event.reason;
        const affectedChannels = event.affectedChannels;
        const timestamp = event.timestamp;
        const isResumed = event.isResumed;
        showMessage("INFO", JSON.stringify(event));
      });
      // Log in the RTM server.
      try {
        const result = await rtm.login({  token: 'your_token' });
        console.log(result);
      } catch (status) {
        console.log(status);
      }
      // Subscribe to a channel.
      try {
        const result = await rtm.subscribe(msChannelName);
        console.log(result);
      } catch (status) {
        console.log(status);
      }
    }
    const publishMessage = async (message) => {
      const payload = { type: "text", message: message };
      const publishMessage = JSON.stringify(payload);
      const publishOptions = { channelType: 'MESSAGE'}
      try {
        const result = await rtm.publish(msChannelName, publishMessage, publishOptions);
        showMessage(userId, publishMessage);
        console.log(result);
      } catch (status) {
        console.log(status);
      }
    }
    const showMessage = (user, msg) => {
      // Get text from the text box.
      const inputText = textInput.value;
      const newText = document.createTextNode(user + ": " + msg);
      const newLine = document.createElement("br");
      textDisplay.appendChild(newText);
      textDisplay.appendChild(newLine);
    }
    window.onload = setupRTM;
  </script>
  <div id="container">
    <h1>Hello RTM !</h1>
    <div>
      <div id="textDisplay"></div>
    </div>
    <div id="inputContainer">
      <input type="text" id="textInput" placeholder="Enter text">
      <button id="submitButton" onclick="buttonClick()"> Send </button>
    </div>
  </div>
</body>
</html>
```

现在，你可以开始运行你的程序：

1. 保存 **index.html**，并在浏览器中运行。我们称此时看到的页面为 A 页面。
2. 复制当前项目，在 **index.html** 的 `userId` 变量中填入一个不同的用户 ID，保存后运行。我们称此时看到的页面为 B 页面。
3. 在 A 页面文本输入框中输入要发送的信息，点击 **Send** 按钮发送，然后在 B 页面中观察是否收到信息。
4. 在 B 页面文本输入框中输入要发送的信息，点击 **Send** 按钮发送，然后在 A 页面中观察是否收到信息。

如果步骤 3 和步骤 4 中都能看到正确的信息，那么你已经成功集成并正确使用了 RTM 服务。

## 贯穿始终 [#贯穿始终-2]

相比于介绍如何写出代码，声网更愿意帮助你掌握上述程序编写的过程和逻辑。上述程序依次完成了以下操作，让你可以正确地收发消息：

1. 设置并初始化 RTM 对象。
2. 添加 `message`、`presence` 和 `linkState` 事件监听函数。
3. 调用 `login` 登录了 RTM 服务。
4. 调用 `subscribe` 订阅了一个 Message Channel。
5. 调用 `publish` 发送消息。

## 下一步 [#下一步-2]

现在，你已经学会了如何使用 RTM JavaScript SDK 来实现 Message Channel 的收发消息功能。下一步，你可以通过 SDK 的 [API 参考](/zh-CN/api-reference/rtm/web/configuration)了解更多功能的使用方法。












