TCP/IPネットワークプログラミング(四)

7605 ワード

ソケットと標準I/O
標準I/O関数の利点
  • は良好な移植性を有する
  • である.
  • は、バッファリングにより性能を向上することができる
  • .
    ソケットを作成すると、オペレーティングシステムはI/O用のバッファを生成します.このバッファリングは、TCPプロトコルを実行する際に重要な役割を果たす.標準関数を使用すると、追加の別のバッファのサポートが得られます.
    関数バッファは伝送性能を向上させるためであり,ソケットバッファはウィンドウ制御,再送などのプロトコルを実現するためである.
    パフォーマンスの向上は、次の2つの観点から考えられます.
  • 伝送データ量
  • データの出力バッファへの移動回数
  • .
    標準関数の欠点:
  • 双方向通信が容易でない
  • .
  • は、fflush関数
  • を頻繁に使用する場合がある.
  • は、FILE構造体ポインタとしてファイル記述子
  • に戻る必要がある.
    標準I/O関数の使用
    ポインタ変換
    #include <stdio.h>
    
    FILE * fdopen(int fildes, const char * mode); //           FILE      
    
    ~ fildes:       
    ~ mode:     ,   (r)    (w)
    FILE * fp;
    int fd = open("data.dat", O_WRONLY|O_CREAT|O_TRUNC);
    fp = fdopen(fd, "w");
    ...
    fclose(fp);

    ファイル記述子に変換
    #include <stdio.h>
    
    int fileno(FILE * stream); //      -1

    ソケットベースの標準I/O関数の使用
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    
    #define BUF_SIZE 1024
    void err_handling(char *message);
    
    int main(int argc, char *argv[])
    {
        int serv_sock;
        int clnt_sock;
        char message[BUF_SIZE];
        int str_len, i;
    
        struct sockaddr_in serv_addr;
        struct sockaddr_in clnt_addr;
        socklen_t clnt_addr_size;
    
        FILE *readfd;
        FILE *writefd;
    
    
        if(argc != 2)
        {
            printf("Usage : %s <port> 
    "
    , argv[0]); exit(1); } serv_sock = socket(PF_INET, SOCK_STREAM, 0); if(serv_sock == -1) err_handling("socket() error"); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) err_handling("shenmecuoel bind() error"); if(listen(serv_sock, 5) == -1) err_handling("listen() error"); clnt_addr_size = sizeof(clnt_addr); for(i=0; i<5; i++) { clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); printf("clnt_sock : %d
    "
    , clnt_sock); if(clnt_sock == -1) err_handling("accept() error"); else printf("Connected client. %d
    "
    , i+1); readfd = fdopen(clnt_sock, "r"); // FILE writefd = fdopen(clnt_sock, "w"); while(!feof(readfd)) { fgets(message, BUF_SIZE, readfd); fputs(message, writefd); fflush(writefd); } fclose(readfd); fclose(writefd); } close(serv_sock); return 0; } void err_handling(char * message) { fputs(message, stderr); fputc('
    '
    , stderr); exit(1); }
    echo_client.c

    I/O分離のメリット
  • は、fork関数を呼び出すことによって、入力および出力で使用されるファイル記述子を区別するためにファイル記述子をコピーすることを前述した.
  • 入力プロセスと出力プロセスを分離することによって符号化の実現の難易度を低減する
  • .
  • 入力に関係のない出力動作は、速度
  • を向上することができる.
  • は、読み込みモードFILEおよび書き込みモードFILEのポインタ分離入力ツールおよび出力ツールを作成する.
  • FILEのポインタを読み書きモードと読み書きモードで区別するために
  • .
  • 実現難易度を低減
  • I/Oキャッシュを区別することにより性能を向上させる
  • .

    ファイルディスクリプタのコピーとハーフクローズ
    同じファイル記述子に対してFILEポインタを変換した後のモデルは以下の通りである.
    2つのポインタは同じファイル記述子を指すため、任意のFILEポインタに対してfclose関数を呼び出すと、ファイル記述子、すなわち終了ソケットが閉じられます.
    上記の状況は私たちが避けるべきことですが、どのようにして半閉鎖を実現すればいいのでしょうか.
    次のモデルを作成します.
    すべてのファイル記述子を破棄した後でのみソケットが破棄されるため、各ファイル記述子を使用してポインタを作成します.これはオペレーティングシステムによって保証されます.
    書き込みモードポインタがオフの場合、対応するファイル記述子のみが破棄され、ソケットは破棄されません.
    これは、オリジナルファイル記述子によってI/Oを行うことができるため、不完全である.後で具体的な実装を示します.
    ファイル記述子のコピー
    ここで説明するレプリケーションとfork関数レプリケーションは、プロセス全体が異なり、同じプロセスに同じソケットが存在する異なるファイル記述子を作成します.
    #include <unistd.h>
    
    int dup(int fildes);
    int dup2(int fildes, int fildes2);
    
    ~ fildes:           
    ~ fildes2:              

    ファイル記述子のコピー後のストリームの分離
    FILE * readfp;
    FILE * writefp;
    ...
    readfp = fdopen(clnt_sock, "r");
    writefp = fdopen(dup(clnt_sock), "w");
    ...
    fflush(writefp);
    shutdown(fileno(writefp), SHUT_ER); //           ,       EOF。      ,                     ,    EOF。