TCP/IPネットワークプログラミング学習ノート


最近はネットのプログラミングを勉強しています。メモを作りました。簡単で、明確で、自分で調べやすく、多くの人に見てもらいたいです。
1、ネットワークプログラムで接続要求を受け付けるソケットの作成過程
第一歩:socket関数を呼び出してソケットを作成します。
第二ステップ:bind関数を呼び出してIPアドレスとポート番号を割り当てます。
第三ステップ:listen関数を呼び出して、受信可能要求状態に遷移する。
ステップ4:accept関数を呼び出して接続要求を受け付ける
2、サーバー側で作成されたソケットはサーバー端子ソケットまたは傍受ソケットとも呼ばれます。
3、クライアントは「socket関数を呼び出してソケットを作成する」と「connect関数を呼び出してサーバ側に接続要求を送信する」という二つのステップだけです。
4、ファイル記述子
ファイルまたはソケットを生成するたびに、オペレーティングシステムは、それらに割り当てられた整数を返します。この整数はプログラマーとオペレーティングシステムとの間の良好なコミュニケーションのルートになります。実際、ファイル記述子は、オペレーティングシステムが作成したファイルやソケットを便利に呼ぶために与えられた数にすぎない。
ファイル記述子は、ファイルのハンドルとも呼ばれることがあるが、「ハンドル」は主にWindowsの用語である。
5、協議
簡単に言えば、契約はデータ交換を完了するために約束された約束です。
6、ソケットの作成
#include 

int socket(int domain, int type, int protocol)
    ->           ,     -1
  • domanソケットに使用されるプロトコルクラスタ情報
  • typeソケットデータ伝送タイプ情報
  • protocolコンピュータ間通信に使用されるプロトコル情報
  • プロトコルクラスタ
    名前
    プロトコルクラスタ
    PF_INET
    IPv 4インターネットプロトコルクラスタ
    PF_INE 6
    IPv 6インターネットプロトコルクラスタ
    PF_LOCAL
    ローカル通信のUNIXプロトコルクラスタ
    PF_PACKET
    下のソケットのプロトコルクラスタ
    PF_IPX
    IPX Novellプロトコルクラスタ
    ソケットで実際に採用されている最終プロトコル情報は、socket関数の3番目のパラメータで伝達されます。指定されたプロトコルクラスタの範囲内で第一パラメータにより第三パラメータが決定される。
    ソケットタイプ(type)
    ソケットタイプとは、ソケットのデータ伝送方式を指し、socket関数の第二のパラメータ伝達によって、作成されたソケットのデータ伝送方式を決定することができる。
    ソケットタイプ1:接続に向けたソケット(SOCKET_USTREAM)
    この方法の特徴:
  • 伝送中にデータは消えません。
  • は、データを逐次転送する
  • によって送信されたデータは、データ境界
  • に存在しない。
    一言では、接続に向けたソケットを要約します。「信頼できる、順番に伝達される、バイトベースの接続に向けたデータ転送方式のソケット」
    ソケットタイプ2:メッセージ向けソケット(SOCK_uDGRAM)
    この方法の特徴:
  • は、伝送順序
  • ではなく、高速伝送を強調する。
  • 伝送のデータは失われるかもしれません。
  • を損なうかもしれません。
  • 伝送のデータには、データ境界
  • があります。
  • は、伝送毎のデータサイズ
  • を制限する。
    メッセージに向けたソケットは、接続に向けたソケットよりも速い伝送速度を有していますが、データの紛失や破損は避けられません。
    「信頼できない、順番に渡さない、データの高速転送を目的としたソケット」
    プロトコルの最終選択(3番目のパラメータ)
    socket関数の最初の2つのパラメータを通してプロトコルクラスタ情報とソケットデータ伝送方式が伝えられました。一般的には、この2つのパラメータは必要なソケットを作成することができます。したがって、ほとんどの場合は、以下のような場合を除き、第3のパラメータに0を渡すことができる。
    「同じプロトコルクラスタには複数のデータ転送方式と同じプロトコルが存在する」
    以下のソケットを作成します。「IPv 4プロトコルクラスタの接続に向けたソケット」、パラメータPF_INETはIPv 4ネットワークプロトコルクラスタ、SOCK_を指す。STREAMは接続に向けたデータ伝送である。この二つの条件を満たすプロトコルはIPPROTO_のみです。TCP、したがって:
    int tcp_socket=socket(PFuINET、SOCK__STREAM、IPPROTO_TCP)
    以下に、以下のソケットを作成します。「IPv 4プロトコルクラスタにおけるメッセージ向けのソケット」
    int up_socket=socket(PFuINET、SOCK_uDGRAM、IPPROTO_UDP);
    7、アドレス族とデータ系列
    IPは、ネットワークデータを送受信するためにコンピュータに割り当てられた値です。ポート番号は、コンピュータに与えられた値ではなく、プログラムで作成されたソケットを区別するためにソケットのシーケンス番号に割り当てられます。
    ネットワークアドレス(Internet Address)
    IPアドレスは2種類に分けられます。IPv 4とIPv 6
    まとめ:IPは特定のホストに位置するためのもので、ポート番号は特定のホストに位置するためのプログラムです。
    ポート番号は重複を許さないが、TCPソケットとUDPソケットは共通ポート番号ではないので重複を許可します。例えば、あるTCPソケットが9190番のポートを使用すると、他のTCPソケットは9190を使用できませんが、UDPソケットは使用できます。
    つまり、データ転送先アドレスはIPアドレスとポート番号を同時に含んでいます。このようにしてこそ、データは最終的なアプリケーションに転送されます。
    住所情報の表示
    アプリケーションで使用するIPアドレスとポート番号は、構造体として定義されています。
    struct sockaddr_in
    {
        sa_family_t     sin_family;     //   (Address Family)
        uint16_t        sin_port;       //16 TCP/UDP   
        struct in_addr  sin_addr;       //32 IP  
        char            sin_zero[8];    //   
    }
    この構造体で述べられたもう一つの構造体in_addrは以下のように定義されています。32ビットのIPアドレスを保存するために使用されます。
    struct in_addr
    {
        In_addr_t       s_addr;         //32 ipv4  
    }
    ネットワークのバイト順は大端順と同じです。
    サーバー側のよくあるソケット初期化プロセス
    int serv_sock;
    struct sockaddr_in serv_addr;
    char *serv_port = "9190";
    
    //         (     )
    serv_sock=socket(PF_INET,SOCK_STREAM,0);
    
    //       
    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(serv_addr));
    
    //      
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    8、TCPに基づくサーバ端とクライアント
    データ伝送方式によって、ネットワークプロトコルに基づくソケットは、一般にTCPソケットとUDPソケットに分けられます。TCPソケットは接続に向けられているため、ストリームベースのソケットとも呼ばれる。
    サーバ側が接続要求待ち状態とは、クライアントが接続を要求した場合、接続を受け付ける前に要求接続を待機状態とすることです。
    ハロワールドサーバー端コードserver.
    int main(int argc, char *argv[]){
        int serv_sock;
        int clnt_sock;
    
        struct sockaddr_in serv_addr;
        struct sockaddr_in clnt_addr;
        socklen_t clnt_addr_size;
    
        char message[] = "hello world!";
    
        if(argc != 2){
            printf("Usage: %s 
    "
    , agrv[0]); exit(1); } serv_sock = socket(PF_INET, SOCK_STREAM, 0); if(serv_sock == -1){ error_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) error_handling("bind() error"); if(listen(serv_sock, 5)==-1) error_handling("listen() error"); clnt_addr_size = sizeof(clnt_addr); clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size); if(clnt_sock == -1){ error_handling("accept() error"); } write(clnt_sock, message, sizeof(message)); close(clnt_sock); close(serv_sock); return 0; } void error_handling(char *message){ fputs(message, stderr); fputc('
    '
    , stderr); exit(1); }
    ハローワールドクライアントworld.
    int main(int argc, char *argv[]){
        int sock;
        struct sockaddr_in serv_addr;
        char message[30];
        int str_len;
    
        if(argc != 3){
            printf("Usage: %s  
    "
    , argv[0]); exit(0); } sock = socket(PF_INET, SOCK_STREAM, 0); if(sock == -1){ error_handling("socket() error"); } memset(&serv_addr, 0 ,sizeof(serv_addr); serv_addr.sin_family=AF_INET; serv_addr.sin_addr.s_addr=inet_addr(argv[1]); serv_addr.sin_port=htons(atoi(argv[2])); if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1) error_handling("connect() error"); str_len = read(sock. message, sizeof(message)-1); if(str_len == -1){ error_handling("read() error"); } printf("Message from server : %s
    "
    ,message); close(sock); return 0; } void error_handling(char *message){ fputs(message, stderr); fputc('
    '
    ,stderr); exit(1); }
    9、UDPベースのサーバ端/クライアント
    UDPソケットの特徴
    UDPの動作原理をメッセージで説明し,UDP特性と完全に一致している。手紙を出す前に、まず封筒に差出人と受取人の住所を記入してから、切手を貼ってポストに入れます。もちろん、手紙の特徴は相手が受け取ったかどうか確認できません。また、郵送中に手紙が紛失することもあります。つまり、手紙は信頼できない伝送方式である。これに類似して、UDPは同様に信頼できないデータ伝送サービスを提供しています。
    フロー制御は、UDPとTCPを区別する最も重要なフラグである。TCPの生命はフロー制御にある。TCPは信頼性のないIP層でフロー制御を行うが、UDPにはこのようなフロー制御機構がない。
    UDPの最も重要な役割はポート番号によってホストに渡されるパケットを最終的なUDPソケットに渡すことです。
    TCPがUDPより遅い理由は、通常以下の2つがあります。
  • データの送受信前後に行われる接続設定およびクリアプロセス
  • データの送受信中に信頼性を保証するために追加されたフロー制御
  • UDPベースのサーバー/クライアントを実現
    UDPではサーバー側とクライアントが接続されていません。
    UDPサーバ端/クライアントはTCPのように接続状態でデータを交換しないので、TCPとは違って、接続プロセスを経なくても大丈夫です。つまり、TCP接続中に呼び出されるlisten関数とaccept関数を呼び出す必要はない。UDPにはソケットとデータ交換プロセスのみが作成されます。
    UDPサーバ側とクライアント側は全部1つのソケットだけが必要です。
    TCPではソケット間は一対一の関係であるべきです。10個のクライアントにサービスを提供するには、守門のサーバソケット以外に、10個のサーバー端子ソケットが必要です。しかし、UDPでは、サーバー側であれ、クライアント側であれ、ソケットは1つだけ必要です。
    UDPベースのデータI/O関数
    TCPソケットを作成したら、データを転送する時にアドレス情報を追加する必要がありません。TCPソケットは、相手ソケットとの接続を維持するためです。つまり、TCPソケットは宛先情報を知る。しかし、UDPソケットは接続状態を維持しません(UDPソケットは簡単なポスト機能だけです)ので、転送データごとにターゲットアドレス情報を追加します。これは手紙を出す前に住所を書き添えるのと同じです。
    データ転送時に呼び出すUDP相関関数
    #include
    
    ssize_t sendto(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen);
    
        ->           ,     -1
    
        sock           UDP        
        buff                 
        nbytes          ,      
        flags        ,      0
        to               sockaddr         
        addrlen      to           
    UDPデータを受信する関数
    #include
    
    ssize_t recvfrom(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen);
    
        ->           ,     -1
    UDPプログラムを作成する時の一番核心部分は上記の二つの関数です。
    UDPベースのエコーサーバ端/クライアント
    UDPとTCPは違って、要求接続と受付プロセスが存在しないので、サーバ端とクライアントを明確に区別することはできません。ただ、サービスを提供するためにサーバ端と呼ばれています。
    uechoserver.
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_SIZE 30
    void error_handing(char *message);
    
    int main(int argc, char *argv){
        int serv_sock;
        char message[BUF_SIZE];
        int str_len;
        socklen_t clnt_adr_sz;
        struct sockaddr_in serv_adr,clnt_adr;
        if(argc != 2){
            printf("Usage: %s 
    "
    ,argv[0]); exit(1); } serv_sock = socket(PF_INET,SOCK_DGRAM, 0); if(serv_sock == -1) error_handling("UDP socket creation error"); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=htonl(INADDR_ANY); serv_adr.sin_port=htons(atoi(argv[1])); if(bind(serv_sock,, (struct sockaddr*)&serv)_adr, sizeof(serv_addr))==-1) error_handling("bind() error"); while(1){ clnt_adr_sz=sizeof(clnt_adr); str_len=recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_adr, &clnt_adr_sz); sendto(serv_sock, message, str_len, 0, (struct sockaddr*)&clnt_adr, clnt_adr_sz); } close(serv_sock); return 0; } void error_handling(char *message){ fputs(message, stderr); fputc('
    '
    ,stderr); exit(1); }
    uechoclient.cはconnect関数コールが存在しません。
    #include 
    
    #define BUF_SIZE 30
    void error_handling(char *message);
    
    int main(int argc, char *argv[]){
        int sock;
        char message[BUF_SIZE];
        int str_len;
        socklen_t adr_sz;
    
        struct sockaddr_in serv_adr, clnt_adr;
        if(argc != 3){
            printf("Usage: %s  
    "
    , argv[0]); exit(1); } sock = socket(PF_INET, SOCK_DGRAM, 0); if(sock ==-1){ error_handling("socket() error"); } memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=inet_addr(argv[1]); serv_addr.sin_port=atoi(argv[2])); while(1){ fputs("Inert message(q to quit): ",stdout); fgets(message, sizeof(message), stdin); if(!strcmp(message,"q
    "
    ) || !strcmp(message, "Q
    "
    )) break; sendto(sock, message, strlen(message), 0, (struct sockadr*)&serv_adr, sizeof(serv_adr)); adr_sz=sizeof(from_adr); str_len=recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz); message[str_len]=0; printf("Message from server: %s", message); } close(sock); return 0 } void error_handling(char *message){ fputs(message, stderr); fputc('
    '
    ,stderr); exit(1); }
    TCPはconnect関数を呼び出したときに自動的にIPアドレスとポート番号を割り当てますが、UDPはsendto関数を呼び出したときに自動的にIPとポート番号を割り当てます。