《TCP/IP网络编程》第16章 关于I/O流分离的其他内容

92 篇文章 18 订阅
34 篇文章 3 订阅


fopen打开文件后可以与文件交换数据,fopen创建了流(数据流动,收发路径,数据收发为目的的桥梁)。

分离I/O流

两种分流方法:

  • fork复制文件描述符(输入、输出)
    分开输入过程(代码)和输出过程,降低实现难度;
    与输入无关的输出操作可以提高速度。

  • fdopen创建FILE指针(读、写模式)
    FILE指针按读写模式区分,降低实现难度;
    区分I/O缓冲提高缓冲性能。

流分离带来的EOF问题:

  • fork复制文件描述符后,可以使用shutdown(sock, SHUT_WR);半关闭。
  • FILE指针(读或写模式)调用fclose后全关闭。
16.sep_serv.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 9999
#define BUF_SIZE 1024

void error_handling(const char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

int main(int argc, char *argv[])
{
    int serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (serv_sock == -1)
        error_handling("socket() error");

    int opt = 1;
    if (setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt)) == -1)
        error_handling("setsockopt() error");

    socklen_t addr_size = sizeof(struct sockaddr_in);

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, addr_size);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(PORT);

    if (bind(serv_sock, (struct sockaddr *)&serv_addr, addr_size) == -1)
        error_handling("bind() error");

    if (listen(serv_sock, 5) == -1)
        error_handling("listen() error");

    struct sockaddr_in clnt_addr;
    int clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &addr_size);
    if (clnt_sock == -1)
        error_handling("accept() error");

    FILE *readfp = fdopen(clnt_sock, "r");
    FILE *writefp = fdopen(clnt_sock, "w");

    fputs("FROM SERVR: Hi~ client?\n", writefp);
    fputs("I love all of the world.\n", writefp);
    fputs("You are awesome!\n", writefp);
    fflush(writefp);
    fclose(writefp); // 全关闭,非半关闭

    char buf[BUF_SIZE] = {0};

    fgets(buf, BUF_SIZE - 1, readfp);
    fputs(buf, stdout);
    fclose(readfp);

    close(serv_sock);

    return 0;
}

// gcc 16.sep_serv.c -o 16.sep_serv && ./16.sep_serv
16.sep_clnt.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define IP "127.0.0.1"
#define PORT 9999
#define BUF_SIZE 1024

void error_handling(const char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

int main(int argc, char *argv[])
{
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1)
        error_handling("socket() error");

    socklen_t addr_size = sizeof(struct sockaddr_in);

    struct sockaddr_in addr;
    memset(&addr, 0, addr_size);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(IP);
    addr.sin_port = htons(PORT);

    if (connect(sock, (struct sockaddr *)&addr, addr_size) == -1)
        error_handling("connect() error");

    FILE *readfp = fdopen(sock, "r");
    FILE *writefp = fdopen(sock, "w");
    while (1)
    {
        char buf[BUF_SIZE] = {0};
        if (fgets(buf, BUF_SIZE - 1, readfp) == NULL) // 收到EOF返回NULL
            break;
        fputs(buf, stdout);
        fflush(stdout);
    }

    fputs("FROM CLIENT: Thank you!\n", writefp);
    fflush(writefp);

    fclose(readfp);
    fclose(writefp);

    close(sock);

    return 0;
}

// gcc 16.sep_clnt.c -o 16.sep_clnt && ./16.sep_clnt

文件描述符的复制和半关闭

  • 16.sep_serv.c中读模式FILE指针和写模式FILE指针都是基于同一个文件描述符创建的。针对任意一个FILE指针调用fclose函数都会关闭文件描述符,即终止套接字。

  • 销毁所有文件描述符后才能销毁套接字。

复制文件描述符

同一进程内多个文件描述符(不同整数值)可以同时访问文件。

#include <unistd.h>

//失败-1
int dup(int fildes); //随机文件描述符
int dup2(int fildes, int fildes);//指定文件描述符
16.dup.c
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int cfd1 = dup(1);
    int cfd2 = dup2(1, 7);

    printf("fd1=%d, fd2=%d\n", cfd1, cfd2);

    char str1[] = "Hi~ \n";
    write(cfd1, str1, sizeof(str1));

    char str2[] = "nice day~ \n";
    write(cfd2, str2, sizeof(str2));

    close(cfd1);
    close(cfd2);

    write(1, str1, sizeof(str1));
    close(1);

    write(1, str2, sizeof(str2)); // 所有输出文件描述符已关闭,客户端不再输出

    return 0;
}

// gcc 16.udp.c -o 16.udp && ./16.udp

复制文件描述符后分离“流”

16.sep_serv2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 9999
#define BUF_SIZE 1024

void error_handling(const char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

int main(int argc, char *argv[])
{
    int serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (serv_sock == -1)
        error_handling("socket() error!");

    int opt = 1;
    if (setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt)) == -1)
        error_handling("setsockopt() error!");

    socklen_t addr_size = sizeof(struct sockaddr_in);

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, addr_size);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(PORT);

    if (bind(serv_sock, (struct sockaddr *)&serv_addr, addr_size) == -1)
        error_handling("bind() error!");

    if (listen(serv_sock, 5) == -1)
        error_handling("listen() error!");

    struct sockaddr_in clnt_addr;
    int clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &addr_size);
    if (clnt_sock == -1)
        error_handling("accept() error!");

    FILE *readfp = fdopen(clnt_sock, "r");
    FILE *writefp = fdopen(dup(clnt_sock), "w");

    fputs("FROM SERVR: Hi~ client?\n", writefp);
    fputs("I love all of the world.\n", writefp);
    fputs("You are awesome!\n", writefp);
    fflush(writefp);

    // 调用shutdown函数,无论复制出多少文件描述符
    // 都进入半关闭状态,同时传递EOF
    shutdown(fileno(writefp), SHUT_WR);
    fclose(writefp);

    char buf[BUF_SIZE] = {0};
    fgets(buf, BUF_SIZE - 1, readfp);
    fputs(buf, stdout);
    fclose(readfp);

    close(serv_sock);

    return 0;
}

// gcc 16.sep_serv2.c -o 16.sep_serv2 && ./16.sep_serv2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值