增加I/O多路複用的回射程序

上一版本的回射程序中,若服務器子進程被殺死,則客戶端檢測不到這一事件的發生。原因在於,子進程被殺死時,雖然發送了FIN給客戶端套接字,但此時客戶端進程是阻塞於等待標準輸入上的,因此檢測不到套接字的輸入。解決辦法就是使用I/O多路複用。如下圖所示:



另一個問題:若輸入是批量輸入,則輸入結束後,客戶端檢測到EOF,則客戶端會關閉連接,而網絡中還有其他請求和應答,則會發現輸出比輸入少的現象,如下圖所示


當全部的9個請求發出後,則會關閉連接,而此時只有應答1被接收到,其他的會丟失。

解決辦法就是請求全部發送後,只關閉套接字寫的那端,不關閉讀的那端,也即半關閉狀態

爲此,使用了select來進行I/O多路複用。程序如下:

客戶端:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/time.h>

#define err_exit(m)\
        {\
                perror(m);\
                exit(EXIT_FAILURE);\
        }

#define SERV_PORT 9877

#define BUFSIZE 4096

int max(int a, int b)
{
        if (a > b)
                return a;
        else
                return b;
}

//客戶端具體操作函數
void str_cli(FILE *fp, int sockfd);

int main(int argc, char **argv)
{
        if (argc != 2)
        {
                printf("argument error\n");
                exit(0);
        }
        int sockfd;
        struct sockaddr_in servaddr;
        int status;

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(SERV_PORT);
        inet_pton(AF_INET, argv[1], &servaddr.sin_addr);//將點分十進制IP地址轉化爲網絡字節序的二進制地址

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd == -1)
                err_exit("socket");
        status = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//連接服務器
        if (status == -1)
                err_exit("connect");
        str_cli(stdin, sockfd);
        exit(0);
}

void str_cli(FILE *fp, int sockfd)
{
        int stdineof = 0;
        int maxfdp1;
        fd_set set;
        int n;
        char buf[BUFSIZE];

        FD_ZERO(&set);
        for (;;)
        {
                if (stdineof == 0)
                        FD_SET(fileno(fp), &set);//若stdineof = 1, 代表輸入已完成,不再監聽該描述符
                FD_SET(sockfd, &set);
                maxfdp1 = max(fileno(fp), sockfd) + 1;
                select(maxfdp1, &set, NULL, NULL, NULL);//使用select的I/O多路複用

                if (FD_ISSET(sockfd, &set))//套接字可讀
                {
                        if ((n = read(sockfd, buf, BUFSIZE)) == 0)
                        {
                                if (stdineof == 1)
                                        return ; //正常退出
                                else
                                {
                                        printf("server terminated prematurely");
                                        exit(1);
                                }
                        }
                        write(fileno(stdout), buf, n);
                }
                if (FD_ISSET(fileno(fp), &set))//標準輸入可讀
                {
                        if ((n = read(fileno(fp), buf, BUFSIZE)) == 0) //爲0,則表示輸入結束
                        {
                                stdineof = 1;
                                shutdown(sockfd, SHUT_WR);
                                FD_CLR(fileno(fp), &set);
                                continue;
                        }
                        write(sockfd, buf, n);
                }
        }

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章