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

解码器在使用过程中,开机容易出现 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 的错误。

 

你可能感兴趣的:(域通信中数据报方式发送失败的原因分析)