2011-08-27 56 views
1

我有一个单元测试,用于检查阻塞和非阻塞套接字上的行为 - 服务器写入一个长响应,并且在某些时候它不应该能够再写入数据,写入块。套接字在写操作时没有被阻塞:OpenSolaris

基本上一边写道​​,另一边不读。

在Solaris下,在某些时候,我得到一个错误“没有足够的空间”(写入75MB后),而不是阻塞写:

计划能重现问题:

#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <errno.h> 
#include <string.h> 
#include <signal.h> 
#include <arpa/inet.h> 
#include <sys/socket.h> 
#include <sys/un.h> 
#include <netinet/in.h> 

char const *address = "127.0.0.1"; 
#define check(x) do { if((x) < 0) { perror(#x) ; exit(1); } } while(0) 

int main() 
{ 
    signal(SIGPIPE,SIG_IGN); 
    struct sockaddr_in inaddr = {}; 
    inaddr.sin_family = AF_INET; 
    inaddr.sin_addr.s_addr = inet_addr(address); 
    inaddr.sin_port = htons(8080); 

    int res = fork(); 
    if(res < 0) { 
     perror("fork"); 
     exit(1); 
    } 
    if(res > 0) { 
     int fd = -1; 
     int status; 
     sleep(1); 
     check(fd = socket(AF_INET,SOCK_STREAM,0)); 
     check(connect(fd,(sockaddr*)&inaddr,sizeof(inaddr))); 
     sleep(5); 
     close(fd); 

     wait(&status); 
     return 0; 
    } 
    else { 
     int acc,fd; 
     check(acc = socket(AF_INET,SOCK_STREAM,0)); 
     int yes = 1; 
     check(setsockopt(acc,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))); 
     check(bind(acc,(sockaddr*)&inaddr,sizeof(inaddr))); 
     check(listen(acc,10)); 
     check(fd = accept(acc,0,0)); 

     char buf[1000]; 
     long long total= 0; 
     do { 
      int r = send(fd,buf,sizeof(buf),0); 
      if(r < 0) { 
       printf("write %s\n",strerror(errno)); 
       return 0; 
      } 
      else if(r==0) { 
       printf("Got eof\n"); 
       return 0; 
      } 
      total += r; 
      if(total > 100*1024*1024) { 
       printf("Too much!!!!\n"); 
       return 0; 
      } 
      printf("%lld\n",total); 
     }while(1); 
    } 
    return 0; 
} 

的输出在Solaris上(最后两行)

75768000 
write Not enough space 

Linux上的预期输出(最后两行)

271760 
write Connection reset by peer 

只有当另一侧关闭套接字时才会发生这种情况。

任何想法为什么以及如何修复它,设置什么选项?

PS:是的OpenSolaris 2009.06,86

编辑

  • 增加能重现问题完整的C代码

答:

这似乎是Solaris特定版本中的错误nel,libc库。

+0

你肯定送不回部分完成?它不在你的示例代码中。 – bazsi77

+0

@ bazsi77部分完成首先是好的,第二不是它总是成功地写出完整的块 – Artyom

+0

我知道它是好的,我只是想知道代码是否正确处理了这种情况。但是,如果你可以发送这么多的数据,它必须存储在某个地方。在Linux上,netstat能够显示缓冲量。此外,系统内存使用必须增加。如果确实如此,那么它确实是内核中的一个bug,在这种情况下,我会提交bugreport,或者尝试在OpenSolaris内核代码中找到问题。 – bazsi77

回答

1

从OpenSolaris的源代码,恐怕SO_SNDTIMEO选项是不支持的:https://hg.java.net/hg/solaris~on-src/file/tip/usr/src/uts/common/inet/sockmods/socksctp.c#l1233

+0

奇怪,因为setsockopt成功,并根据您链接它的代码应该失败。另外它似乎不支持getsockopt而不是setsockopt。此外,在不使用此选项的异步代码中,它也失败。 – Artyom

+0

setsockopt“支持”SO_SNDTIMEO,它接受任何传递的值,但它对此无能为力。我刚刚在Solaris 11 Express上测试了更新后的代码,并在140kBytes之后写入了写入过程块。在监听过程关闭套接字并且下一次写入完成后,它终于得到了一个坏的管道错误(但你忽略了它)。上面示例中的 – jlliagre

+0

现在我根本不使用'SO_SNDTIMEO'。顺便说一句,我不会忽视破碎的管道,我会阻止它,否则这个过程就会退出。我在套接字上遇到错误。 – Artyom