- 《TCP/IP网络编程》 尹圣雨
第一种是TCP I/O过程分离。这种方法通过调用fork函数复制出1个文件描述符,以区分输入和输出中使用的文件描述符。虽然文件描述符本身不会根据输入和输出进行区分,但分开了2个文件描述符的用途,因此也属于流的分离。
第二种是使用标准I/O函数时,通过2次fdopen函数的调用,创建读模式FILE指针和写模式FILE指针,分离了输入工具和输出工具,也可以视为流的分离。
分离流的好处第一种分离流的方式的好处:
- 通过分开输入过程(代码)和输出过程降低实现难度与输入无关的输出操作可以提高速度
第二种分离流的方式的好处:
- 为了将FILE指针按读模式和写模式区分可以通过区分读写模式降低实现难度通过区分I/O缓冲提高缓冲性能
第一种分离流的方式没有问题,但是第二种分离流的方式有问题。
虽然表面上看可以针对输出模式的FILE指针调用fclose函数,向对方传递EOF,编程可以接收数据但无法发送数据的半关闭状态,但实际上,fclose(writefp)完全终止了套接字,而不是半关闭。
终止流时无法半关闭的原因:读模式FILE指针和写模式FILE指针都是基于同一文件描述符创建的。因此,针对任意一个FILE指针调用fclose函数时都会关闭文件描述符,也就终止套接字。
文件描述符的复制和半关闭为了实现可以输入但无法输出的半关闭状态,创建FILE指针前先复制文件描述符即可。然后,利用各自的文件描述符生成读模式FILE指针和写模式FILE指针。因为,销毁所有文件描述符后才能销毁套接字。即针对写模式FILE指针调用fclose函数,只能销毁与该FILE指针相关的文件描述符,无法销毁套接字。
但实际上,剩余的与读模式FILE指针相关的文件描述符还是可以同时进行I/O,因此,仅仅如此还不能实现半关闭。还需要使用shutdown函数。
复制文件描述符与fork函数不同,调用fork函数将复制整个进程,因此同一进程内不能同时有原件和副本。如果在同一进程内完成文件描述符的复制,需要dup或dup2函数。
#includeint 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; }



