音视频聊天开发: 6 简单的stun server-凯发app官方网站

凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 1235535
  • 博文数量: 76
  • 博客积分: 1959
  • 博客等级: 上尉
  • 技术积分: 2689
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-19 12:07
个人简介

樽中酒不空

文章分类

全部博文(76)

文章存档

2020年(4)

2019年(1)

2017年(2)

2016年(2)

2015年(7)

2014年(11)

2013年(13)

2012年(18)

2011年(2)

2010年(16)

相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: c/c

2014-10-14 13:18:55

说是stun server,其实只是用了一下名字,和开源的stun server不是一回事,这里还加了一些其他功能。
功能: 
1 用户通过客户端登录上来,返回它的公网ip和端口。
2 为了开发方便,同时记录下客户端的公网ip端口,以及它提交上来的本地ip和端口,以供查询。(这部分功能正常来讲应该单独做成一个服务,比如im server)
3 用户查询其他用户的公网ip和内网ip(用户拿到后可以尝试打洞)。

功能简单,实现也同样简单,采用前面文章提到的cudpsocket和cudpsession就可以了。简单代码说明:
static void onrecvfrom(int sockid, char *data, int len, int ip, int port, void* param)
{
cpackin pack;
pack.setcontent(data, len);
int ncmd;
pack >> ncmd;

switch(ncmd)
{
case cmd_get_self_public_ip_port:
{
if (len < sizeof(int)*2)
return;
int nid = 0;
pack >> nid;
if (nid < 1)
return;


cusermgr::instance().adduser(nid, ip, port);


cpackout* pack = new cpackout;
(*pack) << cmd_re_get_self_public_ip_port;
(*pack) << nid;
(*pack) << 0;  //0 is success
(*pack) << ip;
(*pack) << port;



char* pbuf = null;
int nsize;
pack->getcontent(pbuf, nsize);
g_sess.send(pbuf, nsize, ip, port);


delete pack;
pack = null;
}
break;


case cmd_get_dest_public_ip_port:
{
if (len < sizeof(int)*2)
return;
int ndestid = 0;
pack >> ndestid;
if (ndestid < 1)
return;


cpackout* pack = new cpackout;
(*pack) << cmd_re_get_dest_public_ip_port;
(*pack) << ndestid;



cuser* puser = cusermgr::instance().finduser(ndestid);
if (puser)
{
(*pack) << 0;  //0 is success
(*pack) << ip;
(*pack) << port;
}
else
{
(*pack) << 404;//err 
}


char* pbuf = null;
int nsize;
pack->getcontent(pbuf, nsize);
g_sess.send(pbuf, nsize, ip, port);


delete pack;
pack = null;
}
break;


}
}


从代码上看,只处理了两条指令:cmd_get_self_public_ip_port和cmd_get_dest_public_ip_port。前一条是用户取到自己的公网ip和端口,用户先连接server,发送请求,recvfrom之后直接就拿到了它的公网ip和端口。用户自己的内网ip和端口可以在数据包里带上。然后server保存在map里。
后一条是取联系人的公网ip和端口。回复的时候,同可以把内网ip端口同时带上。用户拿到后,从公网ip可以看出是否同一网段,如果是的话,先用内网ip端口尝试打洞,不通再用公网的。打洞功能下篇文章在客户端实现,本文是讲server端。

g_sess.send(pbuf, nsize, ip, port);
这里,调用了cudpsession里的send ,在send里面,又包装了一包。这里绝对不能直接调用cudpsession::sendpacket,因为这个函数是直接发送裸数据的。不过可以简单修改一下把send和sendpacket结合起来用。这个小功能根据实际需要随时可做修改,本文只是演示。

server端记录user信息后,应该做一个保活检查,在一定时间内没有数据提交,认为用户下线,然后从 map里删除。这个功能代码没有实现,以后会完善的。不过很简单,有兴趣可以自己练习着加上。但这个定时器一定要注意,g_sess.m_ptimeoutcb = ontimeout;要从ontimeout这个回调函数里去执行,因为所有的map没有进行同步保护的。而 udpsession是单线程执行,由它的回调函数执行链表不会冲突,但由其他线程,比如自己再写个定时器来执行,map  一定要加锁了。


完整代码在:

因为本文主要调试网络,就把视频部分简单注释掉了。
见void cclientdlg::onrecvfrom(int sockid, char *data, int len, int ip, int port)
//if (sockid == m_nsessid)
//{
// trace("recv ret = %d\n", len);
// m_dec.decode_frame((byte*)data, len);
//}


目前主要是为了演示音视频的网络传输,为了方便,把指令都集成到了一起了。更清晰的做法是:
先做一个简单的im,进行指令传输,udp打洞等,然后再进行数据传输。
本文先集中讲音视频数据传输,用这些简单的代码演示功能,最终会出一个正式的商业级的im开源版本。目前这些代码可以看做是演示用的sample。

server版本是linux下的,先进行run 执行make,再cd.. 执行make,会生成stun_server的可执行文件。
不想自己编译的话,可以直接运行客户端,里面server的地址已经设好了: 115.28.170.118。

阅读(6799) | 评论(1) | 转发(2) |
给主人留下些什么吧!~~

2014-11-09 15:47:47

支持,,

|
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图