s-kitaの日記 このページをアンテナに追加 RSSフィード

2008-06-08

非ブロッキングI/O

ソケットはデフォルトでブロッキング。ソケットに関するシステムコールは、4つに分類できる。

  • 入力操作
  • read, readv, recv, recvfrom, recvmsg。これらの関数をブロッキングTCPソケットに対して呼び出した際にソケットの受信バッファにデータが存在しない場合、呼び出したプロセスはデータが到着するまでスリープする。

    ある量のデータが到着するまで待ちたいときには、MSG_WAITALLフラグを指定する。

    非ブロッキングソケットでは、入力操作が即座に終了しない場合、これら関数呼び出しは即座にEWOULDBLOCKエラーを返して終了する。

  • 出力操作
  • write, writev, send, sendto, sendmsg。TCPソケットではカーネルがアプリケーションのバッファからソケットの送信バッファにデータをコピーする。ブロッキングソケットの送信バッファに空きがない場合、呼び出したプロセスは秋が生じるまでスリープさせられる。

    非ブロッキングTCPソケットでは、ソケットの送信バッファに空きが全くない場合、これらの関数は、EWOULDBLOCKエラーを返して即座に終了する。ソケットの送信バッファに空きがあった場合、カーネルがバッファにコピーできたバイト数を関数の戻り値として返す。

    UDPソケットには送信バッファが存在しない。


  • 新規コネクションの受付 accept関数
  • 新しいコネクションが存在しないブロッキングソケットに対してacceptを呼び出すと、呼び出したプロセスはスリープさせられる。

    新しいコネクションが存在しない非ブロッキングソケットに対してacceptを呼び出すとブロックする代わりにEWOULDBLOCKエラーが返される。


  • 新規コネクションの起動 TCPに対するconnect関数
  • TCPのコネクションを確立するには3WHSが必要で、connect関数はクライアントがそのSYNに対するACKを受信するまで制御を戻さない。これはTCPに対するconnectを呼び出したプロセスは少なくともRTT(Round Trip Time)だけブロックすることを意味する。

    非ブロッキングTCPソケットに対してconnectが呼び出され、コネクションを即座に確立することが出来ない場合、コネクションの確立が起動されるが、EINPROGRESSエラーが返される(errnoにセットされる)。

    非ブロッキングconnect
    
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/time.h>
    
    int 
    set_nonblocking(int sockfd)
    {
      int val;
      val = fcntl(sockfd, F_GETFL, 0); 
      fcntl(sockfd, F_SETFL, val | O_NONBLOCK);
    }
    
    int 
    set_blocking(int sockfd)
    {
      int val;
      val = fcntl(sockfd, F_GETFL, 0);
      val = val & ~(val & O_NONBLOCK);
      fcntl(sockfd, F_SETFL, val);
    }
    
    
    int
    main()
    {
      int sockfd;
      struct sockaddr_in server;
      int ret;
      fd_set writefds;
      int maxfdp1;
      char *req = "GET / HTTP/1.1\r\n\r\n";
      int n;
      char buff[1024];
      int len;
      struct timeval timeout;
    
      sockfd = socket(AF_INET, SOCK_STREAM, 0);
     
      server.sin_family = AF_INET;
      server.sin_port = htons(80);
      server.sin_addr.s_addr = inet_addr("127.0.0.1");
     
      set_nonblocking(sockfd);
      ret = connect(sockfd, (struct sockaddr*)&server, sizeof(server));
      set_blocking(sockfd);
    
      FD_ZERO(&writefds);
      FD_SET(sockfd, &writefds);
      maxfdp1 = sockfd + 1;
      timeout.tv_sec = 3;
      timeout.tv_usec = 0;
      if ( (ret = select(maxfdp1, NULL, &writefds, NULL, &timeout) ) > 0) {
        if (FD_ISSET(sockfd, &writefds)) {   
          n = send(sockfd, req, strlen(req), 0);
          len = recv(sockfd, buff, sizeof(buff), 0);
          if (len > 0) {
            buff[len] = '\0';
            printf("%s\n", buff);
          }
        }
      } else if (ret == 0) {
        printf("timeout\n");
      } else {
        printf("error = %d\n", errno);
      }
     
      return 0;
    }
    
    


    非ブロッキングI/Oでは、ディスクリプタの読み書きの可能性を検査するために、普通selectが併用される。

    スパム対策のためのダミーです。もし見えても何も入力しないでください
    ゲスト


    画像認証

    Connection: close