标准的I / O流和文件描述符相互关联(文件描述符与文件指针间的转换)


表头文件:#include<stdio.h>
定义函数:FILE * fdopen(int fildes,const char * mode);
说明:fdopen()会将参数fildes 的文件描述词,转换为对应的文件指针后返回。 fdopen取一个现存的文件描述符,并使一个标准的I / O流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数获得的描述符。因为这些特殊类型的文件不能用标准I/O fopen函数打开,首先必须先调用设备专用函数以获得一个文件描述符,然后用fdopen使一个标准I/O流与该描述符相结合。   fdopen取一个现存的文件描述符(我们可能从 open,dup,dup2,fcntl,pipe,socket,socketpair或accept函数得到此文件描述符) ,并使一个标准的I/O流与该描述符相结合。则代表着文件指针的流形态,此形态必须和原先文件描述词读写模式相同。

一句话:就是根据Linux下的标准的输入流,输出流得到返回文件操作中的一个文件描述符。这样实现了可以通过文件操作中的函数调用来操作输入输出设备。

返回值:转换成功时返回指向该流的文件指针。失败则返回NULL,并把错误代码存在errno中。

范例:#include<stdio.h>
main()
{
FILE * fp =fdopen(1,”w+”);
fprintf(fp,”%s\n”,”hello!”);
fclose(fp);
}

范例2:
#include <sys\stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

int main(void)
{
   int handle;
   FILE *stream;

   /* open a file */
   handle = open("DUMMY.FIL", O_CREAT,
    S_IREAD | S_IWRITE);

   /* now turn the handle into a stream */
   stream = fdopen(handle, "w");

   if (stream == NULL)
      printf("fdopen failed\n");
   else
   {
      fprintf(stream, "Hello world\n");
      fclose(stream);
   }
   return 0;
}


文件描述符

在linux系统中,设备也是以文件的形式存在,要对该设备进行操作就必须先打开这个文件,打开文件就会获得文件描述符,它是个很小的正整数。每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。文件描述符的优点:兼容POSIX标准,许多Linux和UNIX系统调用都依赖于它。文件描述符的缺点:不能移植到UNIX以外的系统上去,也不直观。

文件指针

C语言中使用的是文件指针而不是文件描述符做为I/O的句柄。文件指针指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区和一个文件描述符。而文件描述符是文件描述符表的一个索引,因此从某种意义上说文件指针就是句柄的句柄(在Windows系统上,文件描述符被称作文件句柄)。FILE *中除了包含了fd信息,还包含了IO缓冲,是C标准形式,所以FILE *比fd更适合跨平台,应该多用fopen在,少用open。

C语言文件指针与文件描述符之间可以相互转换

这要通过fdopen和fileno两个函数实现。它们都包含在头文件stdio.h中。

扫描二维码关注公众号,回复: 3189117 查看本文章

fdopen的原型: FILE * fdopen(int filedes, const char *opentype);
       第一个参数filedes是一个打开的文件描述符,opentype是表示打开方式的字符串,和fopen函数具有相同的取值,比如"w"或"w+"等。但是你必须保证该字符串的描述和文件实际的打开方式是匹配的。函数fopen()就是返回打开文件的指针;如果操作失败,返回空指针null。
     把文件流指针转换成文件描述符用fileno函数,其原型为:
     int fileno(FILE *stream);
     它返回和stream文件流对应的文件描述符。如果失败,返回-1。
     当程序执行时,就已经有三个文件流打开了,它们分别是标准输入stdin,标准输出stdout和标准错误输出stderr。和流式文件相对应的是,也有三个文件描述符被预先打开,它们分别是0,1,2,代表标准输入、标准输出和标准错误输出。

应用实例

很多互联网上的协议例如HTTP、SIP、SMTP、FTP的控制连接协议都是基于文本行的。所谓基于文本行,指的是信息以文本传递,一个信息单元传递完毕后要传送换行。比如对于HTTP的GET请求来说,GET /index.html HTTP/1.0
是一行,接下去每个头部信息各占一行。一个空行表示整个请求结束。而tcp是基于流的,使用read/recv和write/send一次读入或写入的字节可能比要求的少,并且信息单元也不是按换行分割的。事实上流是不可分割的。对方调用send一次传送200K,也许接收方第一次调用recv只能收到 50K,后续还要调用多次recv才能收完。并且如果需要在TCP上应用HTTP这样的协议,需要自己检测换行。将数据接收下来以后,换行可能在任何地方出现而不只是在数据的末尾出现。

如果不苛求十全十美,有一个简单方法可以解决这个问题,那就是将socket与c标准库里的标准输入输出连接起来。

具体的说,就是使用下面这个调用。
FILE *fdopen(int fildes, const char *mode);
假设sockfd是用socket()建立的一个socket描述符。调用
fpin=fdopen(sockfd,"r");
fpout=fdopen(dup(sockfd),"w");
可以建立两个FILE指针fpin和fpout。
调用
setlinebuf(fpin);
setlinebuf(fpout);
可以将缓冲模式设置成行缓冲。

现在在这两个FILE指针上就可以调用fgets、fputs等函数了。在遇到换行符号时,fputs会将缓冲区内的数据实际发送到网络上,而对于 fgets,会一直累计接收数据并放在缓冲区中,直到遇到换行才返回。当然,需要给fgets指定一个行长度的上限,以免缓冲区溢出。与gets、 puts不同,fgets、fputs会保留换行符。另外,调用fflush可以强制将缓冲区的数据写到网络上。

在这样的基础上去实现基于行的文本协议就非常方便了



参考文献:http://m.blog.csdn.net/blog/xhu_eternalcc/37723423


猜你喜欢

转载自blog.csdn.net/chunlovenan/article/details/49742535