unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法,所用API于在不同主机上执行客户/服务器通信所有的 API(套接字API,如AF_INET、AF_INET6等类型的API)相同。unix域协议可以视为是进程之间本地通信IPC的一种。
unix域提供两类套接口:字节流套接口(类似TCP)和数据报套接口(类似UDP)。使用Unix域套接口的理由有三:
Unix域中用域标识客户和服务器的协议地址是普通文件系统中的路径名(类比:IPv4协议的地址由一个32位地址和一个16位端口号构成,IPv6协议的地址由一个128位地址和16位端口号构成。)。
简单介绍了Unix域套接口之后,进入主题——描述我碰到的问题。由于unix域套接口用于本机间进程通信比网络套接口效率高,因为它是不经过协议 栈的!在项目中选择了unix域的数据报套接口。在使用过程中碰到了如下,问题:发送<128K的消息时,客户、进程可以正常收发消息;发 送>=128K的消息时,发送端(sendto)返回ENOBUFS的错误。
服务器的代码如下:
#include < stdio.h >
#include < stdlib.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < sys / un.h >
#include < errno.h >
// define send and recv buf size
#define BUFSIZE 512*1024
// define unix domain socket path
#define pmmanager "/tmp/pmmanager"
#define pmapi "/tmp/pmapi"
int main( int argc, char ** argv)
{
char rx_buf[BUFSIZE];
int pmmanager_fd, ret;
socklen_t len;
struct sockaddr_un pmmanager_addr, pmapi_addr;
// create pmmanager socket fd
pmmanager_fd = socket(AF_UNIX, SOCK_DGRAM, 0 );
if (pmmanager_fd == - 1 )
{
perror( " cannot create pmmanager fd. " );
}
unlink(pmmanager);
memset( & pmmanager_addr, 0 , sizeof (pmmanager_addr));
pmmanager_addr.sun_family = AF_UNIX;
strncpy(pmmanager_addr.sun_path, pmmanager, sizeof (pmmanager_addr.sun_path) - 1 );
// bind pmmanager_fd to pmmanager_addr
ret = bind(pmmanager_fd, ( struct sockaddr * ) & pmmanager_addr, sizeof (pmmanager_addr));
if (ret == - 1 )
{
perror( " can not bind pmmanager_addr " );
}
int recvBufSize;
len = sizeof (recvBufSize);
ret = getsockopt(pmmanager_fd, SOL_SOCKET, SO_RCVBUF, & recvBufSize, & len);
if (ret ==- 1 )
{
perror( " getsocket error. " );
}
printf( " Before setsockopt, SO_RCVBUF-%d/n " ,recvBufSize);
recvBufSize = 512 * 1024 ;
ret = setsockopt(pmmanager_fd, SOL_SOCKET, SO_RCVBUF, & recvBufSize, len);
if (ret == - 1 )
{
perror( " setsockopt error. " );
}
ret = getsockopt(pmmanager_fd, SOL_SOCKET, SO_RCVBUF, & recvBufSize, & len);
if (ret ==- 1 )
{
perror( " getsocket error. " );
}
printf( " Set recv buf successful, SO_RCVBUF-%d/n " ,recvBufSize);
int recvSize;
memset( & pmapi_addr, 0 , sizeof (pmapi_addr));
len = sizeof (pmapi_addr);
printf( " ==============wait for msg from pmapi====================/n " );
for (;;)
{
memset(rx_buf, 0 , sizeof (rx_buf));
recvSize = recvfrom(pmmanager_fd, rx_buf, sizeof (rx_buf), 0 , ( struct sockaddr * ) & pmapi_addr, & len);
if (recvSize == - 1 )
{
perror( " recvfrom error. " );
}
printf( " Recved message from pmapi: %s/n " , rx_buf);
}
}
客户端的代码如下:
#include < stdio.h >
#include < stdlib.h >
#include < sys / types.h >
#include < sys / socket.h >
#include < sys / un.h >
#include < errno.h >
// define send and recv buf size
#define BUFSIZE 250*1024
// define unix domain socket path
#define pmmanager "/tmp/pmmanager"
#define pmapi "/tmp/pmapi"
int main( int argc, char ** argv)
{
char tx_buf[BUFSIZE];
int pmapi_fd, ret;
socklen_t len;
struct sockaddr_un pmmanager_addr, pmapi_addr;
// create pmmanager socket fd
pmapi_fd = socket(AF_UNIX, SOCK_DGRAM, 0 );
if (pmapi_fd == - 1 )
{
perror( " cannot create pmapi fd. " );
}
unlink(pmapi);
// configure pmapi's addr
memset( & pmapi_addr, 0 , sizeof (pmapi_addr));
pmapi_addr.sun_family = AF_UNIX;
strncpy(pmapi_addr.sun_path, pmapi, sizeof (pmapi_addr.sun_path) - 1 );
// bind pmapi_fd to pmapi_addr
ret = bind(pmapi_fd, ( struct sockaddr * ) & pmapi_addr, sizeof (pmapi_addr));
if (ret == - 1 )
{
perror( " bind error. " );
}
int sendBufSize;
len = sizeof (sendBufSize);
ret = getsockopt(pmapi_fd, SOL_SOCKET, SO_SNDBUF, & sendBufSize, & len);
if (ret ==- 1 )
{
perror( " getsocket error. " );
}
printf( " Before setsockopt, SO_SNDBUF-%d/n " ,sendBufSize);
sendBufSize = 512 * 1024 ;
ret = setsockopt(pmapi_fd, SOL_SOCKET, SO_SNDBUF, & sendBufSize, len);
if (ret == - 1 )
{
perror( " setsockopt error. " );
}
ret = getsockopt(pmapi_fd, SOL_SOCKET, SO_SNDBUF, & sendBufSize, & len);
if (ret ==- 1 )
{
perror( " getsocket error. " );
}
printf( " Set send buf successful, SO_SNDBUF-%d/n/n/n " , sendBufSize);
// configure pmmanager's addr
memset( & pmmanager_addr, 0 , sizeof (pmmanager_addr));
pmmanager_addr.sun_family = AF_UNIX;
strncpy(pmmanager_addr.sun_path, pmmanager, sizeof (pmmanager_addr) - 1 );
len = sizeof (pmmanager_addr);
int sendSize = 0 ;
int i;
for (i = 1 ; i <= 4 ; i ++ )
{
memset(tx_buf, ' 0 ' , sizeof (tx_buf));
sprintf(tx_buf, " send msg %d to pmmanager. " , i);
printf( " %s, msg size - %d/n " ,tx_buf, sizeof (tx_buf));
sendSize = sendto(pmapi_fd, tx_buf, sizeof (tx_buf), 0 , ( struct sockaddr * ) & pmmanager_addr, len);
if (sendSize == - 1 )
{
perror( " sendto error. " );
}
printf( " Send message to pmmanager: %s/n/n/n " , tx_buf);
}
}
3、可能碰到的另外一个问题
如果你没有设置足够大的发送缓冲区大小,你很有可能碰到EMSGSIZE的错误!因为应用程序写了一个大于套机口发送缓冲区大小的数据报,内核报EMSGSIZE错误。如下图:
(注意:UDP套接口有发送缓冲区的大小,并且可以通过SO_SNDBUF套接口选项修改。不过它仅仅是写到套接口的UDP数据报的大小,因为 UDP是不可靠的,它不必保存应用进程的数据拷贝,因此无需一个真正的发送缓冲区。)上面的代码已经设置了足够大的发送缓冲区大小。
在sendto发送>=128K大小的消息时,返回ENOBUFS错误。
。。。。。。
size - 131072 (DMA) 0 0 131072 1 32 : tunables 8 4 0 : slabdata 0 0 0
size - 131072 0 0 131072 1 32 : tunables 8 4 0 : slabdata 0 0 0
size - 65536 (DMA) 0 0 65536 1 16 : tunables 8 4 0 : slabdata 0 0 0
size - 65536 0 0 65536 1 16 : tunables 8 4 0 : slabdata 0 0 0
size - 32768 (DMA) 0 0 32768 1 8 : tunables 8 4 0 : slabdata 0 0 0
size - 32768 0 0 32768 1 8 : tunables 8 4 0 : slabdata 0 0 0
size - 16384 (DMA) 0 0 16384 1 4 : tunables 8 4 0 : slabdata 0 0 0
size - 16384 0 0 16384 1 4 : tunables 8 4 0 : slabdata 0 0 0
size - 8192 (DMA) 0 0 8192 1 2 : tunables 8 4 0 : slabdata 0 0 0
size - 8192 0 0 8192 1 2 : tunables 8 4 0 : slabdata 0 0 0
size - 4096 (DMA) 0 0 4096 1 1 : tunables 24 12 0 : slabdata 0 0 0
size - 4096 4 4 4096 1 1 : tunables 24 12 0 : slabdata 4 4 0
size - 2048 (DMA) 0 0 2048 2 1 : tunables 24 12 0 : slabdata 0 0 0
size - 2048 12 14 2048 2 1 : tunables 24 12 0 : slabdata 7 7 0
size - 1024 (DMA) 0 0 1024 4 1 : tunables 54 27 0 : slabdata 0 0 0
size - 1024 11 12 1024 4 1 : tunables 54 27 0 : slabdata 3 3 0
size - 512 (DMA) 0 0 512 8 1 : tunables 54 27 0 : slabdata 0 0 0
size - 512 208 208 512 8 1 : tunables 54 27 0 : slabdata 26 26 0
size - 256 (DMA) 0 0 256 15 1 : tunables 120 60 0 : slabdata 0 0 0
size - 256 75 75 256 15 1 : tunables 120 60 0 : slabdata 5 5 0
size - 192 (DMA) 0 0 192 20 1 : tunables 120 60 0 : slabdata 0 0 0
size - 192 40 40 192 20 1 : tunables 120 60 0 : slabdata 2 2 0
size - 128 (DMA) 0 0 128 30 1 : tunables 120 60 0 : slabdata 0 0 0
size - 128 86 90 128 30 1 : tunables 120 60 0 : slabdata 3 3 0
size - 96 (DMA) 0 0 96 40 1 : tunables 120 60 0 : slabdata 0 0 0
size - 96 388 400 96 40 1 : tunables 120 60 0 : slabdata 10 10 0
size - 64 (DMA) 0 0 64 59 1 : tunables 120 60 0 : slabdata 0 0 0
size - 32 (DMA) 0 0 32 113 1 : tunables 120 60 0 : slabdata 0 0 0
size - 64 451 472 64 59 1 : tunables 120 60 0 : slabdata 8 8 0
size - 32 871 904 32 113 1 : tunables 120 60 0 : slabdata 8 8 0
。。。。。。
我在Ubuntu 10.10上测试,不会报ENOBUFS的错误。内核版本为:
作者:吴秦
出处:http://www.cnblogs.com/skynet/
本文基于署名 2.5 中国大陆 许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名吴秦 (包含链接).