欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用udp發(fā)送>=128K的消息會(huì)報(bào)ENOBUFS的錯(cuò)誤的解決方法

 更新時(shí)間:2019年05月20日 17:36:52   作者:吳秦  
在項(xiàng)目中選擇了unix域的數(shù)據(jù)報(bào)套接口。在使用過程中碰到了如下,問題:發(fā)送<128K的消息時(shí),客戶、進(jìn)程可以正常收發(fā)消息;發(fā)送>=128K的消息時(shí),發(fā)送端(sendto)返回ENOBUFS的錯(cuò)誤。下面小編來詳細(xì)說下

這是一個(gè)困擾我兩天的問題,

Google和Baidu沒有找到解決方法!

此文為記錄這個(gè)問題,并給出原因和解決方法。

1、Unix domain socket簡(jiǎn)介

unix域協(xié)議并不是一個(gè)實(shí)際的協(xié)議族,而是在單個(gè)主機(jī)上執(zhí)行客戶/服務(wù)器通信的一種方法,所用API于在不同主機(jī)上執(zhí)行客戶/服務(wù)器通信所有的API(套接字API,如AF_INET、AF_INET6等類型的API)相同。unix域協(xié)議可以視為是進(jìn)程之間本地通信IPC的一種。

unix域提供兩類套接口:字節(jié)流套接口(類似TCP)和數(shù)據(jù)報(bào)套接口(類似UDP)。使用Unix域套接口的理由有三:

Unix域套接口往往比位于同一主機(jī)的TCP套接口快出一倍。Unix域套接口可用于在同一主機(jī)上的不同進(jìn)程之間傳遞描述字。Unix域套接口把客戶的憑證(用戶ID和用戶組ID)提供給服務(wù)器,從而實(shí)現(xiàn)能夠提供額外的安全檢查措施。

Unix域中用域標(biāo)識(shí)客戶和服務(wù)器的協(xié)議地址是普通文件系統(tǒng)中的路徑名(類比:IPv4協(xié)議的地址由一個(gè)32位地址和一個(gè)16位端口號(hào)構(gòu)成,IPv6協(xié)議的地址由一個(gè)128位地址和16位端口號(hào)構(gòu)成。)。

2、問題描述

簡(jiǎn)單介紹了Unix域套接口之后,進(jìn)入主題——描述我碰到的問題。由于unix域套接口用于本機(jī)間進(jìn)程通信比網(wǎng)絡(luò)套接口效率高,因?yàn)樗遣唤?jīng)過協(xié)議棧的!在項(xiàng)目中選擇了unix域的數(shù)據(jù)報(bào)套接口。在使用過程中碰到了如下,問題:發(fā)送<128K的消息時(shí),客戶、進(jìn)程可以正常收發(fā)消息;發(fā)送>=128K的消息時(shí),發(fā)送端(sendto)返回ENOBUFS的錯(cuò)誤。

服務(wù)器的代碼如下:

服務(wù)器端

#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、可能碰到的另外一個(gè)問題

如果你沒有設(shè)置足夠大的發(fā)送緩沖區(qū)大小,你很有可能碰到EMSGSIZE的錯(cuò)誤!因?yàn)閼?yīng)用程序?qū)懥艘粋€(gè)大于套機(jī)口發(fā)送緩沖區(qū)大小的數(shù)據(jù)報(bào),內(nèi)核報(bào)EMSGSIZE錯(cuò)誤。如下圖:

(注意:UDP套接口有發(fā)送緩沖區(qū)的大小,并且可以通過SO_SNDBUF套接口選項(xiàng)修改。不過它僅僅是寫到套接口的UDP數(shù)據(jù)報(bào)的大小,因?yàn)閁DP是不可靠的,它不必保存應(yīng)用進(jìn)程的數(shù)據(jù)拷貝,因此無需一個(gè)真正的發(fā)送緩沖區(qū)。)上面的代碼已經(jīng)設(shè)置了足夠大的發(fā)送緩沖區(qū)大小。

4、我的嘗試

在sendto發(fā)送>=128K大小的消息時(shí),返回ENOBUFS錯(cuò)誤。

我懷疑是否是sendto()的原因,我改用sendmsg(),未果還是返回這個(gè)錯(cuò)誤。有人說是:“發(fā)送消息太頻繁,間隔太短”。其實(shí)項(xiàng)目中發(fā)送消息根本就不頻繁,背著死馬當(dāng)活馬醫(yī),未果還是返回這個(gè)錯(cuò)誤。嘗試修改/proc/sys/net/core下面的各種相關(guān)選項(xiàng),如

未果,還是返回這個(gè)錯(cuò)誤。(其它路徑下的相關(guān)選項(xiàng)也試了,不行)?我無從下手了,不知道128K的這個(gè)限制在哪?既然“No buffer space available”,我怎樣給他空間?5、最終原因及解決辦法(都是內(nèi)核惹得禍?。。┲链?,我實(shí)在沒有辦法了,不知道如何解決!但是從錯(cuò)誤ENOBUFS的說明:

ENOBUFS means there is no sufficient memory available and the system(kernel) can not allocate any more.Application will usually retry the operation when it detects this error from asystem call since it indicates there is a transient resource shortage.It is the Operating system that refuses the resource request from the listener.The virtual memory allocation routine of the OS will determine if a swap can bemade to disk of a real memory segment thereby allowing the listener access tosome more real memory.

可以看出一些端倪,這肯定跟內(nèi)存分配有關(guān)!而且限制在分配128K就失??!利用Socket進(jìn)行進(jìn)程間的通信,需要經(jīng)過Linux內(nèi)核:進(jìn)程1將數(shù)據(jù)寫到內(nèi)核,進(jìn)程2從內(nèi)核讀取數(shù)據(jù)。內(nèi)核必須申請(qǐng)一個(gè)空間來存放數(shù)據(jù)包!實(shí)際上,socket發(fā)送數(shù)據(jù)包時(shí),需要從slab中申請(qǐng)一塊cache存放數(shù)據(jù)包。在2.6.21內(nèi)核中(這就是我們公司服務(wù)器的內(nèi)核版本),slab分配器最大支持的size為128K(詳情可見/proc/slabinfo)。在2.6.31內(nèi)核中,slab分配器最大支持的size大小為32M。所以2.6.21內(nèi)核上,發(fā)送大于128K的數(shù)據(jù)包時(shí),Kmalloc()會(huì)失敗,并返回no buffer的錯(cuò)誤。建議:對(duì)于本地進(jìn)程通信,可以使用其它的IPC方式,進(jìn)行數(shù)據(jù)通信,如shm、pipe等。找出了原因,可以采用以下方式來解決該問題:升級(jí)內(nèi)核,或修改內(nèi)核的這個(gè)限制。改用unix 域udp套接口為unix域tcp套接口(最終我們采用的方式)。改用其它的IPC方式(這個(gè)涉及到太多的修改,故我們放棄使用)。附/proc/slabinfo 信息:size-131072即128K的限制!

代碼

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上測(cè)試,不會(huì)報(bào)ENOBUFS的錯(cuò)誤。內(nèi)核版本為:

/proc/slabinfo的信息如下,跟上面的有些差異:

kmalloc的最大限制是8192K,故我們運(yùn)行上述程序沒有問題!原來都是內(nèi)核惹得禍阿,害我困惑那么久?。。aidu和google都沒有找到原因,因此分享此文,以警惕后者。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C語言實(shí)題講解快速掌握單鏈表上

    C語言實(shí)題講解快速掌握單鏈表上

    單鏈表是后面要學(xué)的雙鏈表以及循環(huán)鏈表的基礎(chǔ),要想繼續(xù)深入了解數(shù)據(jù)結(jié)構(gòu)以及C語言,我們就要奠定好這塊基石!接下來就和我一起學(xué)習(xí)吧
    2022-04-04
  • C++?中的類型詳細(xì)

    C++?中的類型詳細(xì)

    這篇文章主要介紹了C++?中的類型,C++的類型很復(fù)雜,往往一個(gè)類型匹配錯(cuò)誤就會(huì)導(dǎo)致程序報(bào)錯(cuò),本篇主要講解一些常用類型的概念以及細(xì)節(jié),需要的朋友可以參考一下,希望對(duì)你有所幫助
    2021-12-12
  • C++項(xiàng)目實(shí)戰(zhàn)之makefile使用

    C++項(xiàng)目實(shí)戰(zhàn)之makefile使用

    這篇文章主要介紹了C++項(xiàng)目實(shí)戰(zhàn)之makefile使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 用C語言模仿Python函數(shù)的一種簡(jiǎn)單實(shí)現(xiàn)方法

    用C語言模仿Python函數(shù)的一種簡(jiǎn)單實(shí)現(xiàn)方法

    這篇文章主要介紹了用C語言模仿Python函數(shù)的一種簡(jiǎn)單實(shí)現(xiàn)方法,需要的朋友可以參考下
    2017-05-05
  • C++實(shí)現(xiàn)連連看消除算法

    C++實(shí)現(xiàn)連連看消除算法

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)連連看消除算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • 淺析C++內(nèi)存布局

    淺析C++內(nèi)存布局

    本文給大家介紹了C++內(nèi)存布局的相關(guān)知識(shí),通過進(jìn)程間通信比線程間通信難也是因?yàn)檫M(jìn)程間的用戶空間是相互隔離的,無法相互訪問,需要通過進(jìn)程間通信方式通信,通過內(nèi)核地址空間
    2021-10-10
  • C語言每日練習(xí)之動(dòng)態(tài)顯示系統(tǒng)時(shí)間

    C語言每日練習(xí)之動(dòng)態(tài)顯示系統(tǒng)時(shí)間

    這篇文章主要介紹了C語言動(dòng)態(tài)顯示系統(tǒng)時(shí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-11-11
  • C++實(shí)現(xiàn)雙向冒泡排序算法

    C++實(shí)現(xiàn)雙向冒泡排序算法

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)雙向冒泡排序算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • 深度解析C語言中數(shù)據(jù)的存儲(chǔ)

    深度解析C語言中數(shù)據(jù)的存儲(chǔ)

    本文詳細(xì)介紹了C語言中數(shù)據(jù)的存儲(chǔ),文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • 針對(duì)Ruby的Selenium WebDriver安裝指南

    針對(duì)Ruby的Selenium WebDriver安裝指南

    這篇文章主要介紹了針對(duì)Ruby的Selenium WebDriver安裝指南,Selenium直接運(yùn)行于瀏覽器之中,是進(jìn)行各種調(diào)試的一大神器,需要的朋友可以參考下
    2015-07-07

最新評(píng)論