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

时间:2022-01-27 18:55:33

解码器在使用过程中,开机容易出现ztebwvplayer发送控制命令失败,时常返回Resource temporarilyunavailable错误,导致一些关键的命令没有办法发送成功,造成字幕叠加不正确或者播放不成功,给使用带来一些不便。在解码器中,ztebwvplayer分别以进程形式存在,使用高效的Unix Domain Socket通信,用非阻塞模式。由于解码器同时支持8路视频点播,每一路视频播放都通过同一Unix Domain Socket发送,造成网络阻塞发送不成功,也不是没有有可能的。那原因究竟在哪呢?

先看看Unix Domain Socket这种Socket。按照网上的说法,UNIXDomain 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_qlenlimits 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 ablocking sendto) or returns error (in a non-blocking sendto). The default valueis 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(intargc, 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@localhostTestProg]# ./testUnixDomain 4

sendto: Resourcetemporarily unavailable

COUNT=11    

[root@localhostTestProg]# ./testUnixDomain 4000

sendto: Resourcetemporarily unavailable

COUNT=11

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

[root@localhostTestProg]# ./testUnixDomain 4

sendto: Resourcetemporarily unavailable

COUNT=51

[root@localhostTestProg]# ./testUnixDomain 4000

sendto: Resourcetemporarily unavailable

COUNT=26

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

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