register-test.html 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>JsSIP WSS Register Test</title>
  7. <style>
  8. body {
  9. font-family: Arial, sans-serif;
  10. margin: 24px;
  11. line-height: 1.5;
  12. background: #f5f7fa;
  13. color: #222;
  14. }
  15. .card {
  16. max-width: 900px;
  17. background: #fff;
  18. padding: 20px;
  19. border-radius: 10px;
  20. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  21. }
  22. .row {
  23. display: flex;
  24. gap: 12px;
  25. margin-bottom: 12px;
  26. flex-wrap: wrap;
  27. }
  28. .field {
  29. flex: 1 1 280px;
  30. }
  31. label {
  32. display: block;
  33. font-size: 14px;
  34. margin-bottom: 6px;
  35. font-weight: bold;
  36. }
  37. input {
  38. width: 100%;
  39. padding: 10px;
  40. box-sizing: border-box;
  41. border: 1px solid #ccc;
  42. border-radius: 6px;
  43. font-size: 14px;
  44. }
  45. button {
  46. padding: 10px 16px;
  47. border: 0;
  48. border-radius: 6px;
  49. font-size: 14px;
  50. cursor: pointer;
  51. background: #1677ff;
  52. color: #fff;
  53. }
  54. button.secondary {
  55. background: #666;
  56. }
  57. pre {
  58. margin-top: 16px;
  59. padding: 12px;
  60. min-height: 260px;
  61. overflow: auto;
  62. background: #0d1117;
  63. color: #d7e3f4;
  64. border-radius: 8px;
  65. white-space: pre-wrap;
  66. word-break: break-word;
  67. }
  68. .hint {
  69. font-size: 13px;
  70. color: #666;
  71. margin-top: 10px;
  72. }
  73. .ok {
  74. color: #0a7f2e;
  75. font-weight: bold;
  76. }
  77. .err {
  78. color: #c62828;
  79. font-weight: bold;
  80. }
  81. </style>
  82. </head>
  83. <body>
  84. <div class="card">
  85. <h2>JsSIP WSS Register Test</h2>
  86. <div class="row">
  87. <div class="field">
  88. <label for="wsUrl">WSS</label>
  89. <input id="wsUrl" value="wss://sip.ylrzcloud.com:8443">
  90. </div>
  91. <div class="field">
  92. <label for="sipDomain">SIP Domain</label>
  93. <input id="sipDomain" value="sip.ylrzcloud.com">
  94. </div>
  95. </div>
  96. <div class="row">
  97. <div class="field">
  98. <label for="username">分机号</label>
  99. <input id="username" value="1228">
  100. </div>
  101. <div class="field">
  102. <label for="password">分机密码</label>
  103. <input id="password" type="password" placeholder="请输入分机密码">
  104. </div>
  105. </div>
  106. <div class="row">
  107. <div class="field">
  108. <label for="registrarServer">Registrar</label>
  109. <input id="registrarServer" value="sip:sip.ylrzcloud.com">
  110. </div>
  111. <div class="field">
  112. <label for="realm">Realm</label>
  113. <input id="realm" value="sip.ylrzcloud.com">
  114. </div>
  115. </div>
  116. <div class="row">
  117. <button id="startBtn" disabled>开始注册</button>
  118. <button id="stopBtn" class="secondary">停止</button>
  119. <button id="clearBtn" class="secondary">清空日志</button>
  120. </div>
  121. <div id="status">状态:未启动</div>
  122. <div class="hint">
  123. 打开页面后先输入分机密码,再点击“开始注册”。如果浏览器证书未信任,请先在浏览器中访问一次
  124. <code>wss://sip.ylrzcloud.com:8443</code> 对应域名页面,确认无证书拦截。
  125. </div>
  126. <pre id="log"></pre>
  127. </div>
  128. <script src="./jssip.min.js"></script>
  129. <script>
  130. let ua = null;
  131. let jssipReady = !!window.JsSIP;
  132. function log(message, data) {
  133. const logEl = document.getElementById("log");
  134. const now = new Date().toLocaleTimeString();
  135. let line = "[" + now + "] " + message;
  136. if (typeof data !== "undefined") {
  137. try {
  138. line += "\n" + JSON.stringify(data, null, 2);
  139. } catch (e) {
  140. line += "\n" + String(data);
  141. }
  142. }
  143. logEl.textContent += line + "\n\n";
  144. logEl.scrollTop = logEl.scrollHeight;
  145. console.log(message, data || "");
  146. }
  147. function setStatus(text, className) {
  148. const el = document.getElementById("status");
  149. el.textContent = "状态:" + text;
  150. el.className = className || "";
  151. }
  152. function setStartButtonEnabled(enabled) {
  153. document.getElementById("startBtn").disabled = !enabled;
  154. }
  155. function ensureJsSIPLoaded() {
  156. if (window.JsSIP) {
  157. jssipReady = true;
  158. setStartButtonEnabled(true);
  159. setStatus("JsSIP 已就绪", "ok");
  160. log("已从本地文件加载 JsSIP", "./jssip.min.js");
  161. return;
  162. }
  163. setStatus("JsSIP 加载失败", "err");
  164. log("无法从本地加载 JsSIP,请确认当前页面与 ./jssip.min.js 在同一目录,并通过 HTTP 服务访问该页面。");
  165. }
  166. function stopUA() {
  167. if (ua) {
  168. try {
  169. ua.stop();
  170. log("已调用 ua.stop()");
  171. } catch (e) {
  172. log("停止 UA 异常", e.message || String(e));
  173. }
  174. ua = null;
  175. }
  176. setStatus("已停止");
  177. }
  178. function buildConfig() {
  179. const wsUrl = document.getElementById("wsUrl").value.trim();
  180. const sipDomain = document.getElementById("sipDomain").value.trim();
  181. const username = document.getElementById("username").value.trim();
  182. const password = document.getElementById("password").value;
  183. const registrarServer = document.getElementById("registrarServer").value.trim();
  184. const realm = document.getElementById("realm").value.trim();
  185. if (!wsUrl || !sipDomain || !username || !password || !registrarServer || !realm) {
  186. throw new Error("WSS、SIP 域名、分机号、密码、Registrar、Realm 都不能为空");
  187. }
  188. const socket = new JsSIP.WebSocketInterface(wsUrl);
  189. const configuration = {
  190. sockets: [socket],
  191. uri: "sip:" + username + "@" + sipDomain,
  192. authorization_user: username,
  193. password: password,
  194. registrar_server: registrarServer,
  195. realm: realm,
  196. register: true,
  197. session_timers: false
  198. };
  199. return configuration;
  200. }
  201. function bindUAEvents(instance) {
  202. instance.on("connecting", function(data) {
  203. setStatus("正在连接 WSS...");
  204. log("[SIP] connecting", data);
  205. });
  206. instance.on("connected", function(data) {
  207. setStatus("WSS 已连接");
  208. log("[SIP] connected", data);
  209. });
  210. instance.on("disconnected", function(data) {
  211. setStatus("WSS 已断开", "err");
  212. log("[SIP] disconnected", {
  213. code: data && data.code,
  214. reason: data && data.reason
  215. });
  216. });
  217. instance.on("registered", function(data) {
  218. setStatus("注册成功", "ok");
  219. log("[SIP] 注册成功", data);
  220. });
  221. instance.on("unregistered", function(data) {
  222. setStatus("已注销");
  223. log("[SIP] unregistered", data);
  224. });
  225. instance.on("registrationFailed", function(data) {
  226. setStatus("注册失败", "err");
  227. log("[SIP] 注册失败", {
  228. cause: data && data.cause,
  229. status_code: data && data.response && data.response.status_code,
  230. reason_phrase: data && data.response && data.response.reason_phrase
  231. });
  232. });
  233. instance.on("newRTCSession", function(data) {
  234. log("[SIP] newRTCSession", {
  235. direction: data && data.session && data.session.direction
  236. });
  237. });
  238. }
  239. document.getElementById("startBtn").addEventListener("click", function() {
  240. try {
  241. if (!jssipReady || !window.JsSIP) {
  242. throw new Error("JsSIP 库还没有加载成功");
  243. }
  244. stopUA();
  245. const configuration = buildConfig();
  246. log("准备启动 JsSIP", {
  247. sockets: configuration.sockets.map(function(item) { return item.url; }),
  248. uri: configuration.uri,
  249. authorization_user: configuration.authorization_user,
  250. registrar_server: configuration.registrar_server,
  251. realm: configuration.realm,
  252. register: configuration.register
  253. });
  254. ua = new JsSIP.UA(configuration);
  255. bindUAEvents(ua);
  256. ua.start();
  257. setStatus("已启动,等待连接...");
  258. } catch (e) {
  259. setStatus("启动失败", "err");
  260. log("启动异常", e.message || String(e));
  261. }
  262. });
  263. document.getElementById("stopBtn").addEventListener("click", function() {
  264. stopUA();
  265. });
  266. document.getElementById("clearBtn").addEventListener("click", function() {
  267. document.getElementById("log").textContent = "";
  268. });
  269. ensureJsSIPLoaded();
  270. </script>
  271. </body>
  272. </html>