zoukankan      html  css  js  c++  java
  • Windows下编译eXosip、osip,以及UAC和UAS的例子

    今天开始了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.0platformvsnet目录,用VS2010直接打开osip.sln文件,项目自动转换

    2.更改libosip2-3.6.0platformvsnetosip2.def 文件,在文件末尾追加
      
       osip_transaction_set_naptr_record @138

    3.更改libosip2-3.6.0platformvsnetosipparser2.def 文件,在文件末尾追加


       osip_realloc @416
       osip_strcasestr @417 
       __osip_uri_escape_userinfo @418



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


    osip2.lib

    osip2.dll
    osipparser2.lib
    osipparser2.dll

    第三步,解压,编译eXosip

    进入libeXosip2-3.6.0platformvsnet目录,用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.0include包含进来

    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.0include、libeXosip2-3.6.0include包含进来


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

    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   向服务器注册
    
    ");
    	printf("c   取消注册
    
    ");
    	printf("i   发起呼叫请求
    
    ");
    	printf("h   挂断
    
    ");
    	printf("q   推出程序
    
    ");
    	printf("s   执行方法INFO
    
    ");
    	printf("m   执行方法MESSAGE
    
    ");
    
    	//初始化
    	i=eXosip_init();
    
    	if(i!=0)
    	{
    		printf("Couldn't initialize eXosip!
    ");
    		return -1;
    	}
    	else
    	{
    		printf("eXosip_init successfully!
    ");
    	}
    
    	//绑定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!
    ");
    		return -1;
    	}
    	flag=1;
    
    	while(flag)
    	{
    		//输入命令
    		printf("Please input the command:
    ");
    		scanf("%c",&command);
    		getchar();
    
    		switch(command)
    		{
    		case 'r':
    			printf("This modal is not completed!
    ");
    			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!
    ");
    				break;
    			}
    			//符合SDP格式,其中属性a是自定义格式,也就是说可以存放自己的信息,
    			//但是只能有两列,比如帐户信息
    			//但是经过测试,格式vot必不可少,原因未知,估计是协议栈在传输时需要检查的
    			_snprintf(tmp,4096,
    				"v=0
    "
    				"o=anonymous 0 0 IN IP4 0.0.0.0
    "
    				"t=1 10
    "
    				"a=username:rainfish
    "
    				"a=password:123
    ");
    
    			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!
    ");
    					break;
    				}
    				switch(je->type)   //可能会到来的事件类型
    				{
    				case EXOSIP_CALL_INVITE:   //收到一个INVITE请求
    					printf("a new invite received!
    ");
    					break;
    				case EXOSIP_CALL_PROCEEDING: //收到100 trying消息,表示请求正在处理中
    					printf("proceeding!
    ");
    					break;
    				case EXOSIP_CALL_RINGING:   //收到180 Ringing应答,表示接收到INVITE请求的UAS正在向被叫用户振铃
    					printf("ringing!
    ");
    					printf("call_id is %d,dialog_id is %d 
    ",je->cid,je->did);
    					break;
    				case EXOSIP_CALL_ANSWERED: //收到200 OK,表示请求已经被成功接受,用户应答
    					printf("ok!connected!
    ");
    					call_id=je->cid;
    					dialog_id=je->did;
    					printf("call_id is %d,dialog_id is %d 
    ",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!
    ");
    					break;
    				case EXOSIP_CALL_ACK: //ACK received for 200ok to INVITE 
    					printf("ACK received!
    ");
    					break;
    				default: //收到其他应答
    					printf("other response!
    ");
    					break;
    				}
    				eXosip_event_free(je); //Free ressource in an eXosip event
    			}
    			break;
    
    		case 'h':   //挂断
    			printf("Holded!
    ");
    
    			eXosip_lock();
    			eXosip_call_terminate(call_id,dialog_id);
    			eXosip_unlock();
    			break;
    
    		case 'c':
    			printf("This modal is not commpleted!
    ");
    			break;
    
    		case 's': //传输INFO方法
    			eXosip_call_build_info(dialog_id,&info);
    			_snprintf(tmp,4096,"
    This 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
    ");
    			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!
    ");
    			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!
    ");
       return -1;
    }
       else
    {
       printf ("eXosip_init successfully!
    ");
    }
       i = eXosip_listen_addr (IPPROTO_UDP, NULL, 15061, AF_INET, 0);
       if (i != 0)
    {
       eXosip_quit ();
       fprintf (stderr, "eXosip_listen_addr error!
    Couldn't initialize transport layer!
    ");
    }
       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!
    ");
       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
    ", 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
    ",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!
    ");
          eXosip_call_send_answer (je->tid, 400, NULL);
           }
       else
           {
          snprintf (tmp, 4096,
                "v=0
    "
                "o=anonymous 0 0 IN IP4 0.0.0.0
    "
                "t=1 10
    "
                "a=username:rainfish
    "
                "a=password:123
    ");
          
          //设置回复的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!
    ");
           }
       eXosip_unlock ();
       
       //显示出在sdp消息体中的attribute 的内容,里面计划存放我们的信息
       printf ("the INFO is :
    ");
       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
    ", at->a_att_field, at->a_att_value);//这里解释了为什么在SDP消息体中属性a里面存放必须是两列
          
          pos ++;
           }
       break;
    case EXOSIP_CALL_ACK:
       printf ("ACK recieved!
    ");
       // printf ("the cid is %s, did is %s/n", je->did, je->cid); 
       break;
    case EXOSIP_CALL_CLOSED:
       printf ("the remote hold the session!
    ");
       // 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!
    ");
          eXosip_call_send_answer (je->tid, 400, NULL);
          
          }
       else
           {
          eXosip_call_send_answer (je->tid, 200, answer);
          printf ("bye send 200 over!
    ");
           }       
       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
    ");
       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
    ", body->body);
          }
       }
       break; 
    default:
       printf ("Could not parse the msg!
    ");
    }
    }
    }
    


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

    运行示意图:






  • 相关阅读:
    MySQL查看表占用空间大小(转)
    Nginx搭建flv视频点播服务器
    Nginx SPDY Pagespeed模块编译——加速网站载入
    RAD,V模型
    java运算符的优先级
    union和union all的区别
    java数据的5种存储位置(转)
    java对象的初始化过程和创建对象的几种方式
    java异常的原理以及应用
    缓冲与缓存的概念(转)
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3155427.html
Copyright © 2011-2022 走看看