免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2450 | 回复: 3
打印 上一主题 下一主题

关于writev发送导致数据丢失的问题,不知道大家有没有遇到过? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-04 09:28 |只看该作者 |倒序浏览
最近在使用lighttpd实现RPC远程调用时,发现大报文会导致数据丢失,经过定位,发现writev在发送数据时,造成的数据丢失。
为证实这个问题,我写了一个段test code
具体情况是,server端创建了一个unix socket文件,在accept一个client socket时,将发送模式设置为unblock模式,这时client向server端发送超过tcp buffer所能缓冲的报文时,就是造成报文丢失,block模式不会丢失。
    网上搜索相关问题发现也有人提出可能会造成丢失的问题,但没有解决方案,是否为kernel问题。

    请问哪位遇到过这样的问题?如何解决?谢谢


testcode

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <zlib.h>
#include <ctype.h>
#include <signal.h>
#include <sys/un.h>

#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/tcp.h>

#define SOCK_PATH  "/tmp/debug/testx"
#define BUFF_SIZE  1024*16

int main (int argc, char**argv)
{
    socklen_t len=4;
    int rrr,ret;
    int sss = socket(AF_UNIX, SOCK_STREAM, 0);
    if(sss > 0)
    {
        rrr = getsockopt(sss, SOL_SOCKET,SO_RCVBUF,&ret, &len);
        if(rrr < 0){
            printf("getsockopt ret %d, %s\n", rrr, strerror(errno));
            return 1;
        }
        printf("get socket optional: sock receive buff %d,len%d\n",ret,len);
        getsockopt(sss, SOL_SOCKET,SO_SNDBUF,&ret, &len);
        printf("get socket optional: sock send buff %d,len%d\n",ret,len);
        close(sss);
    }
    else
    {
        printf("create socket faid\n";
    }

    if(argc !=2)
        return 1;
    if(strcmp(argv[1],"server" == 0){
    int listenfd, connfd;
    char buff[1024*300]={'0'};
    int sum=0;
    socklen_t clilen;
    struct sockaddr_un cliaddr, servaddr;
    listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
    unlink(SOCK_PATH);
   
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sun_family = AF_UNIX;
    strcpy(servaddr.sun_path, SOCK_PATH);
    bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    listen(listenfd,5);
    while(1){
        clilen = sizeof(cliaddr);
        if((connfd = accept(listenfd, (struct sockaddr*)(&cliaddr),&clilen))<0)
            continue;
        int n;
        sum=0;

        while((n = read(connfd, buff, 1024)) > 0){
            sum+=n;
        }
            printf("%d bytes read\n", sum);
        printf("------------------------------------------\n";
        if( n<0 )
            printf("read err\n";
    }
    }
    else{
        int sockfd;
    int n,val=1;
    struct sockaddr_un servaddr;
    char buf0[BUFF_SIZE];
    char buf1[BUFF_SIZE];
    char buf2[BUFF_SIZE];
    char buf3[BUFF_SIZE];
    char buf4[BUFF_SIZE];
    char buf5[BUFF_SIZE];
    char buf6[BUFF_SIZE];
    char buf7[BUFF_SIZE];
    struct iovec iov[8];
    int iovcnt;
    int ret ;
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sun_family = AF_UNIX;
    strcpy(servaddr.sun_path, SOCK_PATH);
    connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
        n = setsockopt(sockfd, IPPROTO_TCP,TCP_CORK,&val,4);
        if(n<0)
            printf("setsockopt err %d %s\n",n,strerror(errno));
        //setsockopt(connfd, IPPROTO_TCP,3,&val,4);
        n = fcntl(sockfd,F_SETFL,O_RDWR|O_NONBLOCK);
        if(n<0)
            printf("fcntl err %d %s\n",n,strerror(errno));
    memset(buf0,'a',BUFF_SIZE);
    iov[0].iov_base = buf0;
    iov[0].iov_len = BUFF_SIZE;
    memset(buf1,'b',BUFF_SIZE);
    iov[1].iov_base = buf1;
    iov[1].iov_len = BUFF_SIZE;
    memset(buf2,'c',BUFF_SIZE);
    iov[2].iov_base = buf2;
    iov[2].iov_len = BUFF_SIZE;
    memset(buf3,'d',BUFF_SIZE);
    iov[3].iov_base = buf3;
    iov[3].iov_len = BUFF_SIZE;
    memset(buf4,'e',BUFF_SIZE);
    iov[4].iov_base = buf4;
    iov[4].iov_len = BUFF_SIZE;
    memset(buf5,'f',BUFF_SIZE);
    iov[5].iov_base = buf5;
    iov[5].iov_len = BUFF_SIZE;
    memset(buf6,'g',BUFF_SIZE);
    iov[6].iov_base = buf6;
    iov[6].iov_len = BUFF_SIZE;
    memset(buf7,'h',BUFF_SIZE);
    iov[7].iov_base = buf7;
    iov[7].iov_len = BUFF_SIZE;

    iovcnt = sizeof(iov)/sizeof(struct iovec);
    int i ;
    for(i=0;i<10;i++){
        printf("%d bytes must be write\n", iov[0].iov_len*iovcnt);
        ret = writev(sockfd, iov, iovcnt);
        if(ret <0)
            printf("writev error %s\n", strerror(errno));
        else
        printf("%d bytes written\n", ret);
        usleep(100);
    }
    sleep(5);
    close(sockfd);
    }


    return 0;
}

论坛徽章:
0
2 [报告]
发表于 2010-01-04 09:32 |只看该作者
root@x:~# ./test xx
get socket optional: sock receive buff 108544,len4
get socket optional: sock send buff 108544,len4
setsockopt err -1 Operation not supported
131072 bytes must be write
113344 bytes written------------------------>实际发送的长度总是小于应该发送的长度:em12:
131072 bytes must be write
113344 bytes written
131072 bytes must be write
113344 bytes written
131072 bytes must be write
113344 bytes written
131072 bytes must be write
113344 bytes written
131072 bytes must be write
113344 bytes written
131072 bytes must be write
113344 bytes written
131072 bytes must be write
113344 bytes written
131072 bytes must be write
113344 bytes written
131072 bytes must be write
113344 bytes written
root@x:~# cat /proc/sys/net/core/wmem_default
108544
root@x:~# cat /proc/sys/net/core/wmem_max
108544

论坛徽章:
0
3 [报告]
发表于 2010-01-05 14:40 |只看该作者

论坛徽章:
0
4 [报告]
发表于 2010-01-05 15:34 |只看该作者

回复 #1 python56387 的帖子

你要发送的数据超过了client socket的sendbuf size,NONBLOCK socket不会block等待memory,而是立即返回已经发送的数据大小。

这是正确的行为。如果是nonblock socket的话,你自己的程序需要处理发送小于requested size的情况。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP