官网: 关于如何编译:
http://www.qutecom.org/wiki/HowToBuildFromSource#CMake
vs 2008版本
///推荐博客
- QuteCom手记(21)
QuteCom手记:sip简介&SIP:INVITE消息
参考《sip中文参考手册.pdf》已经放在在csdn的资源里
该协议的实现在exosip和osip2两个工程里。
phapi调用了exosip的接口
SIP:会话控制协议
1.1简介
作用:
1、根据SIP帐号定位用户(IP)。
2、商讨会话(语音通话,视频连接之类)的参数(语音编码之类)。
3、建立、终止回话。
4、其他没有在qutecom中用到的功能不记。
在QuteCom项目里可以简单认为使用SIP协议做以下几件事:
1、User1向SIP服务器注册一个形如:sip:User1@xxxx.com的账号
2、User1打电话给一个用户SIP:User2@xxxx.com,此时服务器通过SIP协议找到User1的IP地址(定位)
3、服务器处理User1向User2发起的邀请(INVITE),找到用户后,通过SIP协议协商通话的参数,比如User1向User2提供了一个语音编码的选项表(G711,G723,G726,G729等等),User2收到邀请(INVITE)后查看列表和自己支持的列表,假如自己的列表首选的是G723,那么发送一个OK(200)消息给User1。这样两方就协商好了通话的音频编码为G723(或者其他)。这就是商讨会话参数
4、建立、终止会话,基本上就是打电话,接电话之类。就是发送各种消息改变通话状态。这部分内容茫茫多,将另外写一篇笔记。
特点:
1、基于文本,例如发起邀请的INVITE请求格式为:
- INVITE sip:to@192.168.105.14 SIP/2.0
- Via: SIP/2.0/UDP 192.168.105.5:5060;rport;branch=z9hG4bK2195
- From: <sip:from@192.168.105.5>;tag=29244
- To: <sip:to@192.168.105.14>
- Call-ID: 8103
- CSeq: 20 INVITE
- Contact: <sip:from@192.168.105.5:5060>
- Content-Type: application/sdp
- Max-Forwards: 70
- User-Agent: Linphone/3.2.1 (eXosip2/3.3.0)
- Subject: Phone call
- Content-Length: 315
- v=0
- o=from 123456 654321 IN IP4 192.168.105.5
- s=A conversation
- c=IN IP4 192.168.105.5
- t=0 0
- m=audio 7078 RTP/AVP 111 110 0 3 8 101
- a=rtpmap:111 speex/16000/1
- a=rtpmap:110 speex/8000/1
- a=rtpmap:0 PCMU/8000/1
- a=rtpmap:3 GSM/8000/1
- a=rtpmap:8 PCMA/8000/1
- a=rtpmap:101 telephone-event/8000
- a=fmtp:101 0-11
全是字符串格式的格式,类似HTTP协议,SMTP协议。这样的好处是可读行好,解析也方便。如解读方用正则表达式就可以十分方便解析出各种参数。
1.2 INVITE格式解析 第1~12行是SIP的包头,其中第一行为请求类型(INIVITE),其后至12行为该请求的头域,格式为(xx:yy)。 第13~25是该SIP包携带的SDP包(即为该SIP包的消息正文) 第1行:请求的类型(INVITE)。 第2行:VIA域指定SIP版本2.0,传输协议为UDP。包含了Alice 接收发送请求的服务器地址(pc33.atlanta.com) 。同样这个包含了一个分支参数来标志Alice和这个服务器的会话事务。 第3行和第4行:指定发起者和接收者。 第5行:Call_ID 包含一个全局的唯一标志,用来唯一标志这个呼叫,通过随机字串和softphone的自己名字或者IP地址混和产生的。 通过TO TAG, FROM TAG 和CALL-ID 完整定义了Alice和Bob之间的端到端的SIP 关系,并且表示这个是一个对话性质的关系。 第6行:CSEQ 或者Command Sequence 包含了一个整数和一个请求名字。这个Cseq 数字是顺序递增的。每当对话中发起一个新的请求都会 引起这个数字的顺序递增。 第7行:服务器,IP:Port 第8行:Content-Type: application/sdp 表示该SIP消息携带的消息正文类型为sdp包。 第9行:Max-Forwards规定了最大转发数量,它通讯中本消息转发的最大次数,每转发一次减一。这个可以不理他。 第10行:User-Agent,用户客户端描述。这里说明客户端是Linphone版本为3.2.1 使用的eXosip2库版本为3.3.0。 这个可以是wengophone/qutecom等等。 第11行:Subject: Phone call。说明这个SIP包的主题是一个Phone Call。这个可以不理它。 地12行:Content-Length: 315 表示该SIP消息携带的内容的长度为315字节 第18行代表的所负载类型 m=audio 表示负载是声音数据, 7078 RTP/AVP 表示使用RTP传输,发起方RTP端口7078. 111,110,0,3,8,101是负载的编码/解码方式的数字表示。 第19~23表示对应(111,110,0,3,8,101)的具体格式。 18~23行给对方提出选项,对方可以选择其中之一codec用于建立会话。如对方可以选择GSM作为语音编码作为双方的编码。 这就是SIP的“会话协商”功能。 "a=rtpmap:3 GSM/8000/1"的具体意义如下 3:代表RTP负载GSM的数字代码。 GSM:类型描述 8000:语音数据取样率8kHz 1:单声道
特别注意18~23行的编码选项:如果对方不支持里面的codec之一则无法建立通话。
"
http://blog.csdn.net/nomousewch/article/details/7012392
在进行二次开发,特别是开源项目的二次开发中,有那么几点我觉得是非常重要的,第一是在修改之前要先搞清楚这个开源项目如何进行部署、打包和测试,这些东西一般来说比较烦,容易产生问题,而国外开源软件的文档可能不是那么全面,环境和我们也可能不同(一般是linux环境居多);第二是在修改之前,先研究一下别人的源码,看看别人是不是给你提供了一些二次开发的接口,一般都会有一些plugins之类的,可以直接拿过来用;第三也是最重要的,如果不是特别需要,尽量不要直接修改别人的代码,也不要破坏别人的类结构(比如在别人的包下面加类),这样做,以后维护起来比较方便,而且我们也可以继续更新使用官方的新版本。
"
VOIP综述:
- 简介
VoIP(Voice over Internet Protocol)就是将模拟声音讯号(Voice)数字化,以数据封包(Data Packet)的型式在 IP 数据网络 (IP Network)上做实时传递,最简单的说法,就是通过网络来模拟我们平时的电话通话。
- 特点
VoIP最大的优势是能广泛地采用Internet和全球IP互连的环境,提供比传统业务更多、更好的服务。 VoIP可以在IP网络上便宜的传送语音、传真、视频、和数据等业务,如统一消息、虚拟电话、虚拟语音/传真邮箱、查号业务、Internet呼叫中心、Internet呼叫管理、电视会议、电子商务、传真存储转发和各种信息的存储转发等。
对于网络客户端之间的通话,我们只需要能上网就可以了,因此VoIP相对便宜,尤其是国际长途,而如果是从网络客户端与传统电话、手机之间的通话,则需要向固话网络运营商以及无线通讯运营商支付通话费用。
- VoIP 控制协议
VoIP是基于网络的一种应用,它通过TCP/IP协议来建立通讯、传输语音信号,因此,VoIP从本质上来说和电子邮件、Http网页访问、Ftp下载这些网络应用是相同的,与之相应地,VoIP也势必要有一套自己的控制协议,VoIP目前常用的协议如H.323、SIP、MEGACO和MGCP。其中,SIP协议的发展前景较好。
- VoIP与程序员
对于程序员,搭建一个完整的开源VoIP系统可能包括以下几个方面的工作。
- 协议的选用,这个决定整个VoIP系统的架构。一般选取SIP协议,系统就必须包括这个协议中定义的一些实体:代理服务器(主服务器)、注册服务器、网关、用户代理(终端)等。
- 服务器的选择和部署,如果采用开源的实现,可以使用Asterisk系列或者是freeswitch等开源的服务器端。
- 客户端的选择和部署,这方面的资源丰富,有X-lite、Skyup等闭源实现,也有Jisti、QuteCom等开源实现。
- 多媒体编解码器方面,用以改善通话质量。
http://blog.csdn.net/nomousewch/article/details/6861422
昨天终于将困扰自己接近一个月的一个问题解决了,心情既激动又有点无奈,激动的是自己一直以来的坚持得到了回报,无奈的是克服这个问题实在的花费了巨大的代价,走了很多不必要的弯路。但不管怎么样,这个问题让自己获益良多,值得记录下来,作以参考。
- 问题:
- 解决问题的思路和过程:
首先我们先是通过网络查找有无人遇到这方面的问题,有很多人看似遇到了同样的问题,但是网上没有现成的解决方案。
不得已,只能自己解决这个问题,那么首先是确认问题在哪个方面,我们起初怀疑是SIP服务器端的原因,于是就用其他的SIP语音客户端作测试,结果是X-lite等SIP语音插件可以和X-lite自身客户端和Spark语音插件打通并且有声音,这样来看,应该不是服务器端设置的问题。
然后我们怀疑是openfire服务器端的问题,但是经过研究发现openfire服务器不涉及到SIP语音过程中,而只是提供SIP账号和Spark账号的映射,具体的SIP通讯过程还是在客户端与SIP服务器端之间进行的。
我们又把目光集中到SIP协议上,经过网络抓包,我们研究了SIP协议头,对比了成功的通讯和上述不成功通讯的差别,结果发现SIP协议在接通后的一段时间内自动挂断,这里让我们百思不得其解,另外抓包时发现不成功的通讯的RTP流完全不通,这里由于对于协议不了解,只好去学习SIP协议和RTP协议,但是仍然没有结果。
无奈之下,我们只好开始研究Spark的SIP客户端源码,在这个过程中,又要去学习java的JMF多媒体框架,但是最终发现代码没有问题,可RTP流就是无法接通。
问题到这里遇到了死胡同,只好另外再想办法,于是我们又找了另外一个开源的Java SIP客户端jitsi,这个客户端互相之间是可以打通并且有声音的,于是想通过研究它的源码来解决上面的问题,可是由于对于底层的运行机理不了解,依然没有搞清楚问题。
到这步实在是没辙了,于是就考虑直接剥离jitsi客户端的SIP模块,然后将Spark客户端的SIP插件重写,可是这也不是一件轻松的活,因为jitsi应用了Apache的felix这样一个基于osgi的动态模块框架,因为涉及到模块之间的依赖关系而使剥离模块很困难。
恰好到此时,我碰巧用别的机器做了一下测试,结果竟然有声音!因为之前一直是我和一个同事的机器之间在测试通讯,没有成功,结果在别的机器上竟然是通的!这让我想到了被我们忽略的网络环境,然后测试了一下,果然,有的机器之间有声音,有的机器之间没有声音,因为我们公司的网络环境并不稳定!我们之前的解决思路有一大部分是错误的!幸好及时发现这个问题,不然可能是沿着之前错误的道路越走越远。
- 教训和收获
回顾上面这个问题的解决过程,我们在前几步就出现了失误,不应该一直在同样的两台机器上测试,而应该充分考虑网络的个别差异,这也可以说是一种巧合,可是严谨的解决问题的思路应该尽量避免这种巧合给我们工作带来的影响,在解决一件事情时要充分考虑各方面的因素,在我和我同事的工作中,一些非常“难以解决”的问题,一般并不是一些很高深的技术难题,恰恰相反,反而可能是一些与技术没有什么关系但是却不易注意的小问题。
当然,在教训的背后,也让我收获了很多东西,首先是信心,要对自己有信心,以现在看来,对于不了解linux、sip、rtp、xmpp,网络知识匮乏的我们,能够把一个完整的SIP服务搭建起来并且运行,实在是一件不可想象的事情,可是一步一步的也被我们搞出来了,可见所有问题在正确的方法下都是可以解决的,解决问题的关键不在于问题本身,而在于自己对解决这个问题有没有信心和决心,只有这样,你才会在不断地挫折之中逐渐找到正确的答案。
对MVC模式的理解
自从接触Java以来,无论是JavaWEB应用中Struts框架,还是Java swing桌面应用,都无数次听到MVC这个概念,那么到底什么是MVC,在这里我谈一下个人的理解。
首先,MVC是面向对象软件设计的一种模式,设计的使用目的是为了减少软件日益增长的复杂度,使得软件的可维护性,可复用性得到提高。
进一步说,MVC设计模式的应用偏重于GUI相关,即所谓图形用户界面,比如说office,QQ这种桌面应用程序,或者是基于浏览器的网络应用,这些应用都需要大量的窗口、菜单、文本域来与用户进行交互。而像服务器这种应用,基本不需要GUI。
GUI的产生是为了让非专业人员使用计算机,而不用死记硬背大量命令,它的特点是可以减少用户的认知负担,同时可以满足不同用户的创意需求,其中最重要就是简单、形象和美观。为了满足GUI的需要,我们有时不能够将数据直接展现出来,而需要在其外面包装上一层GUI,比如,一组统计数据,可以用表来展示,也可以用柱状图来表示,也可以用饼状图来表示,这样一来,原本只需要输出几个数据,现在却要做3份工作,加大了软件的复杂度。而MVC模式正是为了简化GUI开发的复杂性而出现的。
计算机中,一切都可以最终归结为数据,在GUI应用中,数据可以分为GUI数据和应用数据,前者代表GUI的状态,例如一个按钮按下时为0松开时为1,后者表示与GUI无关的数据,例如一张报表,它可以是一张GUI中的表,也可以使GUI中的柱状图。在MVC模式实现GUI的过程中, MVC模式分离了Model和View这两者,分离了以上所说的数据和显示,并抽象地设置了Controller这个对象。当Model或者View更新时,它们需要向Controller发送一个状态改变的事件信息,由Controller负责更新其对应的状态。
HTTP是一种无状态协议,其本质上由一个请求和一个响应组成:浏览器请求一个特定 URL(可能还提供补充数据),服务器用一个响应页面来应答。尽管最终用户可能觉得他们的网上冲浪过程由一系列连续的步骤组成,但是对于协议来说,每个交付的页面都是相互独立的;任何显示仅仅是与最近的 URL 请求对应的输出。交互发生在客户端到服务器的提交上,对于用户来说,浏览器可以表示View方,当用户点击提交按钮时,要求服务器作出相应的反应,来更新自身的View状态。在Struts中,用户的每次提交都会触发一个action,可以将这个action看做一个事件,每一个action都会被ActionServlet类这个Controller拦截,然后访问Model来更新View信息。这样做的好处在于,当我们需要对数据以不同的View呈现时,只需要新加入一个View页面,并将其指向对应的action即可,这样我们事实上只加入了新的内容,而没有对以前的代码进行修改,符合“开闭原则”,而且只需要修改View而实现了对Controller和Model的重用。