65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 简单的 SIP(VOIP)电话

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (42投票s)

2010 年 12 月 21 日

CPOL

4分钟阅读

viewsIcon

388384

downloadIcon

61377

一个基于C#的简单SIP(VOIP)拨出电话。

SIP_CallOut.gif

引言

这是一个基于C#的简单SIP(VOIP)拨出电话。这个SIP应用程序已开发完成,目前用作“帮助 -> 联系支持”。其理念是创建一个零配置的、非常简单的拨出电话,现在它就是这样(尽管支持基于IP的来电;例如:sip:test@ip:7666,7666是SIP_Call out运行的端口)。

目前,此应用程序仅在Windows上运行。由于某种原因,.NET“仍然”没有对音频输入和音频输出的托管支持。音频部分使用非托管的Windows wave API。

我试图使示例应用程序井然有序、清晰易懂并带有良好的注释——不知道结果如何,您可以自行判断。对于初学者,我建议您使用Google搜索并阅读一些SIP入门教程,否则您将无法理解正在发生的事情。

由于代码充满了注释,我认为这里不需要过多的解释;只需深入研究代码即可。

使用的SIP命令和术语(在示例应用程序中)

  • INVITE - Invite有两个含义
    • 初始INVITE - 简单来说,我们或远程方只是发送一个呼叫请求。
    • 会话中INVITE - 在SIP规范中,这称为“RE-INVITE”。
  • RE-INVITE用于修改会话信息;在我们的例子中,实现呼叫保持。RE-INVITE可以由我们或远程方发送。
  • ACK - 对于每个INVITE/RE-INVITE的2xx肯定响应,都必须向远程方发送ACK。ACK只是确认我们收到了2xx响应。
  • CANCEL - CANCEL可用于取消挂起的INVITE或RE-INVITE请求。
  • BYE - BYE用于结束活动呼叫。呼叫终止方必须发送BYE。
  • SIP对话 - 我们可以将其想象为我们和远程方之间的会话。
  • SIP呼叫 - SIP呼叫由SIP对话和音频RTP会话组成。

建立呼叫

呼叫建立从创建RTP音频会话开始,因为我们需要在SDP中宣传我们的RTP会话IP:端口。之后,如果需要,我们需要进行NAT处理。现在可以创建初始INVITE请求并将其发送到远程方。更多详细信息,请阅读RFC 3216(请参阅下面的链接)。

交换的示例SIP消息

INVITE sip:bob@192.168.1.44 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.33;branch=z9hG4bKnashds8
Max-Forwards: 70
To: Bob <sip:bob@domain.com>
From: Alice <sip:alice@domain.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@192.168.1.33> 
Content-Type: application/sdp
Content-Length: sdp_size_in_bytes

v=0
o=- 2890844526 2890844526 IN IP4 192.168.1.33
s=
c=IN IP4 192.168.1.33
t=0 0
m=audio 1111 RTP/AVP 0 97
a=rtpmap:0 PCMU/8000
a=sendrecv

SIP/2.0 180 Ringing
Via: SIP/2.0/UDP 192.168.1.33;branch=z9hG4bK4b43c2ff8.1 ;received=192.0.2.3
To: Bob <sip:bob@domain.com>;tag=a6c85cf
From: Alice <sip:alice@domain.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.168.1.44>
CSeq: 314159 INVITE
Content-Length: 0

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.1.33;branch=z9hG4bK4b43c2ff8.1;received=192.0.2.3
To: Bob <sip:bob@domain.com>;tag=a6c85cf
From: Alice <sip:alice@domain.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@1192.168.1.44>
Content-Type: application/sdp
Content-Length: sdp_size_in_bytes

v=0
o=- 2890844526 2890844526 IN IP4 192.168.1.44
s=
c=IN IP4 192.168.1.44
t=0 0
m=audio 2222 RTP/AVP 0 97
a=rtpmap:0 PCMU/8000
a=sendrecv

ACK sip:bob@192.0.2.4 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.33;branch=z9hG4bKnashds9
Max-Forwards: 70
To: Bob <sip:bob@domain.com>;tag=a6c85cf
From: Alice <sip:alice@domain.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 ACK
Content-Length: 0

呼叫保持

在SIP中,呼叫保持并非精确定义。实际上,SIP不知道任何关于保持的信息。保持完全由应用程序处理(这是因为SIP不关心它发送的SDP数据)。因此,不同的实现可能会以不同的方式处理它。

到目前为止,我已经看到了四种不同的电话处理方式

  1. 在SDP中将音频流IP设置为“0.0.0.0”。
  2. 通过将端口设置为“0”来禁用SDP中的音频流。
  3. 将音频流设置为“sendonly”——通常情况下,呼叫保持者会向远程方发送保持音乐。
  4. 更清晰的方法是将音频流设置为“inactive”(这就是我们的做法)。

禁用或设置非活动音频流的主要区别在于,“inactive”RTCP中,会话仍然存在并且会发送RTCP数据包。关于“inactive”中的RTP流,应用程序可以在保持状态下暂停会话或将其释放。

示例SDP(进行和取消保持)

[onhold our offer]
v=0
o=- 2890844526 2890844526 IN IP4 192.168.1.33
s=
c=IN IP4 192.168.1.33
t=0 0
m=audio 1111 RTP/AVP 0 97
a=rtpmap:0 PCMU/8000
a=inactive

[onhold remote-party answer]
v=0
o=- 2890844526 2890844526 IN IP4 192.168.1.44
s=
c=IN IP4 192.168.1.44
t=0 0
m=audio 2222 RTP/AVP 0 97
a=rtpmap:0 PCMU/8000
a=inactive 

------------------------------------------------

[unhold our offer]
v=0
o=- 2890844526 2890844526 IN IP4 192.168.1.33
s=
c=IN IP4 192.168.1.33
t=0 0
m=audio 1111 RTP/AVP 0 97
a=rtpmap:0 PCMU/8000
a=sendrecv

[unhold remote-party answer]
v=0
o=- 2890844526 2890844526 IN IP4 192.168.1.44
s=
c=IN IP4 192.168.1.44
t=0 0
m=audio 2222 RTP/AVP 0 97
a=rtpmap:0 PCMU
a=sendrecv

NAT处理

我们支持两种不同的NAT处理方法

  1. STUN - 向STUN服务器发送STUN请求,它会回复请求发出的IP:端口。
  2. UPnP - 使用UPnP API打开路由器端口。只有在路由器支持UPnP的情况下才能使用此方法。

只有在路由器支持UPnP或路由器类型(STUN)不是“对称NAT”的情况下,才能拨打电话。“对称NAT”中,每个对新IP:端口的新UDP请求都映射到一个新的路由器,外部新的IP:端口。有关NAT类型的更多信息,请参阅:http://en.wikipedia.org/wiki/Network_address_translation

如果路由器支持SIP ALG(应用层网关),则存在一个特殊情况,应用程序**不必**进行NAT处理。路由器会根据需要更改SIP、SDP并打开路由器端口。(到目前为止,我看到汤普森路由器是这样做的。)

最后的寄语

如果你有合适的组件(SIP协议栈、RTP协议栈、音频库),那么构建SIP软电话很容易……但是当你需要从头开始编写所有代码时,这将是一场噩梦。你会陷入大量的混乱的RFC中……

链接

© . All rights reserved.