Android SIP电话应用开发实战

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文深入探讨了基于SIP协议的Android IP电话应用程序的开发过程。SIP协议是用于多媒体通信会话控制的文本协议,而Android平台上的SIP支持始于2.3版本。开发者需要掌握TCP/IP协议、Java编程以及使用Android SDK中的SIP API。此外,还需利用Apache等开源库,优化应用性能,并确保通信安全。通过实践,开发者可以构建出高效稳定的VoIP解决方案。 基于SIP协议的Android端IP电话APP.zip

1. SIP协议基础与应用

SIP协议简介

会话初始协议(Session Initiation Protocol,SIP)是一个应用层控制协议,用于建立、修改和终止多方通信会话。SIP被广泛应用于VoIP(Voice over IP)、即时消息、在线游戏和视频会议等多种互联网通信技术中。

SIP协议的作用

SIP协议的基本作用包括:

  1. 用户定位:SIP通过地址解析找到通信参与者的位置。
  2. 用户可用性:通过SIP通知用户是否可以参与通信。
  3. 会话建立:SIP协调双方建立会话,协商会话参数如音频和视频格式。
  4. 会话修改:SIP可以在通信过程中动态修改会话参数。
  5. 会话终止:SIP用于结束通信会话。

SIP在现代通信中的应用

SIP协议的灵活和可扩展性使其在现代通信系统中扮演了核心角色。企业级通信系统、IP PBX(Private Branch Exchange,私有分组交换机)、统一通信(UC)解决方案以及各种社交和即时通讯应用都采用了SIP技术。通过SIP,用户可以在不同的网络、设备之间无缝切换,享受丰富多样的实时通信服务。

以上内容是《SIP协议基础与应用》第一章的概述。接下来的内容将会更深入地探讨SIP协议的具体细节,以及它在Android平台上的实现与优化。

2. Android平台SIP支持与API使用

2.1 Android SIP功能介绍

2.1.1 SIP在Android中的位置和作用

Android作为一款基于Linux内核的移动操作系统,提供了一套丰富的API来支持SIP协议,使得开发者可以在移动设备上实现SIP通信功能。SIP(Session Initiation Protocol,会话发起协议)是一个应用层的控制协议,主要用于创建、修改和终止跨越IP网络的用户会话。在Android中,SIP功能被广泛应用于VoIP(Voice over Internet Protocol,网络电话)应用中,如即时通讯软件、网络电话和在线视频会议等。Android的SIP支持使得开发者可以创建更丰富的通信应用,推动了移动通信技术的发展。

SIP在Android中的位置可以从以下几个方面理解: 1. 应用层协议支持 :Android支持SIP作为标准的网络通信协议之一,让应用可以直接在应用层创建语音和视频通信会话。 2. 硬件资源的集成 :Android设备通常包含麦克风、扬声器、摄像头等硬件资源,SIP功能与这些硬件资源相结合,可以实现音视频通话。 3. 网络服务的扩展 :通过SIP,Android应用可以实现网络服务的扩展,允许用户在移动网络或Wi-Fi网络上进行通信。

2.1.2 Android支持的SIP协议版本

Android的SIP库支持多个版本的SIP协议,包括SIP 2.0。开发者可以根据目标用户群体的设备版本和需求选择合适的协议版本进行应用开发。需要注意的是,不同的Android版本可能对SIP支持有所差异,较新的Android版本通常会支持更新版本的SIP协议。因此,开发者在选择SIP协议版本时需要考虑到目标设备的兼容性和应用的可维护性。

2.2 Android SIP API详解

2.2.1 核心API的使用方法

Android为SIP通信提供了多个核心API,主要包括以下几个: - SipManager : 用于管理SIP配置和会话。 - SipProfile : 用于配置SIP账户的参数。 - SipAudioCall : 用于管理音频呼叫的相关操作。 - SipVideoCall : 用于管理视频呼叫的相关操作。 - SipRegistrationListener : 用于监听SIP账户的注册状态。 - SipCallListener : 用于监听呼叫的状态。

要使用这些核心API,首先需要在AndroidManifest.xml中声明网络权限(INTERNET)和SIP权限(SIP),以及设置VoIP的权限声明。例如:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SIP" />
<uses-permission android:name="android.permission.VOIP_CALL_WIDTHDRAW"></uses-permission>

随后,可以通过SipManager类创建SipProfile和SipAudioCall等对象。SipManager类使用如下:

SipManager mSipManager;
mSipManager = SipManager.newInstance(context);
SipProfile myAccount = mSipManager.open(mSipAudioCall, SipProfileتطريقة getDefaultAccountName(context), null);
2.2.2 SIP会话的生命周期管理

SIP会话的生命周期管理对于保证通话质量和用户体验至关重要。主要包括以下几个状态: - 空闲状态(Idle) :会话处于初始状态,等待客户端发起呼叫或接收呼叫。 - 呼叫状态(Calling) :会话正在拨打电话。 - 连接状态(Connected) :已成功建立连接,可以进行通话。 - 结束状态(Ended) :会话已结束,资源被释放。

通过SipAudioCall或SipVideoCall对象的方法,可以管理会话的状态。例如,使用 makeCall() 方法可以发起呼叫,使用 answerCall() 方法可以接听呼叫,而 endCall() 方法则用于结束当前的呼叫。

2.2.3 常见事件和错误处理

SIP应用在运行过程中会触发各种事件,例如呼叫的接通、通话过程中的网络问题等。通过注册SipCallListener监听器,开发者可以监听这些事件并做出相应的处理。以下是一个简单的监听器实现示例:

SipCallListener mSipCallListener = new SipCallListener() {
    @Override
    public void onCallAdded(SipAudioCall call) {
        // 新的呼叫已经添加
    }

    @Override
    public void onCallRemoved(SipAudioCall call) {
        // 呼叫已被移除
    }

    @Override
    public void onCallEstablished(SipAudioCall call) {
        // 呼叫已成功建立
    }

    @Override
    public void onCallEnded(SipAudioCall call) {
        // 呼叫已结束
    }

    @Override
    public void onCallBusy(SipAudioCall call) {
        // 呼叫正在忙
    }

    @Override
    public void onCallRing(SipAudioCall call) {
        // 呼叫正在响铃
    }

    // 其他回调方法可根据需要实现...
};

错误处理机制也同样重要,SIP API提供了一套错误代码,如 ERROR_BAD_ADDRESS ERROR_REQUEST_TIMEOUT 等,可以用于判断和处理不同类型的错误。对于每一个错误状态,开发者应该根据错误的类型和场景采取相应的恢复策略或给用户明确的提示。

3. TCP/IP协议在网络传输中的角色

3.1 TCP/IP协议的基本概念

3.1.1 协议栈的层次结构

TCP/IP是一个协议族,其核心协议为传输控制协议(TCP)和互联网协议(IP)。TCP/IP协议栈分为四个层次:链路层、网络层(包含IP协议)、传输层(包含TCP和用户数据报协议UDP),以及应用层。每一层负责不同的网络通信功能,数据在发送时从上层向下层传递,接收时则反向进行。

  • 链路层 :负责在相邻网络节点之间的帧传输。
  • 网络层 :IP协议负责将数据包从源传输到目的地,跨越多个网络。
  • 传输层 :TCP提供面向连接的服务,确保数据可靠传输;UDP提供无连接服务,适用于对传输速率有高要求的场景。
  • 应用层 :负责为应用程序提供网络服务,如HTTP、FTP和SMTP等。

3.1.2 TCP/IP在网络通信中的作用

TCP/IP协议栈的每一层都有其独特的职责,它们共同工作以保证网络数据的正确传输。IP协议位于网络层,它通过IP地址定位网络上的设备,并将数据包发送至目的地。TCP位于传输层,它通过三次握手建立连接,确保数据包的顺序和完整性。整个TCP/IP协议栈的设计目标是使复杂的数据网络能够以一种可靠、有序的方式运行。

3.2 SIP与TCP/IP的协同工作

3.2.1 SIP数据包的传输机制

SIP协议使用UDP和TCP作为其传输层协议。SIP消息作为数据包在网络中传输时,可以采用无连接的UDP或者面向连接的TCP。使用TCP可以提供更加可靠的通信保证,但是会增加额外的连接建立和维护开销。在实现SIP应用时,可以根据实际需求选择合适的传输协议。

3.2.2 网络延迟和数据包丢失的影响

在网络传输中,延迟和数据包丢失可能会造成通信质量下降。TCP通过序列号和确认应答机制保证数据的正确顺序和完整性。如果TCP检测到丢包,它会重新发送丢失的数据包。对于实时通信应用,如VoIP,网络延迟可以影响实时性,因此需要在应用中实现适当的策略来处理这种情况。

3.2.3 保障通信质量的策略

为了解决延迟和丢包的问题,可以采取一些策略来优化SIP通信质量。例如:

  • 使用实时传输协议(RTP)与实时控制协议(RTCP)来管理实时数据流。
  • 实现语音和视频数据的优先级标记,确保其在网络拥堵时能够优先传输。
  • 通过动态带宽调整来适应网络条件的变化。
  • 在网络质量差的情况下,自动降低音频和视频的质量以保证通信的连续性。

接下来,我们通过一个简单的mermaid流程图来展示SIP数据包传输的基本流程,以及在TCP/IP协议栈中的协同工作方式。

4. Java编程在网络应用中的应用

4.1 Java在Android SIP应用中的角色

4.1.1 Java与Android SIP的集成

在Android平台上,Java作为主要的开发语言之一,对于SIP(Session Initiation Protocol)应用的集成发挥着关键作用。Java通过其强大的标准库以及Android提供的特定API,使得开发者能够快速实现SIP通信功能。具体到集成,开发者可以在Android项目中使用Java代码来调用Android SDK中提供的SIP相关的类库,实现注册、呼叫、媒体传输等核心功能。

与SIP集成的过程涉及到几个关键步骤,首先是在AndroidManifest.xml中声明对SIP功能的使用权限。然后,在应用代码中初始化SIP服务,并使用合适的API来管理SIP通话和会话。例如,可以利用 SipManager 类来管理网络状态,以及 SipProfile 来设置SIP账户的参数。

4.1.2 Java网络编程基础

Java网络编程的基础为Android SIP应用提供了底层的通信支持。在Java中,主要通过 java.net 包下的类来实现网络操作,包括套接字编程。Java中的套接字分为两种类型,即面向连接的TCP套接字和无连接的UDP套接字。这些套接字类允许开发者创建网络连接,并通过网络发送和接收数据。

在Android SIP应用中,这两种套接字通常被用于不同的通信场景。例如,SIP协议的信令传输一般使用TCP协议,因为它需要可靠的数据传输和确保数据完整性的机制。而对于媒体数据的传输,可能更倾向于使用UDP协议,因为它的传输效率更高,延迟更低,对于实时通信更为关键。

4.2 Java网络编程实践

4.2.1 套接字编程原理

套接字编程是基于客户端-服务器模型的,任何网络通信的双方都会扮演客户端和服务器的角色。对于SIP应用而言,这通常意味着手机(客户端)与SIP服务器之间的通信。Java中的 Socket 类代表了一个套接字,客户端通过创建Socket实例并连接到服务器的IP地址和端口来发起通信。

服务器端则会监听一个端口,等待客户端的连接请求。当连接建立后,双方可以使用输入/输出流( InputStream OutputStream )进行数据交换。在SIP应用中,这种交换通常是通过特定的协议消息来实现的,例如SIP的REGISTER、INVITE等消息。

4.2.2 Java中的UDP和TCP通信实现

Java提供了两个核心类来处理UDP和TCP通信,分别是 DatagramSocket Socket 类。 DatagramSocket 用于创建UDP连接,发送和接收数据包,而 Socket 类则用于基于TCP的连接。

对于UDP通信,开发者可以使用 DatagramSocket DatagramPacket 类来发送和接收数据。对于TCP通信,则使用 Socket 类和 ServerSocket 类来分别创建客户端和服务器端的连接。TCP连接提供的是一个稳定的数据流,适用于需要可靠传输的场景。

在实际开发中,开发者需要根据SIP协议和实际应用的需求来选择合适的通信方式。例如,对于实时性强的语音通话,通常选择UDP进行媒体传输,而对于认证和注册过程,则更多地使用TCP以确保数据的准确传递。

// TCP通信示例代码
Socket socket = new Socket("server_address", port);
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();

// 发送数据
output.write("Hello, Server!".getBytes());
output.flush();

// 接收数据
byte[] buffer = new byte[1024];
int bytesRead = input.read(buffer);
String receivedData = new String(buffer, 0, bytesRead);

socket.close();

在上述TCP通信示例中,创建了一个TCP连接,并通过输出流发送了一个字符串消息,然后通过输入流接收了来自服务器的数据。代码执行过程中,网络异常处理和数据的正确编码处理是必不可少的,以确保通信的稳定性和数据的正确性。

而对于UDP通信,开发者可以使用如下代码段:

// UDP通信示例代码
DatagramSocket socket = new DatagramSocket();
byte[] data = "Hello, Server!".getBytes();
InetAddress address = InetAddress.getByName("server_address");

DatagramPacket sendPacket = new DatagramPacket(data, data.length, address, port);
socket.send(sendPacket); // 发送数据

byte[] buffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);
socket.receive(receivePacket); // 接收数据

String receivedData = new String(receivePacket.getData(), 0, receivePacket.getLength());

socket.close();

在UDP通信示例中,创建了一个 DatagramSocket 实例,并创建了一个数据包用于发送和接收数据。UDP的接收操作是阻塞的,直到有数据包到达。在SIP应用中,UDP用于SIP消息的传输需要特别注意异常处理,以避免丢包或重发等问题。

5. Apache开源库在Android SIP应用中的应用

5.1 Apache开源库概述

5.1.1 常用Apache库及其功能介绍

在Android SIP应用的开发过程中,Apache开源库提供了丰富的工具包和模块,极大地简化了网络编程和数据处理的复杂性。例如, Apache HTTP Components 用于处理HTTP协议的请求与响应; Apache Commons Codec 库提供了数据编码和解码的功能; Apache Commons Lang 则提供了语言相关的辅助功能,如字符串操作、对象比较等。

Apache库不仅功能丰富,而且因其稳定性和社区支持而广受开发者喜爱。这些库通常都是经过严格测试的,并且在多种项目中得到了广泛的应用,因此对于提升开发效率和应用性能都有着显著的作用。

5.1.2 选择合适库的重要性

在众多可用的Apache库中,选择合适的库对于项目的成功至关重要。开发者需要基于项目的具体需求来决定使用哪个库。例如,在处理JSON数据时,可以选择 org.json 库或者 Jackson Gson 这类更为强大的库。对于网络请求,如果是简单的HTTP GET或POST请求,可以使用Android内置的 HttpURLConnection ,而对于更复杂的场景,则可能会倾向于使用 Apache HttpClient 或者 OkHttp 等更为高级的库。

选择合适的库可以减少开发时间,提高代码质量,并减少潜在的bug。它还可以让开发团队更容易地维护和扩展项目。此外,合适的库还能与Android平台保持良好的兼容性,并利用Android特有的优化技术,提高应用性能。

5.2 Apache库在网络数据处理中的应用

5.2.1 数据压缩与解压缩

在网络传输中,数据压缩可以大幅度减小数据包的大小,从而提高传输效率和降低带宽消耗。Apache Commons Codec库中的 CompressorStream DecompressorStream 类可以用来实现数据的压缩和解压缩。以下是一个简单的示例,展示了如何对字符串数据进行压缩和解压缩:

import org.apache.commons.codec.compression.GzipCompressorOutputStream;
import org.apache.commons.codec.compression.GzipDecompressorInputStream;

String originalString = "This is the original string to be compressed";
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (GzipCompressorOutputStream compressorStream = new GzipCompressorOutputStream(byteArrayOutputStream)) {
    compressorStream.write(originalString.getBytes(StandardCharsets.UTF_8));
    compressorStream.flush();
} catch (IOException e) {
    e.printStackTrace();
}

byte[] compressedBytes = byteArrayOutputStream.toByteArray();

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedBytes);
try (GzipDecompressorInputStream decompressorStream = new GzipDecompressorInputStream(byteArrayInputStream)) {
    ByteArrayOutputStream result = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length;
    while ((length = decompressorStream.read(buffer)) != -1) {
        result.write(buffer, 0, length);
    }
    String decompressedString = result.toString(StandardCharsets.UTF_8.name());
    System.out.println(decompressedString);
} catch (IOException e) {
    e.printStackTrace();
}

在上述代码中,使用了GZIP格式对字符串数据进行压缩和解压缩。 GzipCompressorOutputStream 用于压缩数据,而 GzipDecompressorInputStream 用于解压缩数据。

5.2.2 XML与JSON数据格式处理

在网络应用中,XML和JSON是两种广泛使用的数据交换格式。Apache Commons Lang库中的 StringUtils 类提供了一些基本的字符串操作功能,而 Apache XML Jackson Gson 库则提供了专门用于解析和生成XML和JSON数据的工具。

对于JSON数据处理, Gson 库是一个非常流行的选择,因为它提供了简单而强大的API来将Java对象转换为JSON格式的字符串,同时也支持将JSON字符串转换回Java对象。以下是一个使用 Gson 库将Java对象转换为JSON字符串的例子:

import com.google.gson.Gson;

class Person {
    private String name;
    private int age;

    // getters and setters
    // constructor
}

// 创建一个Person对象并设置属性
Person person = new Person();
person.setName("Alice");
person.setAge(30);

// 创建一个Gson实例
Gson gson = new Gson();

// 将Person对象转换为JSON字符串
String personAsJson = gson.toJson(person);

System.out.println(personAsJson);

使用 Gson 库可以很容易地实现JSON和Java对象之间的转换,它能够处理复杂的数据结构和嵌套的对象。

5.2.3 HTTP通信的封装和优化

Apache HTTP Components提供了 HttpClient 接口,它是一个用于HTTP请求和响应的高级客户端API。它支持许多高级功能,如HTTPS连接、请求拦截器和响应处理程序。 HttpClient 适用于需要灵活配置HTTP连接的场景,同时也可以用于执行自动重试和重定向处理。

下面的示例代码展示了如何使用 HttpClient 发送一个简单的HTTP GET请求:

import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet("http://example.com");

try (CloseableHttpResponse response = httpClient.execute(request)) {
    // 响应状态码
    int statusCode = response.getStatusLine().getStatusCode();
    if (statusCode == 200) {
        // 处理响应体
        String responseBody = EntityUtils.toString(response.getEntity());
        System.out.println(responseBody);
    } else {
        // 处理错误
        System.out.println("Error: " + statusCode);
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    try {
        httpClient.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

在这个示例中,创建了一个 HttpGet 对象,并指定了目标URL。使用 CloseableHttpClient 执行HTTP请求,并通过 CloseableHttpResponse 对象处理响应。 EntityUtils.toString() 方法用于获取响应体的内容,这对于获取返回的数据非常有用。

Apache开源库在Android SIP应用中扮演着重要的角色,为开发者提供了强大的工具和功能。通过合理地利用这些库,开发者可以有效地简化网络编程和数据处理工作,从而更加专注于实现业务逻辑。随着应用的不断演进,选择合适的Apache库对于确保应用的性能、安全性和可维护性至关重要。

6. Sipdroid开源项目参考与分析

6.1 Sipdroid项目概述

6.1.1 Sipdroid功能和架构

Sipdroid是一个开源的SIP客户端,它允许在Android设备上进行语音呼叫和多媒体通信。该应用支持基于IMS网络的VoIP通话,同时也支持标准的SIP服务。其主要功能包括:

  • SIP注册和注销
  • 语音和视频通话
  • 消息发送与接收
  • 高级呼叫转移设置
  • 实时呼叫质量监控
  • 会话描述协议(SDP)协商

架构方面,Sipdroid采用了模块化的设计,主要分为用户界面层、业务逻辑层以及网络通信层。这样的结构不仅有助于提高代码的可维护性,也使得项目易于扩展和维护。

6.1.2 Sipdroid在行业中的应用案例

由于Sipdroid的灵活性和开放性,它已经被多个行业领域采纳和应用。一些典型的应用案例包括:

  • 内部通信系统:企业内部员工间可通过Sipdroid建立稳定的VoIP通话,降低通信成本。
  • 远程医疗咨询:医生通过Sipdroid与远程患者进行视频通话,提供专业咨询和服务。
  • 灾害应急响应:在自然灾害发生时,救援人员利用Sipdroid建立起临时的通信网络,保证关键信息的传递。

6.2 Sipdroid源码分析

6.2.1 主要模块和类的设计

深入分析Sipdroid的源码,我们可以发现它主要包括以下几个核心模块:

  • AccountManager :管理用户的账户信息和SIP账户设置。
  • CallManager :处理来电、去电以及通话状态的管理。
  • MediaManager :管理音频和视频的捕获以及播放,包括音量控制和设备切换等。
  • NetworkManager :负责网络连接的检测、切换和异常处理。

这些类通常包含在Sipdroid的 com.sipdroid 包中,每个类都承担着特定的责任,通过接口和继承关系紧密协作。

6.2.2 关键功能的实现机制

以Sipdroid中的呼叫功能为例,我们来分析关键功能的实现机制:

  • 呼叫建立: 当用户发起呼叫时, CallManager 会处理呼叫请求,并使用 AccountManager 中的账户信息进行SIP邀请。媒体协商则由 MediaManager 负责,选择合适的编解码器和会话描述参数。
  • 呼叫中止: 当用户挂断电话时, CallManager 会发送BYE消息给对方,并且确保所有关联的媒体流被正确关闭。
  • 呼叫转移和保持: 这些功能依赖于对SIP协议中RE-INVITE消息的处理,需要修改现有的呼叫状态并通知网络上的其他设备。

为了深入了解Sipdroid的工作流程,建议阅读其官方文档,并尝试下载源码进行编译和运行。通过阅读源码,开发者可以学习到如何在Android平台上构建复杂的网络应用,以及如何管理各种网络状态和事件。

Sipdroid作为开源项目,其代码和功能实现是透明的,任何开发者都可以基于现有代码进行二次开发和优化。通过持续的社区支持和开发者贡献,Sipdroid能够不断适应新的技术标准和市场需求。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文深入探讨了基于SIP协议的Android IP电话应用程序的开发过程。SIP协议是用于多媒体通信会话控制的文本协议,而Android平台上的SIP支持始于2.3版本。开发者需要掌握TCP/IP协议、Java编程以及使用Android SDK中的SIP API。此外,还需利用Apache等开源库,优化应用性能,并确保通信安全。通过实践,开发者可以构建出高效稳定的VoIP解决方案。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

摘自:http://mbstudio.spaces.live.com/blog/cns!C898C3C40396DC11!955.entry 2007/1/30 oSIP协议栈(及eXoSIP,Ortp等)使用入门(原创更新中) (CopyLeft by Meineson | www.mbstudio.cn,原创文章,欢迎转载,但请保留出处说明!) 本文档最新版本及文中提到的相关源码及VC6工程文件请在本站找,嘿嘿~~ (首页的SkyDriver公开文件夹中,可能需要用代理才能正常访问该空间——空间绝对稳定,不会丢失文件!) (最近工作重心不在SIP开发,SO本文档也没有机会更新,有技术问题也请尽量咨询他人,本人不一定能及时回复。)   一直没空仔细研究下oSIP,最近看到其版本已经到了3.x版本,看到网上的许多帮助说明手册都过于陈旧,且很多文档内容有点误人子弟的嫌疑~~   Linux下oSIP的编译使用应该是很简单的,其Install说明文档里也介绍的比较清楚,本文主要就oSIP在Windows平台下VC6.0开发环境下的使用作出描述。   虽然oSIP的开发人员也说明了,oSIP只使用了标准C开发库,但许多人在Windows下使用oSIP时,第一步就被卡住了,得不到oSIP的LIB库和DLL库,也就没有办法将oSIP使用到自己的程序中去,所以第一步,我们将学习如何得到oSIP的静态和动态链接库,以便我们自己的程序能够使用它们来成功编译和执行我们的程序。 第一阶段: ------------------------------------------------------   先创建新工程,网上许多文档都介绍创建一个Win32动态链接库工程,我们这里也一样,创建一个空白的工程保存。   同样,将oSIP2版本3.0.1 src目录下的Osipparser2目录下的所有文件都拷到我们刚创建的工程的根目录下,在VC6上操作: Project-Add To Project-Files   将所有的源程序和头文件都加入到工程内,保存工程。   这时,我们可以尝试编译一下工程,你会得到许多错误提示信息,其内容无非是找不到osipparser2/xxxxx.h头文件之类。   处理:在Linux下,我们一般是将头文件,lib库都拷到/usr/inclue;/usr/lib之类的目录下,c源程序里直接写#include 时,能直接去找到它们,在VC里,同样的,最简单的方法就是将oSIP2源码包中的Include目录下的 osipparser2目录直接拷到我们的Windows下默认包含目录即可,这个目录在VC6的Tool-Options-Directories里设置,(当然,如果你知道这一步,也可以不用拷贝文件,直接在这里把oSIP源码包所在目录加进来就可以了),默认如果装在C盘,目录则为 C:\Program Files\Microsoft Visual Studio\VC98\Include。   这时,我们再次编译我们的工程,顺利编译,生成osipparser2.dll,这时,网上很多文档里可能直接就说,这一步也会生成libs目录,里面里osipparser2.lib文件,但我们这里没有生成:)   最简单的方法,不用深究,直接再创建一个工程,同上述创建动态链接库方法,创建一个Win32静态链接库工程,直接编译,即可得到osipparser2.lib。 ------------------------------------------------------   上面,我们得到了Osip的解析器开发库,下面再编译完整的Osip协议栈开发库,同样照上述方法,分别创建动态链接库工程和静态链接库工程,只是要拷的文件换成src下的osip目录下文件和include下的osip目录,得到osip2.dll和osip2.lib。   在编译osip2.dll这一步可能会再次得到错误,内容含义是找不到链接库,所以,我们要把前面编译得到的osipparser2.lib也拷到osip工程目录下,并在VC6中操作:   Project-Setting-Link中的Object/Library Modules: kernel32.lib user32.lib ... xxx.lib之类的内容最后增加: osipparser2.lib   保存工程后再次编译,即可成功编译osip2.dll。 ------------------------------------------------------   至此,我们得到了完整的oSIP开发库,使用时,只需在我们的程序里包含oSIP的头文件,工程的链接参数里增加osipparser2.lib和osip2.lib即可。 ------------------------------------------------------   下面我们验证一下我们得到的开发库,并大概了解一下OSIP的语法规范。   在VC里创建win32控制台程序工程,将libosip源码包的SRC目录下的Test目录内的C源程序随便拷一个到工程时,直接编译(工程设置里照前文方法在link选项里增加osip2.lib,osipparser2.lib引用我们之前成功编译得到的静态库文件)就可以运行(带参数运行,参数一般为一个文本文件,同样从Test目录的res目录里拷一个与源文件同名的纯文本文件到工程目录下即可)。   该目录下的若干文件基本上是测试了Osip的一些基本功能函数,例如URI解析之类,可以大概了解一下oSIP的语法规范和调用方法,同时也能校验一下之前编译的OSIP开发库能否正常使用,成功完成本项工作后,可以进入下一步具体的oSIP的使用学习了。 ------------------------------------------------------   由于oSIP是比较底层的SIP协议栈实现,新手较难上手,而官方的示例大都是一些伪代码,需要有实际的例子程序参考学习,而最好的例子就是同样官方发布的oSIP的扩展开发库exosip2,使用exoSIP可以很方便地快速创建一个完整的SIP程序(只针对性地适用于SIP终端开发用,所以我们这里只是用它快速开发一个SIP终端,用来更方便地学习oSIP,要想真正掌握SIP的开发,需要掌握oSIP并熟读RFC文档才行,exoSIP不是我们的最终学习目的),通过成功编译运行一个自己动手开发出的程序,再由浅入深应该是初学都最好的学习方法通过对使用exosip开发库的使用创建自己的SIP程序,熟悉后再一个函数一个函数地深入学习exosip提供的接口函数,就可以深入理解osip 了,达到间接学习oSIP的目的,同时也能从eXoSIP中学习到正确使用oSIP的良好的编程风格和语法格式。   而要成功编译ExoSIP,似乎许多人被难住了,直接在XP-sp2上,用VC6,虽然你使用了eXoSIP推荐的winsock2.h,但是会得到一个 sockaddr_storage结构不能识别的错误,因为vc6自带的开发库太古董了,需要升级系统的Platform SDK,下载地址如下: http://www.microsoft.com/msdownl ... PSP2FULLInstall.htm(VC6的支持已经停止,这是VC6能使用的最新SDK)   成功安装后编译前需加OSIP_MT宏,以启用线程库,否则在程序中使用eXoSIP库时会出错,而编译时也会得到许多函数未定义的Warning提示,编译得到exosip2.lib供我们使用,当然,在此之前需要成功编译了osip2和osipparser2,而在之后的实际使用时,发现oSIP也需要增加OSIP_MT宏,否则OSIP_MT调用oSIP的线程库时会出错,所以我们需要重新编译oSIP了:),因为eXosip是基于oSIP的(同上方式创建静态和动态链接库工程,并需在Link中手工添加oSIP和oSIPparser的lib库)。 ------------------------------------------------------   创建新工程,可以是任意工程,我们从最简单的Win32控制台程序开始,为了成功使用oSIP,我们需要引用相关库,调用相关头文件,经过多次试验,发现需要引用如下的库: exosip2.lib osip2.lib osipparser2.lib WSock32.Lib IPHlpApi.Lib WS2_32.Lib Dnsapi.lib   其中,除了我们上面编译得到的三个oSIP库外,其它库都是系统库,其中有一些是新安装的Platform SDK所新提供的。   至此,我们有了一个简单的开发环境了,可以充分利用网上大量的以oSIP为基础的代码片段和官方说明文档开始具体函数功能的测试和使用了:) ------------------------------------------------------   我们先进行一个简单的纯SIP信令(不带语音连接建立)的UAC的SIP终端的程序开发的试验(即一个只能作为主叫不能作为被叫的的SIP软电话模型),我们创建一个MFC应用程序,对话框模式,照上面的说明,设置工程包含我们上面得到的oSIP的相关开发库及SDK的一些开发库,并且由于默认LIBC的冲突,需要排除MSVCRT[D]开发库(其中D代表Debug模式下,没有D表示Release模式下),直接使用eXosip的几个主要函数就可以创建一个基本的SIP软电话模型。   其主要流程为:   初始化eXosip库-启动事件监听线程-向SIP Proxy注册-向某SIP终端(电话号码)发起呼叫-建立连接-结束连接   初始化代码: int ret = 0; ret = eXosip_init (); eXosip_set_user_agent("##YouToo0.1"); if(0 != ret) { AfxMessageBox("Couldn't initialize eXosip!\n"); return false; } ret = eXosip_listen_addr (IPPROTO_UDP, NULL, 0, AF_INET, 0); if(0 != ret) { eXosip_quit (); AfxMessageBox("Couldn't initialize transport layer!\n"); return false; }   启动事件监听线程: AfxBeginThread(sip_uac,(void *)this);   向SIP Proxy注册: eXosip_clear_authentication_info(); eXosip_add_authentication_info(uname, uname, upwd, "md5", NULL); real_send_register(30);  /* 自定义函数代码请见源码 */   发起呼叫(构建假的SDP描述,实际软电话使用它构建RTP媒体连接): osip_message_t *invite = NULL; /* 呼叫发起消息体 */ int i = eXosip_call_build_initial_invite (&invite, dest_call, source_call, NULL, "## YouToo test demo!"); if (i != 0) { AfxMessageBox("Intial INVITE failed!\n"); } char localip[128]; eXosip_guess_localip (AF_INET, localip, 128); snprintf (tmp, 4096, "v=0\r\n" "o=josua 0 0 IN IP4 %s\r\n" "s=conversation\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio %s RTP/AVP 0 8 101\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=rtpmap:101 telephone-event/8000\r\n" "a=fmtp:101 0-11\r\n", localip, localip, "9900"); osip_message_set_body (invite, tmp, strlen(tmp)); osip_message_set_content_type (invite, "application/sdp"); eXosip_lock (); i = eXosip_call_send_initial_invite (invite); eXosip_unlock ();   挂断或取消通话: int ret; ret = eXosip_call_terminate(call_id, dialog_id); if(0 != ret) { AfxMessageBox("hangup/terminate Failed!"); }   可以看到非常简单,再借助于oRTP和Mediastreamer开发库,来快速为我们的SIP软电话增加RTP和与系统语音API接口交互及语音编码功能,即可以快速开发出一个可用的SIP软电话,关于oRTP和Mediastreamer的相关介绍不是本文重点,将在有空的时候考虑增加相应使用教程,文章前提到的地方可以下载基本可用的完整SIP软电话的VC源码工程文件供参考使用,完全CopyLeft,欢迎转载,但请在转载时注明作者信息,谢谢! 第二阶段: ---------------------------------------------------   得到了一个SIP软电话模型后,我们可以根据软电话的实际运行表现(结合用Ethereal抓包分析)来进行代码的分析,以达到利用eXoSIP来辅助我们学习oSIP的最终目的(如要快速开发一个可用的SIP软电话,请至前面提到的论坛去下载使用oRTP和Mediastreamer快速搭建的一个基本完整可用的SIP软电话##YouToo 0.1版本的VC源码工程文件作参考)。   现在从eXosip的初始化函数开始入手,来分析oSIP的使用,这是第二阶段,第三阶段就是深入学习oSIP的源码了,但大多数情况下应该没有必要了,因为在第二阶段就有部分涉及到第三阶段的工作了,而且oSIP的源码也就大多是一些SIP数据的语法解析和状态机的实现,能深入理解了SIP协议后,这些只是一种实现方式,没必要完全去接受,而是可以用自己的方式和风格来实现一套,比如,更轻量化更有适用目的性的方式,oSIP则只起参考作用了。   eXosip_init()是eXosip的初始化函数,我们来看看它的内部实现:   首行是定义的 osip_t *osip,这在oSIP的官方手册里我们看到,所有使用oSIP的程序都要在最开始处声明一个osip_t的指针,并使用 osip_init(&osip)来初始化这个指针,销毁这个资源使用osip_release(osip)即可。   我们可以在代码中看到很多OSIP_TRACE,这是调试输出宏调用了函数osip_trace,可以用ENABLE_TRACE宏来打开调试以方便我们开发调试。   其它就是很多的eXosip_t的全局变量eXosip的一些初始化操作,包括最上面的memset (&eXosip, 0, sizeof (eXosip))完全清空和下面的类似eXosip.user_agent = osip_strdup ("eXosip/" EXOSIP_VERSION)的exosip变量的一些初始值设置,其中有一个eXosip.j_stop_ua = 0应该是一个状态机开关,后面可以看到很多代码检测这个变量来决定是否继续流程处理,默认置成了0表示现在exosip的处理流程是就绪的,即ua是 not stop的。      osip_set_application_context (osip, &eXosip)是比较有意思的,它让下面的eXosip_set_callbacks (osip)给osip设置大量的回调函数时,能让osip能访问到eXosip这个全局变量中设置的大量程序运行时交互的信息,相当于我们在VC下开启一个线程时,给线程传入的一个void指针指向我们的MFC应用程序的当前dialog对象实例,可以用void *osip_get_application_context (osip_t * osip)这个函数来取出指针来使用,不过好象exosip中并没有用到它,可能是留给个人自已扩展的吧:)      还能看到初始化代码前面有一段WIN32平台下的SOCK的初始化代码,可以知道eXosip是用的原生的winsock api函数,也就是我们可能以前学过的用VC和WINAPI写sock程序时(不是MFC),用到的那段SOCK初始代码,还有一段有意思的代码,就是 jpipe()函数,它们返回的是一个管道,一个有2个整型数值的数组(一个进一个出),查看其代码发现,非WIN32平台是直接使用的pipe系统函数,而WIN32下则是用一对TCP的本地SOCK连接来模拟的管道,一个SOCK写一个SOCK读,这段代码是比较有参考价值的:) j = 50; while (aport++ && j-- > 0) {   raddr.sin_port = htons ((short) aport);   if (bind (s, (struct sockaddr *) &raddr, sizeof (raddr)) transactionid)); }   即,只是打印一下调试,并没有完整实现什么功能,我们学习时,完全可以用相同的方法,定义一大堆回调函数,并不忙想怎么完全实现,先都是只打印一下调试信息,看具体的应用逻辑根据抓包测试分析和看调试看程序走到了哪一步,调用了哪一个回调,来明白具体回调函数要实现什么用途,再来实现代码就方便多了,当然,如果看透了RFC文档,应该从字面就能知道各个回调函数的用途了,这是后话,不是谁都能快速完全看懂RFC的,所以我们要参考eXosip:)      我们对其中的重要的回调函数进行逐个的分析:   ---------------------------   osip_set_cb_send_message (osip, &cb_snd_message) SIP消息发送回调函数   这个函数可能是最重要的回调函数之一,消息发送,包括请求消息和回应消息,一般情况下,状态机的状态就是由它控制的,发起一个消息初始化一个状态机,回应一个消息对状态机修改,终结消息发送结束状态机……   看cb_snd_message的函数实现,要以发现,其主要代码是对参数中的要发送的消息osip_message_t * sip进行分析,找出消息要发送的真实char *host,int port的值(这些参数可以省略,但要发送消息肯定需要host和port,所以要从sip中解析),最后根据sip中解析出的传输方式是TCP还是 UDP选择最终进行消息发送处理的函数cb_udp_snd_message,cb_tcp_snd_message处理(它们的参数一致,即本函数只是补全一些省略的参数并对消息进行合法性检查)。   **毕竟eXosip是一个通用的开发库,它考虑了要支持TCP,UDP,TCPs,IPV4,IPV6,WIN32,*nix,WINCE等等多样化的复杂环境,所以,我们可以略过我们暂时不需要的部分,比如,IPV6相关的代码实现等。      由于我们大多数情况下SIP是用的UDP,所以先来看一下cb_udp_snd_message的实现,它从全局变量exosip中获取可用的 sock,并尽最大能力解析出host和port(??难道前面的函数还不够解析彻底??如最终仍无port信息则默认设置为5060),使用 osip_message_to_str (sip, &message, &length)函数将要发送的格式化的SIP消息转换成能用SOCK传输的简单数据并发送即完成消息发送,代码中有许多复杂的环境探测和错误控制等等等等,我们可以暂时不用过多关注,可以继续向下,结尾处有一个keeplive相关代码,从代码字面分析,可能是SIP的Register消息的自动重发相关代码,可以在后面再细化分析。   cb_tcp_snd_essage的函数实现要比上文的udp的实现简单很多,主要是环境探测错误控制方面,因为毕竟tcp是稳定连接的,对比一下代码,可以看到主要流程还是将SIP消息转换后,发送到从SIP消息中解析出的host和port对应的目标。      看完两个函数,可以知道,eXosip需要有两个sock,是一个数组,0是给UDP用的,1是给TCP用的,要用SOCK当然要初始化,就是下文要介绍的eXosip的网络相关的初始化了,上面的exosip_init可以看成是这个开发库的系统初始化吧:)    至些,我们应该知道了oSIP开发的SIP应用程序的消息是从哪里发出的吧,对了,就是从这个回调函数里,所谓万事开头难,就象开发WIN32应用程序时,找到了WIN32程序的main函数入口下面的工作就好办了,下面就都是为一些事件消息开发对应的处理函数而已了:)   osip_set_kill_transaction_callback 事务终结回调函数   对应ICT,IST,NICT,NIST客户/服务器注册/非注册事务状态机的终结,主要是使用osip_remove_transaction (eXosip.j_osip, tr)将当前tr事务删除,再加上一系列的清理工作,其中,NICT即客户端的非Invite事务的清理比较复杂一些,要处理的内容也比较多,可以根据实际应用的情况进行有必要的清理工作:)   cb_transport_error 传输失败处理回调   对应于上面说到的四种事务状态机,如果它们在处理时失败,则在这时进行统一处理。   从代码可知,只是在NOTIFY,SUBSCRIBE,OPTION操作失败才进行处理,其它错误可直接忽略。   osip_set_message_callback 消息发送处理回调   根据type不同,表示不同的消息发送状态   OSIP_XXX_AGAIN 重发相关消息   OSIP_ICT_INVITE_SENT 发起呼叫   OSIP_ICT_ACK_SENT ACK回应   OSIP_NICT_REGISTER_SENT 发起注册   OSIP_NICT_BYE_SENT BYE发出   OSIP_NICT_CANCEL_SENT Cancel发出   OSIP_NICT_INFO_SENT,OSIP_NICT_OPTIONS_SENT,OSIP_NICT_SUBSCRIBE_SENT,OSIP_NICT_NOTIFY_SENT,OSIP_NICT_UNKNOWN_REQUEST_SENT   我们可以看到,eXosip没有对它们作任何处理,我们可以根据自己需要,比如,重发2xx消息前记录一下日志之类的,扩展一下retransmission的处理方式,发起Invite前记录一下通话日志等等。   OSIP_ICT_STATUS_1XX_RECEIVED uac收到1xx消息,一般是表示对端正在处理中,这时,主要是设置一下事务状态机的状态值,并对会话中的osip的一些参数根据返回值进行相应设置,里面有许多条件判断,但我们常用的一般是100,180,183的判断而已,暂时可以忽略里面复杂的判断代码。   OSIP_ICT_STATUS_2XX_RECEIVED uac收到2xx消息,这里主要跟踪一下Register情况下的2xx,表示注册成功,这时会更新一下exosip的注册字段值,以便让eXosip能自动维护uac的注册,BYE的2xx回应是终结消息,Invite的2xx回应,则主要是初始化一下会话相关的数据,表示已成功建立连接。   其它4xx,5xx,6xx则分别是对应的处理,根据实现情况进行概要的查看即可。   report_event (je, sip)是代码中用来进行事件处理的一个函数,跟踪后发现,其最终是使用了我们上文提到的jpipe管道,以便在状态机外实时观测状态机内的处理信息。      OSIP_NIST_STATUS_XXX_SENT即对应于上面的uac的处理,这里是uas的对应的消息处理,相比较于uac简单一点。   前面简单介绍了一下大量的回调函数及它们的概要处理逻辑,可能会比较混乱,暂时不用管它,只需要记得一个大概的形象,知道一个SIP处理程序是通过osip_set_cb_send_message回调函数来实现真实地发送各种SIP消息,并且SIP的标准事务模型是由oSIP实现好了,我们只需要给不同的事务状态设置不同的回调处理函数来处理事务,具体的状态变化和内部逻辑不用管就可以了。   下面来说一下消息处理回调函数用到的SOCK的初始化函数,即我们上面说的除了系统初始化外的网络初始化函数eXosip_listen_addr:   从上文知道了,系统将初始化两个SOCK,一个UDP一个TCP,但查看代码发现还有第三个,TCPs的,但好象还不能实用,现在不管它,代码首先是根据传输是UDP还是TCP来设置对应的数组值,并且如果没有提供IP地址和端口号,系统会自动取出本机网络接口并创建可用的SOCK(http_port 的方式暂不用考虑)。   SOCK初始化后,如何开始SIP事务的呢?看到这个调用eXosip.j_thread = (void *) osip_thread_create (20000, _eXosip_thread, NULL),对的,这里启用了一个线程,即,eXosip是调用oSIP的线程函数(没用系统提供的线程函数,是为了跨平台)进行事务处理的状态机逻辑是在一个线程中处理的,这样就明白了为什么一直没能看到顺序执行下来的程序启动代码了,接下去看,线程实际处理函数是_eXosip_thread,这里面的代码中,我们看到了上文提到的状态机控制开关变量while (eXosip.j_stop_ua == 0),即,当j_stop_ua设置为1时,osip_thread_exit ()结束事务处理即程序终结,再接下去看,_eXosip_execute是最终的处理函数了,而且它在程序未终结情况下是一直逻辑在执行,注意,要启用oSIP的多线程宏OSIP_MT。      看到_eXosip_execute的代码中有很多时间函数和变量,仔细看,除去一些控制代码,主要处理函数是eXosip_read_message (1, lower_tv.tv_sec, lower_tv.tv_usec),即取出消息,1表示只取出一条消息,其代码量非常的大,但同样的,其中也许多的控制代码和错误检测代码,我们在查看时可以暂时忽略掉它们。   eXosip_read_message读取消息时,即没有采用sock的block也没有用非block方式,而是采用了select方式,具体应用可查询fd_set相关文档。   根据jpipe_read (eXosip.j_socketctl, buf2, 499),我们可以估计,buf2中应该是保存的我们的控制管道的数据,具体作用至些还没有表现出来,应该是用来反映一些状态机内部的警示之类的信息,实际的SIP的处理的状态机的数据是存放在buf中,使用_eXosip_recvfrom获取的,获取后sipevent = osip_parse (buf, i)解析,使用osip_find_transaction_and_add_event (eXosip.j_osip, sipevent)来查询事件对应的事务状态机,找到后就如同其注解所说明的,/* handled by oSIP ! */,即我们上文设置的那一大堆回调函数,至此,我们知道了整个SIP应用所处理的大概流程了。   如果没有找到事务状态机呢?直接丢弃吗?不是的,如果这是一个回应消息,但没有事务状态机处理它,那它是一个错误的,要进行清理后才能丢弃,而如果是一个请求,那更不能丢弃了,因为UAS事务状态机要由它来启动创建的(回应消息表示本地发出了请求消息,即UAC行为,事务状态机应是由启动UAC的代码初始化启动的),整个逻辑应该是很简单的,但eXosip的实现代码却非常多,可见其花了非常多的精力在保证会话的稳定性和应付网络复杂情况上,我们可以对其进行大量的精简来构建满足我们需求的代码实现。   先来看错误的回应消息的处理函数eXosip_process_response_out_of_transaction,可以看到其代码就是一大堆的赋值语句,XXX= NULL,即将一大堆的运行时变量清空,再调用osip_event_free清空事件,或者就是一些复杂的情况下,需要通过解析现在的运行时数据,从中分析出“可能”的正在等待回应的对端,并发送相关终结通知消息等等,可以根据实际需要进行简化。   请求事件的处理 eXosip_process_newrequest,首先是对事件进行探测,MSG_IS_INVITE、MSG_IS_ACK、 MSG_IS_REQUEST……,对事件进行所属状态机分类,随后使用_eXosip_transaction_init (&transaction,(osip_fsm_type_t) tx_type,eXosip.j_osip, evt->sip)根据探测结果进行状态机初始化,实际调用的是osip_transaction_init,初始化后即将事件入状态机 osip_transaction_add_event (transaction, evt),由状态机自动处理后调用相应回调函数处理逻辑了。当然,eXosip为方便快速开发SIP终端应用,在下面又添加了许多自动化的处理代码,来和我们在回调函数中设置的处理代码相区分。   线程调用的事件处理函数代码最后是 if (eXosip.keep_alive > 0) {   _eXosip_keep_alive (); }   这段代码印证了上文提到了,keep_alive是用来设置是否自动重新注册,由_eXosip_keep_alive函数来实现自动将eXosip全局变量中保存的注册消息解析后自动根据需要重新向SIP服务器发起Register注册。   同样,因为注册消息发起是UAC的行为,将它放在这里,可以看出来所有事件消息的事务状态机处理都是在这里,只不过这里只创建UAS的事务状态机,UAC的事务状态机的创建则要继续到下面找了,从我们的YouToo软电话代码中可知,发起呼叫和发起注册分别调用了 eXosip_call_send_initial_invite,eXosip_register_send_register这两个函数(另外用到的两个build函数则是分别构建这两个send函数要发送的SIP消息),查看这两个函数可知,UAC的事务处理状态机是在这里进行初始化的。   eXosip_register_send_register中可以看到是_eXosip_transaction_init (&transaction, NICT, eXosip.j_osip, reg)初始化UAC状态机,实际也同UAS是调用的osip_transaction_init函数,同样使用 osip_transaction_add_event (transaction, sipevent)将事件入状态机,状态机随后将自动处理调用相应回调函数处理逻辑了。   另有osip_new_outgoing_sipmessage(reg),表示发送消息,到这里,我们应该可以理解,真实的发送操作,是要到由状态机处理后,调用了消息发送回调函数才真正地将注册消息发送出去的。   同注册消息发送,它是NICT状态机,呼叫消息的发送是ICT,由eXosip_call_send_initial_invite处理,_eXosip_transaction_init (&transaction, ICT, eXosip.j_osip, invite)初始化了状态机,之前还有一个eXosip_call_init是用来初始化eXosip的一些参数的,暂时不管它,同样 osip_new_outgoing_sipmessage (invite)发送呼叫消息,但实际还是要状态机处理后调用消息发送回调函数真实发送呼叫请求函数的,osip_transaction_add_event (transaction, sipevent)则标准地,将事件入状态机,状态机将能处理随后的应用逻辑调用相应的回调函数了。   好了,作了这么多的分析,我们了解了eXosip是怎样调用oSIP来形成被我能方便地再次调用的了,可以看到,为了实现最大限度的跨平台和兼容性,代码中有大量的测试代码,宏定义和错误再处理代码,看起来非常吃力,但了解了其主要的调用框架:   初始化,回调函数设置,UAC和UAS事务处理状态机的启动,事件处理流程等,就可以基本明白了oSIP各个函数的主要作用和正确的用法了,下一步,可以参考eXosip来针对某个应用,去除掉大量暂时用不到的代码,来构建一个简单的SIP软电话和SIP服务器,来进一步深入oSIP学习应用了。  ------------------------------------------------------ [下回预告:完全基于oSIP的软电话实现及oSIP进一步学习] (CopyLeft by Meineson | www.mbstudio.cn,原创文章,欢迎转载,但请保留出处说明!) 附件为原作者提供的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值