IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 网络编程——分离I/O流 -> 正文阅读

[网络协议]网络编程——分离I/O流

参考

  1. 《TCP/IP网络编程》 尹圣雨

分离I/O流

两种I/O流分离

第一种是TCP I/O过程分离。这种方法通过调用fork函数复制出1个文件描述符,以区分输入和输出中使用的文件描述符。虽然文件描述符本身不会根据输入和输出进行区分,但分开了2个文件描述符的用途,因此也属于流的分离。

第二种是使用标准I/O函数时,通过2次fdopen函数的调用,创建读模式FILE指针和写模式FILE指针,分离了输入工具和输出工具,也可以视为流的分离。

分离流的好处

第一种分离流的方式的好处:

  1. 通过分开输入过程(代码)和输出过程降低实现难度
  2. 与输入无关的输出操作可以提高速度

第二种分离流的方式的好处:

  1. 为了将FILE指针按读模式和写模式区分
  2. 可以通过区分读写模式降低实现难度
  3. 通过区分I/O缓冲提高缓冲性能

分离流带来的EOF问题

第一种分离流的方式没有问题,但是第二种分离流的方式有问题。

虽然表面上看可以针对输出模式的FILE指针调用fclose函数,向对方传递EOF,编程可以接收数据但无法发送数据的半关闭状态,但实际上,fclose(writefp)完全终止了套接字,而不是半关闭。

终止流时无法半关闭的原因:读模式FILE指针和写模式FILE指针都是基于同一文件描述符创建的。因此,针对任意一个FILE指针调用fclose函数时都会关闭文件描述符,也就终止套接字。

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

为了实现可以输入但无法输出的半关闭状态,创建FILE指针前先复制文件描述符即可。然后,利用各自的文件描述符生成读模式FILE指针和写模式FILE指针。因为,销毁所有文件描述符后才能销毁套接字。即针对写模式FILE指针调用fclose函数,只能销毁与该FILE指针相关的文件描述符,无法销毁套接字。

但实际上,剩余的与读模式FILE指针相关的文件描述符还是可以同时进行I/O,因此,仅仅如此还不能实现半关闭。还需要使用shutdown函数。

复制文件描述符

与fork函数不同,调用fork函数将复制整个进程,因此同一进程内不能同时有原件和副本。如果在同一进程内完成文件描述符的复制,需要dup或dup2函数。

#include <unistd.h>

int dup(int fildes);
int dup2(int fildes, int fildes2);

成功时返回复制的文件描述符,失败时返回-1。其中,fildes为需要复制的文件描述符,fildes为明确指定的文件描述符整数值。dup2函数明确指定复制的文件描述符整数值,向其传递大于0且小于进程能生成的最大文件描述符值时,该值将成为复制出的文件描述符值。

复制文件描述符后进行流的分类

我们的目标是,通过服务器端的半关闭状态接收客户端最后发送的字符串。为了完成这样任务,服务器端需要同时发送EOF。

需要注意的是,无论复制出了多少文件描述符,均应调用shutdown函数发送EOF并进入半关闭状态,因此代码中shutdown(fileno(writefp), SHUT_WR);实现了服务器的半关闭状态,并向客户端发送EOF

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

#define BUF_SIZE 1024

int main(int argc, char* argv[])
{
    int serv_sock, clnt_sock;
    FILE* readfp;
    FILE* writefp;

    struct sockaddr_in serv_adr, clnt_adr;
    socklen_t clnt_adr_sz;
    char buf[BUF_SIZE] = {0,};
    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    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(atol(argv[1]));

    bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
    listen(serv_sock, 5);
    clnt_adr_sz = sizeof(clnt_adr);
    clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);

    readfp = fdopen(clnt_sock, "r");
    writefp = fdopen(dup(clnt_sock), "w");                  // 利用dup函数的返回值生成FILE指针

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

    shutdown(fileno(writefp), SHUT_WR);                     // 针对fileno函数返回的文件描述符调用shutdown函数
    fclose(writefp);

    fgets(buf, sizeof(buf), readfp);
    fputs(buf, stdout);
    fclose(readfp);
    return 0;
}
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 19:06:09  更:2022-03-30 19:10:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:54:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码