栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

网络编程——分离I/O流

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

网络编程——分离I/O流

参考
    《TCP/IP网络编程》 尹圣雨
分离I/O流 两种I/O流分离

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

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

分离流的好处

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

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

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

    为了将FILE指针按读模式和写模式区分可以通过区分读写模式降低实现难度通过区分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 

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 
#include 
#include 
#include 
#include 
#include 

#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;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/779816.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号