这周看Linphone的代码,主程序很简单,只有三个函数:
linphone-1.7.1/console下的linphonec.c中的:
main (int argc, char *argv[])
{ if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE);
linphonec_main_loop (&linphonec, sipAddr);
linphonec_finish(EXIT_SUCCESS);
exit(EXIT_SUCCESS); /* should never reach here */
}
可以看到,主要的事物处理在linphonec_main_loop这个函数里面,该函数是一个while循环,他的主要流程就是等待终端输入命令,然后根据命令来进行不同的响应或者发出不同的SIP消息,完毕然后进入下一轮循环重新等待命令的输入。既然linphonec_main_loop只是对输入命令的处理,那么对于从网络上接收到的数据的处理是在哪里实现的呢?既然linphonec_main_loop里面没有,那肯定是在linphonec_init(argc, argv)的时候启动了某个监听网络的线程。
通过对linphonec_init这个函数的代码分析,终于找到了关键点:在这里面他调用了eXosip_init()函数,而在eXosip_init()里面加载了四个有限状态机,正是这四个有限状态机线程能够对接收到的网消息进行自动的状态跳转并处理。下面对eXosip_init()进行详细的分析。
eXosip_init()是eXosip的初始化函数,我们来看看它的内部实现:
首行是定义的 osip_t *osip,这在oSIP的官方手册里我们看到,所有使用oSIP的程序都要在最开始处声明一个osip_t的指针,并使用osip_init(&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)) < 0)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL,
"Failed to bind one local socket %i!/n", aport));
} else
break;
}
含义即,依次检测50个端口,从static int aport = 10500;即10500~10550端口找出一个可用的本地端口来绑定listen模拟pipe的一对sock。