今天开始了SIP开源库的学习,我选择了osip和eXosip,但是这两个库的编译使用有些麻烦,源码下来之后编译会出现很多问题,网上也没有找到完整的编译介绍,只能一步一步的找办法解决,最后终于编译成功!先大概记录下编译过程,后面还要再整理下。

期间还向CSDN论坛求助了下,但是还没有反应:http://bbs.csdn.net/topics/390499635?page=1#post-394866714

描述:error LNK2019: 无法解析的外部符号 _osip_transaction_set_naptr_record,该符号在函数 __eXosip_transaction_init 中被引用

原因:开源代码的库有些函数没有导出,需要手动在def文件中添加

解决方法:第二步的2,3两小步就是为了解决这个问题。

第一步,下载osip和eXosip

osip:    http://ftp.twaren.net/Unix/NonGNU//osip/libosip2-3.6.0.tar.gz

eXosip: http://download.savannah.gnu.org/releases/exosip/libeXosip2-3.6.0.tar.gz

第二步,解压,编译osip:

1.进入libosip2-3.6.0\platform\vsnet目录,用VS2010直接打开osip.sln文件,项目自动转换

2.更改libosip2-3.6.0\platform\vsnet\osip2.def 文件,在文件末尾追加
  
   osip_transaction_set_naptr_record @138

3.更改libosip2-3.6.0\platform\vsnet\osipparser2.def 文件,在文件末尾追加

osip_realloc @416
   osip_strcasestr @417 
   __osip_uri_escape_userinfo @418

4.先编译osipparser2,再编译osip2,最后在libosip2-3.6.0\platform\vsnet\Debug DLL下生成库文件:

osip2.lib

osip2.dll
osipparser2.lib
osipparser2.dll

第三步,解压,编译eXosip

进入libeXosip2-3.6.0\platform\vsnet目录,用VS2010直接打开eXosip.sln文件,项目自动转换:

1.将osip2.lib,osip2.dll,osipparser2.lib,osipparser2.dll拷贝到Debug目录下
2.C/C++ --> 预处理器 --> 预处理器定义: 删除HAVE_OPENSSL_SSL_H
3.C/C++ --> 常规 --> 附加包含目录:     将osip的头文件libosip2-3.6.0\include包含进来

4.编译,生成eXosip.lib

第四步,新建UAC例子项目

1.链接器 --> 输入 --> 附加依赖项:增加静态库引用:Dnsapi.lib;Iphlpapi.lib;Ws2_32.lib;osip2.lib;osipparser2.lib;exosip.lib;

2.C/C++ -->常规 -->附加包含目录: 将osip和eXosip的头文件libosip2-3.6.0\include、libeXosip2-3.6.0\include包含进来

3.链接器 --> 常规  --> 附加库目录:将osip和eXosip的库包含进来,libeXosip2-3.6.0\platform\vsnet\Debug

4.编译UAC代码:

#include <eXosip2/eXosip.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h> int main(int argc,char *argv[])
{ struct eXosip_t *context_eXosip; eXosip_event_t *je;
osip_message_t *reg=NULL;
osip_message_t *invite=NULL;
osip_message_t *ack=NULL;
osip_message_t *info=NULL;
osip_message_t *message=NULL; int call_id,dialog_id;
int i,flag;
int flag1=1; char *identity="sip:140@127.0.0.1"; //UAC1,端口是15060
char *registar="sip:133@127.0.0.1:15061"; //UAS,端口是15061
char *source_call="sip:140@127.0.0.1";
char *dest_call="sip:133@127.0.0.1:15061";
//identify和register这一组地址是和source和destination地址相同的
//在这个例子中,uac和uas通信,则source就是自己的地址,而目的地址就是uac1的地址
char command;
char tmp[4096]; printf("r 向服务器注册\n\n");
printf("c 取消注册\n\n");
printf("i 发起呼叫请求\n\n");
printf("h 挂断\n\n");
printf("q 推出程序\n\n");
printf("s 执行方法INFO\n\n");
printf("m 执行方法MESSAGE\n\n"); //初始化
i=eXosip_init(); if(i!=0)
{
printf("Couldn't initialize eXosip!\n");
return -1;
}
else
{
printf("eXosip_init successfully!\n");
} //绑定uac自己的端口15060,并进行端口监听
i=eXosip_listen_addr(IPPROTO_UDP,NULL,15060,AF_INET,0);
if(i!=0)
{
eXosip_quit();
fprintf(stderr,"Couldn't initialize transport layer!\n");
return -1;
}
flag=1; while(flag)
{
//输入命令
printf("Please input the command:\n");
scanf("%c",&command);
getchar(); switch(command)
{
case 'r':
printf("This modal is not completed!\n");
break;
case 'i'://INVITE,发起呼叫请求
i=eXosip_call_build_initial_invite(&invite,dest_call,source_call,NULL,"This is a call for conversation");
if(i!=0)
{
printf("Initial INVITE failed!\n");
break;
}
//符合SDP格式,其中属性a是自定义格式,也就是说可以存放自己的信息,
//但是只能有两列,比如帐户信息
//但是经过测试,格式vot必不可少,原因未知,估计是协议栈在传输时需要检查的
_snprintf(tmp,4096,
"v=0\r\n"
"o=anonymous 0 0 IN IP4 0.0.0.0\r\n"
"t=1 10\r\n"
"a=username:rainfish\r\n"
"a=password:123\r\n"); 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); //invite SIP INVITE message to send
eXosip_unlock(); //发送了INVITE消息,等待应答
flag1=1;
while(flag1)
{
je=eXosip_event_wait(0,200); //Wait for an eXosip event
//(超时时间秒,超时时间毫秒)
if(je==NULL)
{
printf("No response or the time is over!\n");
break;
}
switch(je->type) //可能会到来的事件类型
{
case EXOSIP_CALL_INVITE: //收到一个INVITE请求
printf("a new invite received!\n");
break;
case EXOSIP_CALL_PROCEEDING: //收到100 trying消息,表示请求正在处理中
printf("proceeding!\n");
break;
case EXOSIP_CALL_RINGING: //收到180 Ringing应答,表示接收到INVITE请求的UAS正在向被叫用户振铃
printf("ringing!\n");
printf("call_id is %d,dialog_id is %d \n",je->cid,je->did);
break;
case EXOSIP_CALL_ANSWERED: //收到200 OK,表示请求已经被成功接受,用户应答
printf("ok!connected!\n");
call_id=je->cid;
dialog_id=je->did;
printf("call_id is %d,dialog_id is %d \n",je->cid,je->did); //回送ack应答消息
eXosip_call_build_ack(je->did,&ack);
eXosip_call_send_ack(je->did,ack);
flag1=0; //推出While循环
break;
case EXOSIP_CALL_CLOSED: //a BYE was received for this call
printf("the other sid closed!\n");
break;
case EXOSIP_CALL_ACK: //ACK received for 200ok to INVITE
printf("ACK received!\n");
break;
default: //收到其他应答
printf("other response!\n");
break;
}
eXosip_event_free(je); //Free ressource in an eXosip event
}
break; case 'h': //挂断
printf("Holded!\n"); eXosip_lock();
eXosip_call_terminate(call_id,dialog_id);
eXosip_unlock();
break; case 'c':
printf("This modal is not commpleted!\n");
break; case 's': //传输INFO方法
eXosip_call_build_info(dialog_id,&info);
_snprintf(tmp,4096,"\nThis is a sip message(Method:INFO)");
osip_message_set_body(info,tmp,strlen(tmp));
//格式可以任意设定,text/plain代表文本信息;
osip_message_set_content_type(info,"text/plain");
eXosip_call_send_request(dialog_id,info);
break; case 'm':
//传输MESSAGE方法,也就是即时消息,和INFO方法相比,我认为主要区别是:
//MESSAGE不用建立连接,直接传输信息,而INFO消息必须在建立INVITE的基础上传输
printf("the method : MESSAGE\n");
eXosip_message_build_request(&message,"MESSAGE",dest_call,source_call,NULL);
//内容,方法, to ,from ,route
_snprintf(tmp,4096,"This is a sip message(Method:MESSAGE)");
osip_message_set_body(message,tmp,strlen(tmp));
//假设格式是xml
osip_message_set_content_type(message,"text/xml");
eXosip_message_send_request(message);
break; case 'q':
eXosip_quit();
printf("Exit the setup!\n");
flag=0;
break;
}
} return(0);
}

第五步,编译UAS项目:

与UAC同样的设置后,编译

# include <eXosip2/eXosip.h>
# include <stdio.h>
# include <stdlib.h>
# include <Winsock2.h> int main (int argc, char *argv[])
{
   eXosip_event_t *je = NULL;
   osip_message_t *ack = NULL;
   osip_message_t *invite = NULL;
   osip_message_t *answer = NULL;
   sdp_message_t *remote_sdp = NULL;
   int call_id, dialog_id;
   int i,j;
   int id;
   char *sour_call = "sip:140@127.0.0.1";
   char *dest_call = "sip:133@127.0.0.1:15060";//client ip
   char command;
   char tmp[4096];
   char localip[128];
   int pos = 0;
   //初始化sip
   i = eXosip_init ();
   if (i != 0)
{
   printf ("Can't initialize eXosip!\n");
   return -1;
}
   else
{
   printf ("eXosip_init successfully!\n");
}
   i = eXosip_listen_addr (IPPROTO_UDP, NULL, 15061, AF_INET, 0);
   if (i != 0)
{
   eXosip_quit ();
   fprintf (stderr, "eXosip_listen_addr error!\nCouldn't initialize transport layer!\n");
}
   for(;;)
{
   //侦听是否有消息到来
   je = eXosip_event_wait (0,50); 
   //协议栈带有此语句,具体作用未知
   eXosip_lock ();
   eXosip_default_action (je);
   eXosip_automatic_refresh ();
   eXosip_unlock ();
   if (je == NULL)//没有接收到消息
continue;
   // printf ("the cid is %s, did is %s/n", je->did, je->cid);
   switch (je->type)
{
case EXOSIP_MESSAGE_NEW://新的消息到来
   printf (" EXOSIP_MESSAGE_NEW!\n");
   if (MSG_IS_MESSAGE (je->request))//如果接受到的消息类型是MESSAGE
       {
   {
       osip_body_t *body;
       osip_message_get_body (je->request, 0, &body); 
       printf ("I get the msg is: %s\n", body->body);
       //printf ("the cid is %s, did is %s/n", je->did, je->cid);
   }
   //按照规则,需要回复OK信息
   eXosip_message_build_answer (je->tid, 200,&answer);
   eXosip_message_send_answer (je->tid, 200,answer);
       }
   break;
case EXOSIP_CALL_INVITE:
   //得到接收到消息的具体信息
         printf ("Received a INVITE msg from %s:%s, UserName is %s, password is %s\n",je->request->req_uri->host,
      je->request->req_uri->port, je->request->req_uri->username, je->request->req_uri->password);
   //得到消息体,认为该消息就是SDP格式.
   remote_sdp = eXosip_get_remote_sdp (je->did);
   call_id = je->cid;
   dialog_id = je->did;
   
   eXosip_lock ();
   eXosip_call_send_answer (je->tid, 180, NULL);
   i = eXosip_call_build_answer (je->tid, 200, &answer);
   if (i != 0)
       {
      printf ("This request msg is invalid!Cann't response!\n");
      eXosip_call_send_answer (je->tid, 400, NULL);
       }
   else
       {
      snprintf (tmp, 4096,
            "v=0\r\n"
            "o=anonymous 0 0 IN IP4 0.0.0.0\r\n"
            "t=1 10\r\n"
            "a=username:rainfish\r\n"
            "a=password:123\r\n");
      
      //设置回复的SDP消息体,下一步计划分析消息体
      //没有分析消息体,直接回复原来的消息,这一块做的不好。
      osip_message_set_body (answer, tmp, strlen(tmp));
      osip_message_set_content_type (answer, "application/sdp");
      
      eXosip_call_send_answer (je->tid, 200, answer);
      printf ("send 200 over!\n");
       }
   eXosip_unlock ();
   
   //显示出在sdp消息体中的attribute 的内容,里面计划存放我们的信息
   printf ("the INFO is :\n");
   while (!osip_list_eol ( &(remote_sdp->a_attributes), pos))
       {
      sdp_attribute_t *at;
      
      at = (sdp_attribute_t *) osip_list_get ( &remote_sdp->a_attributes, pos);
      printf ("%s : %s\n", at->a_att_field, at->a_att_value);//这里解释了为什么在SDP消息体中属性a里面存放必须是两列
      
      pos ++;
       }
   break;
case EXOSIP_CALL_ACK:
   printf ("ACK recieved!\n");
   // printf ("the cid is %s, did is %s/n", je->did, je->cid); 
   break;
case EXOSIP_CALL_CLOSED:
   printf ("the remote hold the session!\n");
   // eXosip_call_build_ack(dialog_id, &ack);
   //eXosip_call_send_ack(dialog_id, ack); 
   i = eXosip_call_build_answer (je->tid, 200, &answer);
   if (i != 0)
       {
      printf ("This request msg is invalid!Cann't response!\n");
      eXosip_call_send_answer (je->tid, 400, NULL);
      
      }
   else
       {
      eXosip_call_send_answer (je->tid, 200, answer);
      printf ("bye send 200 over!\n");
       }       
   break;
case EXOSIP_CALL_MESSAGE_NEW://至于该类型和EXOSIP_MESSAGE_NEW的区别,源代码这么解释的
/*
// request related events within calls (except INVITE) 
      EXOSIP_CALL_MESSAGE_NEW,          < announce new incoming request. 
// response received for request outside calls 
         EXOSIP_MESSAGE_NEW,          < announce new incoming request. 
         我也不是很明白,理解是:EXOSIP_CALL_MESSAGE_NEW是一个呼叫中的新的消息到来,比如ring trying都算,所以在接受到后必须判断
         该消息类型,EXOSIP_MESSAGE_NEW而是表示不是呼叫内的消息到来。
         该解释有不妥地方,仅供参考。
*/
   printf(" EXOSIP_CALL_MESSAGE_NEW\n");
   if (MSG_IS_INFO(je->request) ) //如果传输的是INFO方法
   {
               eXosip_lock ();
             i = eXosip_call_build_answer (je->tid, 200, &answer);
             if (i == 0)
            {
               eXosip_call_send_answer (je->tid, 200, answer);
            }
             eXosip_unlock ();
      {
       osip_body_t *body;
       osip_message_get_body (je->request, 0, &body);
       printf ("the body is %s\n", body->body);
      }
   }
   break; 
default:
   printf ("Could not parse the msg!\n");
}
}
}

例子已经上传:http://download.csdn.net/detail/longlong530/5647749

运行示意图:

Windows下编译eXosip、osip,以及UAC和UAS的例子的更多相关文章

  1. linux下编译eXosip、osip,以及UAC和UAS的例子

    从网站上看到了这样的一篇博文 :Windows下编译eXosip.osip,以及UAC和UAS的例子 (链接:http://www.cnblogs.com/dyllove98/archive/2013 ...

  2. [转] Windows下编译OpenSSL

    简述 OpenSSL是一个开源的第三方库,它实现了SSL(Secure SocketLayer)和TLS(Transport Layer Security)协议,被广泛企业应用所采用.对于一般的开发人 ...

  3. Windows下编译objective-C

    Windows下编译objective-C 2011-08-31 14:32 630人阅读 评论(0) 收藏 举报 windowscocoa工具objective clibraryxcode   目录 ...

  4. 在Windows下编译FFmpeg详细说明

    MinGW:一个可自由使用和自由发布的Windows特定头文件和使用GNC工具集导入库的集合,允许你生成本地的Windows程序而不需要第三方C运行时 MinGW,即 Minimalist GNU F ...

  5. 如何在WINDOWS下编译BOOST C++库 .

    如何在WINDOWS下编译BOOST C++库 cheungmine 2008-6-25   写出来,怕自己以后忘记了,也为初学者参考.使用VC8.0和boost1.35.0.   1)下载boost ...

  6. 在Windows下编译OpenSSL(VS2005和VC6)

    需要说明的是请一定安装openssl-0.9.8a .  openssl-1.0.0我没有编译成功. 如何在Windows下编译OpenSSL (Vs2005使用Vc8的cl编译器)1.安装Activ ...

  7. windows下编译java源文件的编码错误

    import java.util.Arrays;public class ArrayAsAReference{ public static void main(String[] args) { int ...

  8. Windows下编译SDL

    Windows下编译SDL的理由我就不多说了,无论用VS来编译或调试SDL库都是很方便的.而且SDL源代码中也包含了VC工程,你所要做的只是解压VC工程,进行适当的配置,然后编译.调试. 编译SDL大 ...

  9. Windows下编译安装 FFmpeg

    在Linux/Mac下编译 ffmpeg是非常方便的.但要在 Windows下编译 ffmpeg还真要花点时间.以下就是在 Windowns下编译ffmpeg的步骤: 一.安装Cygwin 在wind ...

随机推荐

  1. (转)Maven实战(一)安装与配置

    1. 简介 Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具. 如果你已经有十次输入同样的Ant targets来编译你的代码.jar或者w ...

  2. js过滤空格

    点击查看效果 点击进入下载 <html> <head> <title> 过滤空格 </title> <SCRIPT LANGUAGE=" ...

  3. 经常使用的自己定义UI组件- 一:TimeView

     近期做蛋疼的机顶盒项目,以后遇到哪些经常使用的组件,记录于此. 反编译 youku视频TV偷来的..也希望各位童鞋多学习别人的代码,为己所用. 当然还有其它的办法,比方监听系统发出的广播等等.等 ...

  4. [React] React Router: activeStyle & activeClassName

    react-router provides two props for setting a specific style on a Link component whose path matches ...

  5. Unity用户自定义圆角头像

    前天朋友遇到一个这样的需求,而且比较棘手让我帮忙解决.需求就是棋牌类的游戏,玩家的个人资料中包括自己的头像而且可以浏览相册中的图片或者使用相机拍照设置.关于这个问题我也查阅一些资料,由于涉及安卓部分知 ...

  6. Android文字的复制和粘贴

    Android中提供了简单的额复制粘贴功能.代码很简单 复制文字的代码: ClipboardManager cbm= (ClipboardManager) MainActivity.this .get ...

  7. Handler Looper 原理 详解

    演示代码 public class MainActivity extends ListActivity {     private TextView tv_info;     private CalT ...

  8. 网页JavaScript1

    DOM的操作 windows对象操作 属性: opener,打开当前窗口的源窗口,首次启动 是null. dialogArgument,对话框的返回值 子对象: history , location ...

  9. (一)一个工作任务引起的乱战——c#中结构体与byte[]间相互转换

    一个工作任务涉及到c#与c++系统间的udp通信,处理了蛮长时间没有完成任务,但是期间接触到不少小知识点.本人是初接触c#,c++语言没有接触过.可能写的东西都很小儿科,暂且记录下来当工作日记把. 先 ...

  10. HTML5框架、背景和实体、XHTML的使用规范

    HTML5框架 cols :竖排 rows:横排,frameset frame已弃用  iframe仍保留使用.用法: Target属性: HTML5实体 XHTML的使用规范