TCP/UDPを実現するシンプルなEchoサーバ


シリーズブログリファレンス:http://blog.csdn.net/zy416548283/article/category/1108400コードは番号対応でGithubに置かれます.https://github.com/zy416548283/networkProgramming
タイトル
ネットワーク上のEchoサーバは,それぞれ接続向けと無接続の方式で実現される.Echoサーバは、クライアントがサーバに文字列を送信し、サーバが受信した後、クライアントに同じ文字列を返す.
タイトル解読
  • TCPとUDPの基本的なSocket APIを熟知している.
  • サーバとクライアントのプログラムフローを熟知している.
  • TCPとUDPプログラミングの違いを熟知している.
  • プロセスで発生する可能性のあるいくつかの異常状況は、どのように処理し、プログラムの丈夫性を保証するか.
  • 同時サーバと反復サーバの違い.

  • インプリメンテーション
    宣言:プログラムはUNP第5章と第8章から取られ、UNPのソースコードの取得とコンパイルについては本シリーズの文章を参照することができる.
    TCP(接続向け):
    サーバ側:
    //tcpserver
    #include "unp.h"
    
    int
    main(int argc, char **argv)
    {
        int  listenfd, connfd;
        pid_t    childpid;
        socklen_t    clilen;
        struct sockaddr_in  cliaddr, servaddr;
    
        listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family      = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port        = htons(SERV_PORT);
    
        Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    
        Listen(listenfd, LISTENQ);
    
        for ( ; ; ) {
        clilen = sizeof(cliaddr);
        connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
    
        if ( (childpid = Fork()) == 0) {    /* child process */
        Close(listenfd);    /* close listening socket */
        str_echo(connfd);   /* process the request */
        exit(0);
        }
        Close(connfd);   /* parent closes connected socket */
        }
    }

    サーバ側のプロセス:ソケットの作成–いくつかのパラメータの初期化–ポートのバインド–リスニング–接続の受け入れ–新しい接続を処理するためのサブプロセスの作成–接続を閉じることがわかります.ここで、文字列を処理する関数は次のとおりです.
    void
    str_echo(int sockfd)
    {
        ssize_t  n;
        char     buf[MAXLINE];
    
        again:
        while ( (n = read(sockfd, buf, MAXLINE)) > 0)
        Writen(sockfd, buf, n);
    
        if (n < 0 && errno == EINTR)
        goto again;
        else if (n < 0)
        err_sys("str_echo: read error");
    }

    client
    #include "unp.h"
    
    int
    main(int argc, char **argv)
    {
        int  sockfd;
        struct sockaddr_in  servaddr;
    
        if (argc != 2)
        err_quit("usage: tcpcli <IPaddress>");
    
        sockfd = Socket(AF_INET, SOCK_STREAM, 0);
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(SERV_PORT);
        Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
    
        Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
    
        str_cli(stdin, sockfd);  /* do it all */
    
        exit(0);
    }
    

    Client側の実行プロセス:ソケットの作成–パラメータの初期化–サーバへの接続–文字列の送信と受信–プログラムの終了(ソケットのクローズ)が表示されます.ここでstr_cliは次のとおりです.
    #include "unp.h"
    
    void
    str_cli(FILE *fp, int sockfd)
    {
        char    sendline[MAXLINE], recvline[MAXLINE];
    
        while (Fgets(sendline, MAXLINE, fp) != NULL) {
    
        Writen(sockfd, sendline, strlen(sendline));
    
        if (Readline(sockfd, recvline, MAXLINE) == 0)
        err_quit("str_cli: server terminated prematurely");
    
        Fputs(recvline, stdout);
        }
    }

    プログラムのコンパイル/実行
    サーバ側:
    gcc tcpserv01.c -o server01 -lunp
    ./server01

    Client:
    gcc tcpcli01.c -o client01 -lunp
    ./client01 127.0.0.1

    注意事項
  • netstatコマンドを使用してソケットの状態を表示し、通常のTCPの状態変換を理解することができる.
  • 硬直プロセス、waitとwaitpidの使用を処理する.
  • サーバプロセスが終了し、クライアントが入力状態にあり、サーバから終了メッセージをどのように受信するか、selectのソリューションを考慮する.
  • サーバホストがクラッシュしました.
  • サーバホストがクラッシュした後に再起動します.

  • UDP
    Server
    #include "unp.h"
    
    int
    main(int argc, char **argv)
    {
        int  sockfd;
        struct sockaddr_in  servaddr, cliaddr;
    
        sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family      = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port        = htons(SERV_PORT);
    
        Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
    
        dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
    }
    

    UDPサーバ側のプロセスは、ソケットの作成–初期化パラメータ–バインドポート–反復処理要求–終了(ソケットを閉じる)です.dg_echoは次のとおりです.
    #include    "unp.h"
    
    void
    dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
    {
        int  n;
        socklen_t   len;
        char     mesg[MAXLINE];
    
        for ( ; ; ) {
        len = clilen;
        n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
    
        Sendto(sockfd, mesg, n, 0, pcliaddr, len);
        }
    }
    

    ここではTCPの同時処理とは異なり,反復処理要求を採用している.TCPは長い接続を維持する可能性があり、1つの要求を出さずに閉じるため、サーバを占有する必要があるため、同時使用を採用します.ここでのUDPはショートメッセージを処理し,バッファからFIFOでメッセージを処理し,反復的に処理する.具体的にはブログを参照してください.http://blog.csdn.net/lianghe_work/article/details/46500893
    client
    #include "unp.h"
    
    int
    main(int argc, char **argv)
    {
            int  sockfd;
    
            struct sockaddr_in  servaddr;
    
    
    
            if (argc != 2)
    
             err_quit("usage: udpcli <IPaddress>");
    
    
    
            bzero(&servaddr, sizeof(servaddr));
    
            servaddr.sin_family = AF_INET;
    
            servaddr.sin_port = htons(SERV_PORT);
    
            Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
    
    
    
            sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
    
    
    
            dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
    
    
    
            exit(0);
    }

    udp client側のプロセスは、ソケットの作成–パラメータの初期化–受信データの送信–プログラムの終了(ソケットのクローズ)であり、dg_cli関数は次のとおりです.
    #include "unp.h"
    
    void
    dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
    {
        int n;
        char    sendline[MAXLINE], recvline[MAXLINE + 1];
    
        while (Fgets(sendline, MAXLINE, fp) != NULL) {
    
        Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
    
        n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
    
        recvline[n] = 0;    /* null terminate */
        Fputs(recvline, stdout);
        }
    }

    コンパイルと実行
    上記TCPのコンパイル運転方式と一致
    注意事項
  • clientはサーバより先に実行され、お客様がrecvfromにブロックされます.ここでconnectを使用するとsendtoのエラーを検出したり、recvfromにタイムアウト待ち時間を設定したりすることができます.
  • UDPは流量制御に欠けており、Client側がデータを狂って送信すると受信側が水没する.
  • は、UDPの信頼性をどのように高めるかを考慮することができる.

  • 文章が役に立つと思ったら、支付宝でポイントを打つことができます:<网络编程培训之三> 实现TCP/UDP的简单Echo服务器_第1张图片