關於socket阻塞與非阻塞情況下的recv、send、read、write傳回值

來源:互聯網
上載者:User

標籤:

原帖:http://blog.csdn.net/nodeathphoenix/article/details/23284157

一,管道讀寫規則

當沒有資料可讀時

  • O_NONBLOCK disable:read調用阻塞,即進程暫停執行,一直等到有資料來到為止。
  • O_NONBLOCK enable:read調用返回-1,errno值為EAGAIN。

當管道滿的時候

  • O_NONBLOCK disable: write調用阻塞,直到有進程讀走資料
  • O_NONBLOCK enable:調用返回-1,errno值為EAGAIN

如果所有管道寫端對應的檔案描述符被關閉,則read返回0

如果所有管道讀端對應的檔案描述符被關閉,則write操作會產生訊號SIGPIPE

當要寫入的資料量<=PIPE_BUF時,linux將保證寫入的原子性。

當要寫入的資料量>PIPE_BUF時,linux將不再保證寫入的原子性。

Detail 參見:

http://man7.org/linux/man-pages/man7/pipe.7.html

二,驗證樣本樣本一:O_NONBLOCK disable:read(阻塞)調用阻塞,即進程暫停執行,一直等到有資料來到為止。
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h> int main(void){    int fds[2];    if(pipe(fds) == -1){        perror("pipe error");        exit(EXIT_FAILURE);    }    pid_t pid;    pid = fork();    if(pid == -1){        perror("fork error");        exit(EXIT_FAILURE);    }    if(pid == 0){        close(fds[0]);//子進程關閉讀端        sleep(10);        write(fds[1],"hello",5);        exit(EXIT_SUCCESS);    }    close(fds[1]);//父進程關閉寫端    char buf[10] = {0};    read(fds[0],buf,10);    printf("receive datas = %s\n",buf);    return 0;}

結果:

說明:管道建立時預設開啟了檔案描述符,且預設是阻塞(block)模式開啟

所以這裡,我們讓子進程先睡眠10s,父進程因為沒有資料從管道中讀出,被阻塞了,直到子進程睡眠結束,向管道中寫入資料後,父進程才讀到資料

樣本二:O_NONBLOCK enable:read(非阻塞)調用返回-1,errno值為EAGAIN。
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h> int main(void){    int fds[2];    if(pipe(fds) == -1){        perror("pipe error");        exit(EXIT_FAILURE);    }    pid_t pid;    pid = fork();    if(pid == -1){        perror("fork error");        exit(EXIT_FAILURE);    }    if(pid == 0){        close(fds[0]);//子進程關閉讀端        sleep(10);        write(fds[1],"hello",5);        exit(EXIT_SUCCESS);    }    close(fds[1]);//父進程關閉寫端    char buf[10] = {0};    int flags = fcntl(fds[0], F_GETFL);//先擷取原先的flags    fcntl(fds[0],F_SETFL,flags | O_NONBLOCK);//設定fd為阻塞模式    int ret;    ret = read(fds[0],buf,10);    if(ret == -1){        perror("read error");        exit(EXIT_FAILURE);    }    printf("receive datas = %s\n",buf);    return 0;}

結果:

樣本三:read對應寫端關閉:如果所有管道寫端對應的檔案描述符被關閉,則read返回0
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h> int main(void){    int fds[2];    if(pipe(fds) == -1){        perror("pipe error");        exit(EXIT_FAILURE);    }    pid_t pid;    pid = fork();    if(pid == -1){        perror("fork error");        exit(EXIT_FAILURE);    }    if(pid == 0){        close(fds[1]);//子進程關閉寫端        exit(EXIT_SUCCESS);    }    close(fds[1]);//父進程關閉寫端    char buf[10] = {0};    int ret;    ret = read(fds[0],buf,10);    printf("ret = %d\n", ret);    return 0;}

結果:

可知確實返回0,表示讀到了檔案末尾,並不表示出錯

樣本四:write對應讀端關閉:如果所有管道讀端對應的檔案描述符被關閉,則write操作會產生訊號SIGPIPE
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h>#include <signal.h>void sighandler(int signo);int main(void){    int fds[2];    if(signal(SIGPIPE,sighandler) == SIG_ERR)    {        perror("signal error");        exit(EXIT_FAILURE);    }    if(pipe(fds) == -1){        perror("pipe error");        exit(EXIT_FAILURE);    }    pid_t pid;    pid = fork();    if(pid == -1){        perror("fork error");        exit(EXIT_FAILURE);    }    if(pid == 0){        close(fds[0]);//子進程關閉讀端        exit(EXIT_SUCCESS);    }    close(fds[0]);//父進程關閉讀端    sleep(1);//確保子進程也將讀端關閉    int ret;    ret = write(fds[1],"hello",5);    if(ret == -1){        printf("write error\n");    }    return 0;}void sighandler(int signo){    printf("catch a SIGPIPE signal and signum = %d\n",signo);}

結果:

可知當所有讀端都關閉時,write時確實產生SIGPIPE訊號

樣本五:O_NONBLOCK disable: write(阻塞)調用阻塞,直到有進程讀走資料
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h> int main(void){    int fds[2];    if(pipe(fds) == -1){        perror("pipe error");        exit(EXIT_FAILURE);    }    int ret;    int count = 0;    while(1){        ret = write(fds[1],"A",1);//fds[1]預設是阻塞模式        if(ret == -1){            perror("write error");            break;        }        count++;    }    return 0;}

結果:

說明:fd開啟時預設是阻塞模式,當pipe緩衝區滿時,write操作確實阻塞了,等待其他進程將資料從管道中取走

樣本六:O_NONBLOCK enable:write(非阻塞)調用返回-1,errno值為EAGAIN
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h> int main(void){    int fds[2];    if(pipe(fds) == -1){        perror("pipe error");        exit(EXIT_FAILURE);    }    int ret;    int count = 0;    int flags = fcntl(fds[1],F_GETFL);    fcntl(fds[1],F_SETFL,flags|O_NONBLOCK);    while(1){        ret = write(fds[1],"A",1);//fds[1]預設是阻塞模式        if(ret == -1){            perror("write error");            break;        }        count++;    }    printf("the pipe capcity is = %d\n",count);    return 0;}

結果:

可知也出現EGIN錯誤,管道容量是65536位元組

man 7 pipe說明:

Pipe capacity
       A pipe has a limited capacity.  If the pipe is full, then a write(2)       will block or fail, depending on whether the O_NONBLOCK flag is set       (see below).  Different implementations have different limits for the       pipe capacity.  Applications should not rely on a particular       capacity: an application should be designed so that a reading process       consumes data as soon as it is available, so that a writing process       does not remain blocked.       In Linux versions before 2.6.11, the capacity of a pipe was the same       as the system page size (e.g., 4096 bytes on i386).  Since Linux       2.6.11, the pipe capacity is 65536 bytes.
三,管道寫與PIPE_BUF關係
man協助說明:
PIPE_BUF
       POSIX.1-2001 says that write(2)s of less than PIPE_BUF bytes must be       atomic: the output data is written to the pipe as a contiguous       sequence.  Writes of more than PIPE_BUF bytes may be nonatomic: the       kernel may interleave the data with data written by other processes.       POSIX.1-2001 requires PIPE_BUF to be at least 512 bytes.  (On Linux,       PIPE_BUF is 4096 bytes.)  The precise semantics depend on whether the       file descriptor is nonblocking (O_NONBLOCK), whether there are       multiple writers to the pipe, and on n, the number of bytes to be       written:       O_NONBLOCK disabled, n <= PIPE_BUF              All n bytes are written atomically; write(2) may block if              there is not room for n bytes to be written immediately       阻塞模式時且n<PIPE_BUF:寫入具有原子性,如果沒有足夠的空間供n個位元組全部寫入,則阻塞直到有足夠空間將n個位元組全部寫入管道              O_NONBLOCK enabled, n <= PIPE_BUF              If there is room to write n bytes to the pipe, then write(2)              succeeds immediately, writing all n bytes; otherwise write(2)              fails, with errno set to EAGAIN.      非阻塞模式時且n<PIPE_BUF:寫入具有原子性,立即全部成功寫入,否則一個都不寫入,返回錯誤       O_NONBLOCK disabled, n > PIPE_BUF              The write is nonatomic: the data given to write(2) may be              interleaved with write(2)s by other process; the write(2)              blocks until n bytes have been written.      阻塞模式時且n>PIPE_BUF:不具有原子性,可能中間有其他進程穿插寫入,直到將n位元組全部寫入才返回,否則阻塞等待寫入       O_NONBLOCK enabled, n > PIPE_BUF              If the pipe is full, then write(2) fails, with errno set to              EAGAIN.  Otherwise, from 1 to n bytes may be written (i.e., a              "partial write" may occur; the caller should check the return              value from write(2) to see how many bytes were actually              written), and these bytes may be interleaved with writes by              other processes.
   非阻塞模式時且N>PIPE_BUF:如果管道滿的,則立即失敗,一個都不寫入,返回錯誤,如果不滿,則返回寫入的位元組數為1~n,即部分寫入,寫入時可能有其他進程穿插寫入
  • 當要寫入的資料量不大於PIPE_BUF時,linux將保證寫入的原子性。
  • 當要寫入的資料量大於PIPE_BUF時,linux將不再保證寫入的原子性。

註:管道容量不一定等於PIPE_BUF

樣本:當寫入資料大於PIPE_BUF時

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <errno.h>#include <fcntl.h>#define ERR_EXIT(m)         do         {                 perror(m);                 exit(EXIT_FAILURE);         } while(0)#define TEST_SIZE 68*1024int main(void){    char a[TEST_SIZE];    char b[TEST_SIZE];    char c[TEST_SIZE];    memset(a, ‘A‘, sizeof(a));    memset(b, ‘B‘, sizeof(b));    memset(c, ‘C‘, sizeof(c));    int pipefd[2];    int ret = pipe(pipefd);    if (ret == -1)        ERR_EXIT("pipe error");    pid_t pid;    pid = fork();    if (pid == 0)//第一個子進程    {        close(pipefd[0]);        ret = write(pipefd[1], a, sizeof(a));        printf("apid=%d write %d bytes to pipe\n", getpid(), ret);        exit(0);    }    pid = fork();        if (pid == 0)//第二個子進程    {        close(pipefd[0]);        ret = write(pipefd[1], b, sizeof(b));        printf("bpid=%d write %d bytes to pipe\n", getpid(), ret);        exit(0);    }    pid = fork();        if (pid == 0)//第三個子進程    {        close(pipefd[0]);        ret = write(pipefd[1], c, sizeof(c));        printf("bpid=%d write %d bytes to pipe\n", getpid(), ret);        exit(0);    }    close(pipefd[1]);        sleep(1);    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);    char buf[1024*4] = {0};    int n = 1;    while (1)    {        ret = read(pipefd[0], buf, sizeof(buf));        if (ret == 0)            break;        printf("n=%02d pid=%d read %d bytes from pipe buf[4095]=%c\n", n++, getpid(), ret, buf[4095]);        write(fd, buf, ret);    }    return 0;    }

結果:

可見各子進程間出現穿插寫入,並沒保證原子性寫入,且父進程在子進程編寫時邊讀。

關於socket阻塞與非阻塞情況下的recv、send、read、write傳回值

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.