JsSIP 集成文档

概述

JsSIP是一个纯JavaScript的SIP客户端库,利用WebRTC和SIP over WebSocket技术,让任何网站都能轻松实现实时通信功能。

主要特性

  • 基于WebSocket的SIP传输
  • 支持音频/视频通话、即时消息和在线状态
  • 轻量级,100%纯JavaScript实现
  • 易于使用且功能强大的API
  • 兼容OverSIP、Kamailio、Asterisk和FreeSWITCH服务器

支持的SIP标准

JsSIP实现了以下SIP规范:

  • RFC 3261 “SIP: Session Initiation Protocol”
  • RFC 3311 “SIP UPDATE Method”
  • RFC 3326 “The Reason Header Field for SIP”
  • RFC 3327 “SIP Extension Header Field for Registering Non-Adjacent Contacts” (Path header)
  • RFC 3428 “SIP Extension for Instant Messaging” (MESSAGE method)
  • RFC 3515 “The SIP Refer Method”
  • RFC 3891 “The SIP Replaces Header”
  • RFC 4028 “Session Timers in SIP”
  • RFC 5589 “The SIP Call Control – Transfer”
  • RFC 5626 “Managing Client-Initiated Connections in SIP” (Outbound mechanism)
  • RFC 7118 “The WebSocket Protocol as a Transport for SIP”

核心API类

1. JsSIP.UA (User Agent)

UA是JsSIP的核心类,代表SIP用户代理。

1.1 创建UA实例
var socket = new JsSIP.WebSocketInterface('wss://sip.example.com');
var configuration = {
  sockets: [socket],
  uri: 'sip:alice@example.com',
  password: 'superpassword'
};


var ua = new JsSIP.UA(configuration);
1.2 UA配置参数
参数类型必需默认值说明
uriString-SIP URI (sip:user@domain)
passwordString-SIP密码
display_nameString-显示名称
authorization_userStringuri中的用户名认证用户名
realmStringuri中的域名认证域
ha1String-HA1哈希值
socketsArray-WebSocket接口数组
contact_uriString-Contact URI
instance_idString随机生成实例ID
session_timersBooleantrue会话定时器
no_answer_timeoutNumber60无应答超时(秒)
use_preloaded_routeBooleanfalse使用预加载路由
hack_via_tcpBooleanfalseTCP Via头部修正
hack_ip_in_contactBooleanfalseContact中IP修正(Asterisk兼容)
1.3 UA方法
// 启动UA
ua.start();


// 停止UA
ua.stop();


// 注册
ua.register();


// 注销
ua.unregister();


// 检查是否已注册
ua.isRegistered();


// 检查是否已连接
ua.isConnected();


// 发起呼叫
var session = ua.call('sip:bob@example.com', options);


// 发送消息
ua.sendMessage('sip:bob@example.com', 'Hello World!', options);
1.4 UA事件
ua.on('connecting', function(e) {
  console.log('正在连接...');
});


ua.on('connected', function(e) {
  console.log('已连接');
});


ua.on('disconnected', function(e) {
  console.log('连接断开:', e.cause);
});


ua.on('registered', function(e) {
  console.log('注册成功');
});


ua.on('unregistered', function(e) {
  console.log('注销成功');
});


ua.on('registrationFailed', function(e) {
  console.log('注册失败:', e.cause);
});


ua.on('newRTCSession', function(e) {
  console.log('新的通话会话');
  var session = e.session;
  // 处理会话事件
});


ua.on('newMessage', function(e) {
  console.log('收到新消息:', e.message.body);
});

2. JsSIP.RTCSession (通话会话)

RTCSession处理音频/视频通话会话。

2.1 发起呼叫
var eventHandlers = {
  'progress': function(e) {
    console.log('呼叫进行中');
  },
  'confirmed': function(e) {
    console.log('呼叫已建立');
  },
  'ended': function(e) {
    console.log('呼叫结束');
  },
  'failed': function(e) {
    console.log('呼叫失败:', e.cause);
  }
};


var options = {
  'eventHandlers': eventHandlers,
  'mediaConstraints': {
    'audio': true,
    'video': true
  }
};


var session = ua.call('sip:bob@example.com', options);
2.2 接听呼叫
ua.on('newRTCSession', function(e) {
  var session = e.session;

  if (session.direction === 'incoming') {
    // 接听呼叫
    session.answer({
      'mediaConstraints': {
        'audio': true,
        'video': true
      }
    });

    // 或者拒绝呼叫
    // session.terminate();
  }
});
2.3 RTCSession方法
// 接听
session.answer(options);


// 挂断
session.terminate(options);


// 保持
session.hold(options);


// 取消保持
session.unhold(options);


// 静音
session.mute(options);


// 取消静音
session.unmute(options);


// 发送DTMF
session.sendDTMF('1234');


// 转接
session.refer('sip:charlie@example.com');


// 重新协商
session.renegotiate(options);
2.4 RTCSession事件
session.on('connecting', function(e) {
  console.log('会话连接中');
});


session.on('progress', function(e) {
  console.log('会话进行中');
  if (e.response) {
    console.log('响应码:', e.response.status_code);
  }
});


session.on('confirmed', function(e) {
  console.log('会话已确认');
});


session.on('ended', function(e) {
  console.log('会话结束');
  console.log('原因:', e.cause);
});


session.on('failed', function(e) {
  console.log('会话失败');
  console.log('原因:', e.cause);
});


session.on('hold', function(e) {
  console.log('会话保持');
});


session.on('unhold', function(e) {
  console.log('取消保持');
});


session.on('muted', function(e) {
  console.log('静音');
});


session.on('unmuted', function(e) {
  console.log('取消静音');
});

3. JsSIP.Message (即时消息)

3.1 发送消息
var eventHandlers = {
  'succeeded': function(e) {
    console.log('消息发送成功');
  },
  'failed': function(e) {
    console.log('消息发送失败:', e.cause);
  }
};


var options = {
  'eventHandlers': eventHandlers
};


ua.sendMessage('sip:bob@example.com', 'Hello World!', options);
3.2 接收消息
ua.on('newMessage', function(e) {
  var message = e.message;

  if (message.direction === 'incoming') {
    console.log('收到消息:', message.body);
    console.log('发送者:', message.remote_identity.uri);

    // 回复消息
    message.accept();
  }
});

4. JsSIP.WebSocketInterface (WebSocket接口)

4.1 创建WebSocket接口
var socket = new JsSIP.WebSocketInterface('wss://sip.example.com');


// 带参数的WebSocket
var socket = new JsSIP.WebSocketInterface('wss://sip.example.com', 'sip');


// 多个WebSocket服务器
var sockets = [
  new JsSIP.WebSocketInterface('wss://sip1.example.com'),
  new JsSIP.WebSocketInterface('wss://sip2.example.com')
];
4.2 WebSocket事件
socket.on('connected', function(e) {
  console.log('WebSocket连接成功');
});


socket.on('disconnected', function(e) {
  console.log('WebSocket连接断开');
});

完整示例

基本SIP客户端

<!DOCTYPE html>
<html>
<head>
  <title>JsSIP Demo</title>
  <script src="https://cdn.jsdelivr.net/npm/jssip@3.10.0/dist/jssip.min.js"></script>
</head>
<body>
  <h1>JsSIP SIP客户端</h1>

  <div>
    <button id="register">注册</button>
    <button id="unregister">注销</button>
    <span id="status">未连接</span>
  </div>

  <div>
    <input type="text" id="target" placeholder="目标号码">
    <button id="call">呼叫</button>
    <button id="hangup">挂断</button>
  </div>

  <div>
    <input type="text" id="message" placeholder="消息内容">
    <button id="sendMessage">发送消息</button>
  </div>

  <div id="log"></div>

  <script>
    // 配置
    var socket = new JsSIP.WebSocketInterface('wss://your-freeswitch-server:7443');
    var configuration = {
      sockets: [socket],
      uri: 'sip:1001@your-domain.com',
      password: 'your-password',
      hack_ip_in_contact: true // Asterisk/FreeSWITCH兼容
    };

    var ua = new JsSIP.UA(configuration);
    var currentSession = null;

    // UI元素
    var statusEl = document.getElementById('status');
    var logEl = document.getElementById('log');

    function log(message) {
      logEl.innerHTML += '<div>' + new Date().toLocaleTimeString() + ': ' + message + '</div>';
      logEl.scrollTop = logEl.scrollHeight;
    }

    // UA事件处理
    ua.on('connecting', function(e) {
      statusEl.textContent = '连接中...';
      log('正在连接到SIP服务器');
    });

    ua.on('connected', function(e) {
      statusEl.textContent = '已连接';
      log('已连接到SIP服务器');
    });

    ua.on('disconnected', function(e) {
      statusEl.textContent = '连接断开';
      log('与SIP服务器连接断开: ' + e.cause);
    });

    ua.on('registered', function(e) {
      statusEl.textContent = '已注册';
      log('SIP注册成功');
    });

    ua.on('unregistered', function(e) {
      statusEl.textContent = '已注销';
      log('SIP注销成功');
    });

    ua.on('registrationFailed', function(e) {
      statusEl.textContent = '注册失败';
      log('SIP注册失败: ' + e.cause);
    });

    ua.on('newRTCSession', function(e) {
      var session = e.session;
      currentSession = session;

      if (session.direction === 'incoming') {
        log('收到来电: ' + session.remote_identity.uri);

        // 自动接听(实际应用中应该询问用户)
        session.answer({
          mediaConstraints: {
            audio: true,
            video: false
          }
        });
      }

      // 会话事件处理
      session.on('confirmed', function(e) {
        log('通话已建立');
      });

      session.on('ended', function(e) {
        log('通话结束: ' + e.cause);
        currentSession = null;
      });

      session.on('failed', function(e) {
        log('通话失败: ' + e.cause);
        currentSession = null;
      });
    });

    ua.on('newMessage', function(e) {
      var message = e.message;

      if (message.direction === 'incoming') {
        log('收到消息: ' + message.body + ' (来自: ' + message.remote_identity.uri + ')');
        message.accept();
      }
    });

    // 按钮事件
    document.getElementById('register').onclick = function() {
      ua.start();
    };

    document.getElementById('unregister').onclick = function() {
      ua.stop();
    };

    document.getElementById('call').onclick = function() {
      var target = document.getElementById('target').value;
      if (!target) {
        alert('请输入目标号码');
        return;
      }

      var eventHandlers = {
        'progress': function(e) {
          log('呼叫进行中...');
        },
        'confirmed': function(e) {
          log('通话已建立');
        },
        'ended': function(e) {
          log('通话结束: ' + e.cause);
          currentSession = null;
        },
        'failed': function(e) {
          log('呼叫失败: ' + e.cause);
          currentSession = null;
        }
      };

      var options = {
        eventHandlers: eventHandlers,
        mediaConstraints: {
          audio: true,
          video: false
        }
      };

      currentSession = ua.call('sip:' + target + '@your-domain.com', options);
    };

    document.getElementById('hangup').onclick = function() {
      if (currentSession) {
        currentSession.terminate();
      }
    };

    document.getElementById('sendMessage').onclick = function() {
      var target = document.getElementById('target').value;
      var message = document.getElementById('message').value;

      if (!target || !message) {
        alert('请输入目标号码和消息内容');
        return;
      }

      var eventHandlers = {
        'succeeded': function(e) {
          log('消息发送成功');
        },
        'failed': function(e) {
          log('消息发送失败: ' + e.cause);
        }
      };

      ua.sendMessage('sip:' + target + '@your-domain.com', message, {
        eventHandlers: eventHandlers
      });

      document.getElementById('message').value = '';
    };
  </script>
</body>
</html>

与FreeSWITCH集成

FreeSWITCH配置

1. 启用WebSocket支持

conf/sip_profiles/internal.xml中添加:

<param name="ws-binding" value=":5066"/>
<param name="wss-binding" value=":7443"/>
2. 配置用户

conf/directory/default/目录下创建用户配置文件:

<include>
  <user id="1001">
    <params>
      <param name="password" value="your-password"/>
      <param name="vm-password" value="1001"/>
    </params>
    <variables>
      <variable name="toll_allow" value="domestic,international,local"/>
      <variable name="accountcode" value="1001"/>
      <variable name="user_context" value="default"/>
      <variable name="effective_caller_id_name" value="Extension 1001"/>
      <variable name="effective_caller_id_number" value="1001"/>
    </variables>
  </user>
</include>
3. 配置拨号计划

conf/dialplan/default.xml中添加:

<extension name="Local_Extension">
  <condition field="destination_number" expression="^(10[01][0-9])$">
    <action application="export" data="dialed_extension=$1"/>
    <action application="bridge" data="user/${dialed_extension}@${domain_name}"/>
  </condition>
</extension>

JavaScript客户端配置

var socket = new JsSIP.WebSocketInterface('wss://your-freeswitch-server:7443');
var configuration = {
  sockets: [socket],
  uri: 'sip:1001@your-freeswitch-domain',
  password: 'your-password',
  hack_ip_in_contact: true,  // FreeSWITCH兼容性
  session_timers: false      // 可选:禁用会话定时器
};

常见问题和注意事项

1. 浏览器兼容性

JsSIP需要现代浏览器支持:

  • Chrome 23+
  • Firefox 22+
  • Safari 11+
  • Edge 79+

2. HTTPS要求

WebRTC要求HTTPS环境:

  • 生产环境必须使用HTTPS
  • 本地开发可以使用localhost
  • 需要有效的SSL证书

3. Asterisk兼容性

使用Asterisk时需要特殊配置:

var configuration = {
  // ... 其他配置
  hack_ip_in_contact: true  // 解决Asterisk的Contact头部问题
};

4. 防火墙和NAT

  • 确保WebSocket端口开放
  • 配置STUN/TURN服务器处理NAT
  • 考虑使用ICE服务器

5. 媒体权限

// 请求媒体权限
navigator.mediaDevices.getUserMedia({
  audio: true,
  video: true
}).then(function(stream) {
  // 权限获取成功
}).catch(function(error) {
  console.error('媒体权限获取失败:', error);
});

6. 错误处理

ua.on('registrationFailed', function(e) {
  switch(e.cause) {
    case JsSIP.C.causes.AUTHENTICATION_ERROR:
      console.log('认证失败,请检查用户名和密码');
      break;
    case JsSIP.C.causes.CONNECTION_ERROR:
      console.log('连接失败,请检查网络和服务器');
      break;
    default:
      console.log('注册失败:', e.cause);
  }
});

7. 性能优化

  • 合理设置会话定时器
  • 及时清理事件监听器
  • 使用连接池管理多个会话
  • 实现断线重连机制

8. 安全考虑

  • 使用WSS(WebSocket Secure)
  • 实施适当的认证机制
  • 限制跨域访问
  • 定期更新JsSIP版本

调试技巧

1. 启用调试日志

JsSIP.debug.enable('JsSIP:*');

2. 监控WebSocket连接

socket.on('connected', function() {
  console.log('WebSocket连接建立');
});


socket.on('disconnected', function() {
  console.log('WebSocket连接断开');
});

3. 检查SIP消息

ua.on('sipEvent', function(e) {
  console.log('SIP事件:', e.event, e.data);
});

最佳实践

  1. 错误处理: 始终为关键操作添加错误处理
  2. 用户体验: 提供清晰的连接状态指示
  3. 资源管理: 及时清理不再使用的会话和事件监听器
  4. 安全性: 使用HTTPS和WSS,实施适当的认证
  5. 兼容性: 测试不同浏览器和SIP服务器的兼容性
  6. 性能: 合理配置超时参数,避免内存泄漏

这个文档涵盖了JsSIP的主要功能和集成要点,可以作为开发WebRTC SIP应用的完整参考指南。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值