CSAPP 11. Network Programming

22360 ワード


ジャングルでc言語でWebサーバ,コード,CSAPP書の第11章を実現した.ジャングルで駐車Webサーバーを習った内容を整理したいです.
マットの上で整理した后に持ってきた部分のイメージがあまり良くなくてあるいは笔记がとても乱れています...

1. The Client-Server Programming Model



A server manages some resource, and it provides some service for its clients by manipulating that resource.
サーバは、リソースを管理し、リソースを管理してクライアントにサービスを提供します.

クライアントとは?


サービスを利用するユーザまたはユーザの端末を指す.

サーバとは


サービスを提供するコンピュータであり、多くのクライアントに存在するため、通常は非常に大きな容量とパフォーマンスを有します.しかし,最近ではサーバの役割とクライアントの機能を同時に果たす環境が数多く出現している.
クライアント-サーバの基本トランザクション.
  • クライアント(1つ以上)は、トランザクションを開始するためにサーバに要求を送信します.
  • サーバは、要求を受信し、特定の関数を実行することによってサーバ上のリソースを操作する.
  • サーバは、2の結果として応答をクライアントに送信し、次の要求を待つ.
  • クライアントは、応答結果を処理します.(ex.受信した結果を画面上に置くなど)
  • ネットワーク


    ネットワークとは


    A computer network is a set of computers sharing resources located on or provided by network nodes.
    これは、ネットワークノード(endpoint)によって提供されるリソースを共有するためのコンピュータの集合を意味する.
    ネットワークはホストごとにI/Oデバイスとして認識される.I/Oバス拡張スロットを挿入するアダプタは、ネットワークに物理的なインタフェースを提供します.ネットワークから受信したデータはI/Oとメモリバスを経由し、アダプタからメモリに至るまで、通常DMA方式でコピーされる.

    物理的には、ネットワークは階層を持つシステムです.ネットワークは学習を必要とする領域であるが,この本はHost‐Hub‐Bridgeの階層構造を有することを示す簡潔に紹介した.ブリッジとブリッジの間でルータを介してデータを転送します.
    服役中に隣の中隊から撤退して大掃除をしたのを覚えていますが、黄色い太い網が深さいっぱいの網橋に差し込まれた電源線を抜いて行ってしまい、しばらくネットが途切れて指揮官たちがひっくり返ってしまいました.

    これにより、ホストとホストの間でネットワークを介して通信すると、トランスポート層はホスト内のプロセスとプロセスとの間の通信を行う
    可能にする.IPアドレスは唯一の端末アドレスである.これはホームアドレスの概念であり、コンピュータ上で複数のプロセスが実行されている可能性があり、IPアドレスの後ろのPortに渡される.

    Socket Interface


    ソケットとは?


    ソケットは、ネットワーク上の2つのプロセスが双方向に通信できる装置です.
    カーネルの場合、ソケットは開いているファイルです.ファイルの読み取り/書き込みにより、他のプロセスと通信できます.
    では、コンセントのインターフェースは?
    ソケットインタフェースは、アプリケーション間のネットワークを構成するための関数の集合です.

    クライアントは、getaddrinfoを介してupd方式でサーバのIPアドレスを受信する.
    int socket(int domain, int type, int protocol);
    このアドレスをパラメータとしてsocketを生成し、connect関数を実行する.
     int connect(int clientfd, const struct sockaddr *addr, socklen_t addrlen);
    最初のパラメータは、socket関数として返されるclientfdをパラメータとして受信し、サーバへの接続を試みます.connect関数は、接続が成功する前にブロックされ、成功した場合、clientfdはサーバ上のconnfdに接続され、ファイルを読み取り/書き込みますが、接続に失敗した場合は-1に戻ります.
    サーバがソケットを作成した後、bind関数を使用してサーバのIP、ポートアドレスをそのソケットにバインド(貼り付け)する必要があります.
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    最初は、クライアントが要求をサーバアドレスに送信する機会があり、サーバがなぜ自分のアドレスをバインドするのか、サーバコンピュータの立場で複数のプロセスを実行し、異なるアドレスを区別するために異なるポート番号が必要になるため、独自のソケットが作成されます.
    int listen(int sockfd, int backlog);
    その後、最初のパラメータはbind関数であり、listen関数を実行するために割り当てられたアドレスのsocketfdを受信し、サーバがクライアントから要求を送信し、接続に成功するように要求すると、サーバ側はconnfdを生成し、クライアント側はclientfdを生成する.backlogに送信される第2のパラメータは、clientによって送信されるパケットがキューに入り、backlogのサイズを最大限に保持することができる.tcp方式では、キューにbacklogがあればパケットが失われ、サーバに要求を送信する必要がある.(遅くなる)

    コード#コード#


    クライアントとサーバのプライマリ・プライマリ・コード.

    Client

    int open_clientfd(char *hostname, char *port) {
        int clientfd, rc;
        struct addrinfo hints, *listp, *p;
    
        /* potential server addresses 리스트 설정 */
        memset(&hints, 0, sizeof(struct addrinfo));
        hints.ai_socktype = SOCK_STREAM;  /* 연결 설정 */
        hints.ai_flags = AI_NUMERICSERV;  /* port를 숫자로 받기. */
        hints.ai_flags |= AI_ADDRCONFIG;  /* Recommended for connections */
        if ((rc = getaddrinfo(hostname, port, &hints, &listp)) != 0) {
            fprintf(stderr, "getaddrinfo failed (%s:%s): %s\n", hostname, port, gai_strerror(rc));
            return -2;
        }
      
        /* 연결될 때 까지 addr 가 나온 구조체를 돌면서 연결 요청 */
        for (p = listp; p; p = p->ai_next) {
            /* Create a socket descriptor */
            if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) // 소캣 생성 
                continue; 
                
            /* Connect to the server */
            if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) // 서버에 연결을 요청 
                break; 
            if (close(clientfd) < 0) { /* 연결 닫기 */
                fprintf(stderr, "open_clientfd: close failed: %s\n", strerror(errno));
                return -1;
            } 
        } 
    
        /* 구조체 정리 */
        freeaddrinfo(listp);
        if (!p) /* All connects failed */
            return -1;
        else    /* The last connect succeeded */
            return clientfd;
    }

    server

    int open_listenfd(char *port) 
    {
        struct addrinfo hints, *listp, *p;
        int listenfd, rc, optval=1;
    
        /* server addresses  설정*/
        memset(&hints, 0, sizeof(struct addrinfo));
        hints.ai_socktype = SOCK_STREAM;             /* SOCK_STREAM  */
        hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* IP Address */
        hints.ai_flags |= AI_NUMERICSERV;            
        if ((rc = getaddrinfo(NULL, port, &hints, &listp)) != 0) {
            fprintf(stderr, "getaddrinfo failed (port %s): %s\n", port, gai_strerror(rc));
            return -2;
        }
    
       
        for (p = listp; p; p = p->ai_next) {
            /* Create a socket descriptor */
            if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) // 소켓을 생성 
                continue;  /* Socket failed, try the next */
    
            /* "Address already in use" error 해제 */
            setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 
                       (const void *)&optval , sizeof(int));
    
            /* Bind the descriptor to the address */
            if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) // 해당 소켓을 listenfd 에 bind 처리함
                break; /* Success */
            if (close(listenfd) < 0) { /* Bind failed, try the next */
                fprintf(stderr, "open_listenfd close failed: %s\n", strerror(errno));
                return -1;
            }
        }
    
     
        freeaddrinfo(listp);
        if (!p) /* No address worked */
            return -1;
    
        /* listen 상태로 client 기다리기 */
        if (listen(listenfd, LISTENQ) < 0) {
            close(listenfd);
    	return -1;
        }
        return listenfd;
    }