Linuxネットワークプログラミング——ネットワーク高度プログラミング

11185 ワード

実際の状況では、複数のクライアントがサービス側に接続されている場合がよくあります.connect()やrecv()やsend()などがブロック関数であるため,リソースの準備ができていないとその関数を呼び出すプロセスがスリープ状態になり,I/O多重化の処理ができなくなる.次の2つの解決策を示します.
1.fcntl()
(1)fcntl()レビュー:
fcntl()関数構文の要点
必要なヘッダファイル
#include #include #include
関数プロトタイプ
int fcntl(int fd,int cmd,struct flock *lock)
関数の入力値
fd:ファイル記述子
cmd
F_DUPFD:ファイル記述子のコピー
F_GETFD:fdのclose-on-execフラグを取得し、フラグが設定されていない場合、ファイルはexec()関数を通過した後も開いたままである
F_SETFD:パラメータargのFD_CLOEXECビット決定
F_GETFL:Open設定フラグを取得
F_SETFL:open設定変更フラグ
F_GETLK:lockパラメータ値により、ファイルロックの有無を決定する
F_SETLK:lockパラメータ値を設定するファイルロック
F_SETLKW:F_ですSETLKのブロックバージョン(コマンド名のWはウエイトを表す).ロックが取れない場合は、睡眠状態になります.ロックを取得したり、信号をキャプチャしたりできる場合は戻ります.
lock:flockとして構成され、レコードロックの具体的な状態を設定します
関数の戻り値
0:成功
-1:エラー
lockの構造は以下の通りです.
struct flock
{
    short l_type;
    //F_RDLCK:   (   )
    //F_WRLCK:   (   )
    //F_UNLCK:  
    off_t l_start;            //     (  )
    short l_whence;           //        
    //SEEK_SET:          ,          
    //SEEK_CUR:            ,             
    //SEEK_END:          ,                 
    off_t l_len;              //       
    pid_t l_pid;
}

(2)関数fcntl()はsocketプログラミングに対して以下のプログラミング特性を提供する.
(1)非ブロックI/O:cmdをF_に設定可能SETFL,lockをO_に設定NONBLOCK.
(2)非同期I/O:cmdをF_に設定可能SETFL,lockをO_に設定ASYNC.
次に、fcntl()を使用してソケットを非ブロックI/Oに設定するインスタンスコードを示します.
/*net_fcntl.c*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PORT             1234  //   
#define BUFFER_SIZE      1024
#define MAX_QUE_CONN_NM  5

int main()
{
        struct sockaddr_in server_sockaddr,client_sockaddr; /* Structure describing an Internet (IP) socket address. */
        int sin_size,recvbytes,flags;
        int sockfd,client_fd;//sockfd=     
        char buf[BUFFER_SIZE];

        /*  socket  */
        if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)//AF_INET=IPv4,SOCK_STREAM=TCP
        {
                perror("socket");
                exit(1);
        }
        printf("Socket id = %d
",sockfd); /* sockaddr_in */ server_sockaddr.sin_family = AF_INET; //sin_family= ,AF_INET=IPv4 server_sockaddr.sin_port = htons(PORT);//sin_port= ,htons() > server_sockaddr.sin_addr.s_addr = INADDR_ANY;//s_addr=IP bzero(&(server_sockaddr.sin_zero),8);// 0 struct sockaddr int i=1;// if((setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))) < 0)// 、 { perror("setsockopt"); exit(1); } /*sockfd = level = : SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP IPPROTO_IPV6 optname = optval = , optlen = optval */ /* bind()*/ if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr)) == -1)//server_sockaddr = { //sizeof(struct sockaddr) = perror("bind"); exit(1); } printf("Bind success!
"); /* listen() , */ if(listen(sockfd,MAX_QUE_CONN_NM) == -1)//MAX_QUE_CONN_NM= > , 5 { perror("listen"); exit(1); } printf("Listening...
"); flags = fcntl(sockfd,F_GETFL); if(flags < 0 || fcntl(sockfd,F_SETFL,flags|O_NONBLOCK) < 0) { perror("fcntl"); exit(1); } while(1) { sin_size = sizeof(struct sockaddr_in); /* accept() , */ if((client_fd = accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size)) == -1)//client_sockaddr = { // perror("accept"); exit(1); } /* recv() */ if((recvbytes = recv(client_fd,buf,BUFFER_SIZE,0)) == -1)// { perror("recv"); exit(1); } printf("Received a message:%s
",buf); } close(sockfd); exit(0); }

2.select()
(1)select()レビュー
select()関数構文の要点
必要なヘッダファイル
#include #include #include
関数プロトタイプ
int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exeptfds,struct timeval *timeout)
関数の入力値
numfds:このパラメータ値は、監視が必要なファイル記述法の最大値に1を加算します.
readfds:select()によって監視されるリードファイル記述子セット
writefds:select()によって監視される書き込みファイル記述子セット
exeptfds:select()によって監視される異常処理ファイル記述子セット
timeout
NULL:信号やファイル記述子がスナップされるまで待機します.
具体的な値:struct timevalタイプのポインタは、timeout時間を待ってもファイル記述子が検出されない場合は、すぐに戻ります.
0:指定したすべての記述子をテストし、すぐに戻ります.
関数の戻り値
0より大きい:成功しました.準備したファイル記述子の数を返します.
0:タイムアウト
-1:エラー
ファイル記述子を処理するマクロ関数:
select()ファイル記述子処理関数
FD_ZERO(fd_set *set)
ファイル記述子のセットをクリア
FD_SET(int fd,fd_set *set)
ファイル記述子のセットにファイル記述子を追加
FD_CLR(int fd,fd_set *set)
ファイル記述法をファイル記述子セットから消去する
FD_ISSET(int fd,fd_set *set)
ファイル記述法fdがfd_である場合setセットの要素の1つです.ゼロ以外の値を返します.select()を呼び出した後、ファイル記述子セットのファイル記述子が変化しているかどうかをテストするために使用できます.
PS:select()を使用する前に、まずFD_を使用するZERO()とFD_SET()は、ファイル記述子の集合を初期化する.
select()を使用する場合、FD_をループします.ISSET()は記述子の集合をテストする.
関連記述子の操作が完了したら、FD_を使用します.CLR()は記述子セットをクリアする.
select()関数のtimeoutはstruct timevalタイプのポインタで、構造体は以下の通りです.
struct timeval
{
    long tv_sec;     /*   */
    long tv_unsec;   /*    */
}

(2)select()関数を使用するサーバ側ソースコードを次に示します.
/*net_select.c*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PORT             4321  //   
#define BUFFER_SIZE      1024
#define MAX_QUE_CONN_NM  5
#define MAX_SOCK_FD          FD_SETSIZE

int main()
{
        struct sockaddr_in server_sockaddr,client_sockaddr; /* Structure describing an Internet (IP) socket address. */
        int sin_size,count;
        fd_set inset,tmp_inset;
        int sockfd,client_fd,fd;//sockfd=     
        char buf[BUFFER_SIZE];

        /*  socket  */
        if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)//AF_INET=IPv4,SOCK_STREAM=TCP
        {
                perror("socket");
                exit(1);
        }
        //printf("Socket id = %d
",sockfd); /* sockaddr_in */ server_sockaddr.sin_family = AF_INET; //sin_family= ,AF_INET=IPv4 server_sockaddr.sin_port = htons(PORT);//sin_port= ,htons() > server_sockaddr.sin_addr.s_addr = INADDR_ANY;//s_addr=IP bzero(&(server_sockaddr.sin_zero),8);// 0 struct sockaddr int i=1;// if((setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))) < 0)// 、 { perror("setsockopt"); exit(1); } /*sockfd = level = : SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP IPPROTO_IPV6 optname = optval = , optlen = optval */ /* bind()*/ if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr)) == -1)//server_sockaddr = { //sizeof(struct sockaddr) = perror("bind"); exit(1); } printf("Bind success!
"); /* listen() , */ if(listen(sockfd,MAX_QUE_CONN_NM) == -1)//MAX_QUE_CONN_NM= > , 5 { perror("listen"); exit(1); } printf("Listening...
"); /* socket() */ FD_ZERO(&inset);// inset fd FD_SET(sockfd,&inset);// fd inset while(1) { tmp_inset = inset; sin_size = sizeof(struct sockaddr_in); memset(buf,0,sizeof(buf)); /* select() */ if(!(select(MAX_SOCK_FD,&tmp_inset,NULL,NULL,NULL)) > 0) { perror("select"); } for(fd=0;fd 0) { if(fd == sockfd) { /* */ /* accept() , */ if((client_fd = accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size)) == -1)//client_sockaddr = { // perror("accept"); exit(1); } FD_SET(client_fd,&inset); printf("New connection from %d(socket)
",client_fd); } else { /* recv() */ if((count = recv(fd,buf,BUFFER_SIZE,0)) > 0)// { printf("Received a message from %d:%s
",fd,buf); } else { close(fd); FD_CLR(fd,&inset); printf("Client %d(socket) has left
",fd); } } } } } close(sockfd); exit(0); }