现在的位置: 首页 > 综合 > 正文

域通信中数据报方式发送失败的原因分析

2013年09月15日 ⁄ 综合 ⁄ 共 2968字 ⁄ 字号 评论关闭



解码器在使用过程中,开机容易出现
ztebw

vplayer
发送控制命令失败,时常返回
Resource temporarily
unavailable

错误,导致一些关键的命令没有办法发送成功,造成字幕叠加不正确或者播放不成功,给使用带来一些不便。在解码器中,
ztebw

vplayer
分别以进程形式存在,使用高效的
Unix Domain Socket
通信,用非阻塞模式。由于解码器同时支持
8
路视频点播,每一路视频播放都通过同一
Unix Domain Socket
发送,造成网络阻塞发送不成功,也不是没有有可能的。那原因究竟在哪呢?

先看看
Unix Domain Socket
这种
Socket
。按照网上的说法,
UNIX
Domain Socket

用于
IPC
更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。而且
UNIX Domain Socket
是全双工的,
API
接口语义丰富,相比其它
IPC
机制有明显的优越性,目前已成为使用最广泛的
IPC
机制,比如
X Window
服务器和
GUI
程序之间就是通过
UNIX Domain Socket
通讯的。

有了这样的高帽,我们只能先把焦点放在应用上了。仔细分析启动过程中的消息发送过程。发现


这个过程本身是没有错的,但是解码器考虑到
vplayer
可能会中途退出重启,会主动通知
ztebw
进程,告诉
ztebw
自己已经启动了,会不断发送
VOD_INITED
消息,间隔时间非常短(这本身就有问题),
ztebw
收到此消息的响应是
STB_CFG
消息。这个消息附带着解码器整个的配置消息。因此它非常大,一个消息附带着
4008Bytes
,这个消息连续发送肯定是不能接受的。用
getsockopt
查看,发送缓冲区默认是
64Kbytes
。如果不再考虑
vplayer
退出重启与
ztebw
握手的过程,不主动发送
VOD_INITED
消息,没有再出现发送不成功的现象。

      

问题虽然搞定了,但总觉得有些地方没有彻底弄清楚。这种发送失败的情况在平常异常情况中也会偶尔出现,而这些异常情况大都在正常运行过程中,是不会发送
STB_CFG
消息。

难道另有隐情。

1.
参数
max_dgram_qlen
的大小

      
[wlh@localhost
~]$ cat /proc/sys/net/unix/max_dgram_qlen

10

max_dgram_qlen
limits how many datagrams can be queued on a unix domain socket's (SOCK_DGRAM)
receive buffer. If a sender tries to send more datagrams, it blocks (in a
blocking sendto) or returns error (in a non-blocking sendto). The default value
is 10.

上面的解释说明这个参数是域通信
Socket
在数据报(
UDP
)方式下,队列里最大数据报个数。我们使用的正是这个方式。

// testUnixDomain.c

检测
/proc/sys/net/unix/max_dgram_qlen
的参数,

//
使用
echo “50” >/proc/sys/net/unix/max_dgram_qlen

#include
<sys/socket.h>

#include
<sys/un.h>

#include
<stdio.h>

#include
<stdlib.h>

#include
<string.h>

 

int main(int
argc, char **argv)

{

       
unlink("toto");

       
int sock = socket(AF_UNIX, SOCK_DGRAM,
0);

 

       
if (sock < 0)

       
{

               
perror("socket");

               
exit(1);

       
}

 

       
struct sockaddr_un dest ;

       
dest.sun_family = AF_UNIX;

       
strcpy(dest.sun_path,
"toto");

 

       
int ret = bind(sock,  
(struct  

sockaddr  
*)&dest,  
sizeof(dest));

       
if( ret  
==  

-1)

       
{

               
perror("bind  
error");

               
exit(1);

       
}

       
int bufsize = atoi(argv[1]);

       
char *buf = malloc(bufsize);

 

       
int count = 0;

       
while ( 1 )

       
{

               
int res = sendto(sock, buf,
bufsize, MSG_DONTWAIT,

(struct sockaddr*)&dest, sizeof dest);

     
          
if (res < 0)

               
{

                       

perror("sendto");

                       
break;

               
}

 

               
++count;

       
}

 

       
printf("COUNT=%d/n", count);

       
return 0;

}

2.       



缓冲区的迷惑

有了上面的参数,那缓冲区如何设置呢。缓冲区的设置跟普通
Socket
一样,通过
setsockopt
设置发送和接收缓冲区。

使用上面的程序分别运行。

[root@localhost
TestProg]# ./testUnixDomain 4

sendto: Resource
temporarily unavailable

COUNT=11    

[root@localhost
TestProg]# ./testUnixDomain 4000

sendto: Resource
temporarily unavailable

COUNT=11

[root@localhost
TestProg]# echo "50" >/proc/sys/net/unix/max_dgram_qlen

[root@localhost
TestProg]# ./testUnixDomain 4

sendto: Resource
temporarily unavailable

COUNT=51

[root@localhost
TestProg]# ./testUnixDomain 4000

sendto: Resource
temporarily unavailable

COUNT=26

从上面的测试可以看到,其实
Unix Domain Socket
的数据报方式的发送与两个因素有关,一是
/proc/sys/net/unix/max_dgram_qlen
中允许的最大数据报个数。

另一个是缓冲区的最大值,超过其中一个限制,都会出现
Resource temporarily unavailable
的错误。

 

抱歉!评论已关闭.