十五、套接字和标准IO

本章主要是复习标准I/O函数收发数据的方法,例如fopen()、feof()、fgetc()、fputs(),以及将套接字文件描述符转换成FILE*结构体指针并采用标准I/O修改程序。

标准I/O函数不同于系统函数,标准I/O函数是在系统函数的基础上,进行优化。系统I/O函数不再介绍,感兴趣可以去查阅《Unix系统编程》这本书,里面讲的非常全。这里主要是介绍标准I/O函数,我们的目标是将套接字变成标准I/O使其拥有标准I/O的优点。

标准I/O函数主要有以下优点:

  1. 标准I/O函数具有良好的移植性
  2. 标准I/O函数可以利用缓冲提高性能。(重要)

在我们将套接字变成标准I/O实现之前,我们需要明白几个小的容易忽略的细节。

文件描述符不等于FILE文件指针

我们通过两个小程序看看区别,这两个程序都是将一个文件里面的内容复制到另外一个文件当中。文件描述符只能调用系统函数,FILE文件指针只能调用标准I/O函数。

//文件描述符实现, syscpy.c
#include <stdio.h>
#include <fcntl.h>
#define BUF_SIZE 3

int main(int argc, char const *argv[])
{
    int fd1,fd2;
    int len;
    char buf[BUF_SIZE];
    fd1=open("news.txt",O_RDONLY);
    fd2=open("cpy.txt",O_WRONLY|O_CREAT|O_TRUNC);
    while((len=read(fd1,buf,sizeof(buf)))>0)  write(fd2,buf,len);
    close(fd1);
    close(fd2);
    return 0;
}
//FILE指针实现, stdcpy.c
#include <stdio.h>
#define BUF_SIZE 3

int main(int argc, char const *argv[])
{
    FILE *fp1,*fp2;
    
    char buf[BUF_SIZE];
    fp1=fopen("news.txt","r");
    fp2=fopen("cpy1.txt","w");
    while(fgets(buf,BUF_SIZE,fp1)!=NULL)fputs(buf,fp2);

    fclose(fp1);
    fclose(fp2);
    return 0;
}

注意: news.txt找个数据集数据验证最好,因为只有300M以上的文件才能体会到之间的差距。

主要是FILE*文件指针是使用标准的I/O函数fopen的前提条件,标准I/O函数拥有缓冲区。而文件描述符(如程序syscpy.c)使用的系统函数是没有缓冲区的,这导致复制文件时每次都要访问内存,系统延迟非常明显。标准I/O函数优点很明显,但是并不意味着采用系统函数就一无是处,标准I/O函数还是存在以下几个缺点:

  1. 不容易进行双向通信

  2. 有时可能频繁调用fflush函数

    打开文件时,如果希望同时进行读写操作,则应该以r+、w+、a+模式打开文件,但是由于存在缓冲区,导致每次切换读写都要调用一次fflush函数

  3. 需要以FILE结构体指针的形式返回文件描述符。

    为了使用标准函数,我们需要将文件描述符转换成FILE结构体指针。

套接字创建时返回的时文件描述符

即通过调用socket()返回的是文件描述符

通过上面小程序,我们知道,如果我们在进行网络编程的过程,如果要使用标准I/O函数,我们需要将套接字文件描述符转换成FILE结构体指针。

转换函数

#include<stdio.h>
//文件描述符转FILE结构体指针
FILE *fdopen(int fildes,const char* mode);
//FILE结构体指针转文件描述符
int fileno(FILE* stream);

修改程序

接下来,我们将在第四章、基于TCP的服务器端、客户端(上)程序的基础上进行更改,将套接字描述符转换成FILE指针,并使用I/O标准函数进行实现同样的操作。

//std_server.c
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
    /**
     * @brief  先声明FILE结构体指针
     * @note   
     * @retval None
     */
    FILE *readfp;
    FILE *writefp;
  int serv_sock,clnt_sock;
  char message[BUF_SIZE];
  int str_len,i;
  struct sockaddr_in serv_adr,clnt_adr;
  socklen_t clnt_adr_sz;
  if(argc!=2)
  {
      printf("Usage:%s<port>\n",argv[0]);
      exit(0);
  }
  serv_sock=socket(PF_INET,SOCK_STREAM,0);
  if(serv_sock==-1)error_handling("socket() error");
  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(atoi(argv[1]));
  if(bind(serv_sock,(struct sockaddr *)&serv_adr,sizeof(serv_adr))==-1)error_handling("bind() error");
  if(listen(serv_sock,5)==-1)error_handling("listen() error");
  clnt_adr_sz=sizeof(clnt_adr);
  for(i=0;i<5;i++)
  {
      clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);
      if(clnt_sock==-1)error_handling("accept() error");
      else{
          printf("Connect client %d\n",i+1);
      }
      /**
       * @brief  将套接字描述符转换成FILE结构体指针
       * @note   
       * @retval 
       */
      readfp=fdopen(clnt_sock,"r");
      writefp=fdopen(clnt_sock,"w");

     // while((str_len=read(clnt_sock,message,BUF_SIZE))!=0)
     while(!feof(readfp))//换用标准I/O函数
      {
          //write(clnt_sock,message,str_len);
          fgets(message,BUF_SIZE,readfp);
          fputs(message,writefp);
      }
      //close(clnt_sock);
      fclose(readfp);
      fclose(writefp);
  }
  close(serv_sock);
  return 0;
}
void error_handling(char *message)
{
   fputs(message,stderr);
   fputc('\n',stderr);
   exit(1);
}
//std_client.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc,char *argv[])
{
    /**
     * @brief  声明FILE结构体指针
     * @note   
     * @retval None
     */
    FILE* readfp;
    FILE* writefp;

    int sock;
    char message[BUF_SIZE];
    int str_len;
    struct sockaddr_in serv_adr;
    if(argc!=3)
    {
        printf("Usage:%s<IP><port> \n",argv[0]);
        exit(1);
    }
    sock=socket(PF_INET,SOCK_STREAM,0);
    if(sock==-1)error_handling("socket() error");
    memset(&serv_adr,0,sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_adr.sin_port=htons(atoi(argv[2]));

    if(connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)
    error_handling("connect() error");
    else{
        puts("Connected.....");
    }

    /**
     * @brief  将套接字文件描述符转换成FILE结构体指针
     * @note   
     * @retval 
     */
    fdopen(sock,"r");
    fdopen(sock,"w");
    while(1)
    {
        fputs("Input message(Q to quit):",stdout);
        fgets(message,BUF_SIZE,stdin);
        if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
        {
            printf("testing......\n");
               break;
        }

       // write(sock,message,strlen(message));
       // str_len=read(sock,message,BUF_SIZE-1);
       /**
        * @brief  使用标准I/O函数进行替代write与read函数
        * @note   
        * @retval None
        */
       fputs(message,writefp);
       fflush(writefp);  //读写转换
       fgets(message,BUF_SIZE,readfp);
       message[str_len]=0;
       printf("Message from server:%s",message);
       
    }
    fclose(writefp);
    fclose(readfp);
    close(sock);
    return 0;
}
void error_handling(char *message)
{
  fputs(message,stderr);
  fputc('\n',stderr);
  exit(1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值