TCP/IP网络编程 第十五章:套接字和标准I/O

这篇具有很好参考价值的文章主要介绍了TCP/IP网络编程 第十五章:套接字和标准I/O。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

标准I/O函数的优点

标准I/O函数的两个优点


将标准I/O函数用于数据通信并非难事。但仅掌握函数使用方法并没有太大意义,至少应该
了解这些函数具有的优点。下面列出的是标准I/O函数的两大优点:
□标准I/O函数具有良好的移植性(Portability)
□标准I/O函数可以利用缓冲提高性能。
关于移植性无需过多解释。不仅是IO函数,所有标准函数具有良好的移植性。因为,为了支持所有操作系统(编译器),这些函数都是按照ANSI C标准定义的。当然,这并不局限于网络编程,而是适用于所有编程领域。
接下来讨论标准I/O函数的第二个优点。使用标准IO函数时会得到额外的缓冲支持。这种表达方式也许会带来一些混乱,因为之前讲过,创建套接字时操作系统会准备I/O缓冲。造成更大混乱之前,先说明这两种缓冲之间的关系。创建套接字时,操作系统将生成用于I/O的缓冲。此缓冲在执行TCP协议时发挥着非常重要的作用。此时若使用标准IO函数,将得到额外的另一缓冲的支持。大概意思如下图所示:

TCP/IP网络编程 第十五章:套接字和标准I/O,TCP/IP网络编程,网络,tcp/ip,服务器,网络协议,运维,linux

 从图中可以看到,使用标准I/O函数传输数据时,经过两个缓冲。例如,通过fputs函数传输字符串“Hello”时,首先将数据传递到标准IO函数的缓冲。然后数据将移动到套接字输出缓冲,最后将字符串发送到对方主机。
既然知道了两个缓冲的关系,接下来再说明各自的用途。设置缓冲的主要目的是为了提高性能,但套接字中的缓冲主要是为了实现TCP协议而设立的。例如,TCP传输中丢失数据时将再次传递
,而再次发送数据则意味着在某地保存了数据。存在什么地方呢?套接字的输出缓冲。与之
相反,使用标准IO函数缓冲的主要目的是为提高性能,但是实际上,缓冲并非在所有情况下都能带来卓越的性能。但需要传输的数据越多,有无缓冲带来的性能差异越大。可以通过如下两种角度说明性能的提高。
□传输的数据量
□数据向输出缓冲移动的次数
比较1个字节的数据发送10次(10个数据包)的情况和累计10个字节发送1次的情况。发送数据时使用的数据包中含有头信息(首部)。头信息与数据大小无关,是按照一定的格式填入的。即使假设该头信息占用40个字节(实际更大),需要传递的数据量也存在较大差别。
□1个字节10次40×10=400字节
□10个字节1次40x1=40字节
另外,为了发送数据,向套接字输出缓冲移动数据也会消耗不少时间。但这同样与移动次数
有关。1个字节数据共移动10次花费的时间将近10个字节数据移动1次花费时间的10倍。

标准I/O函数的几个缺点


如果就此结束说明,各位可能认为标准I/O函数只有优点。其实它同样有缺点,整理如下。
□不容易进行双向通信。
□有时可能频繁调用fflush函数。(用以刷新缓冲区)
□需要以FILE结构体指针的形式返回文件描述符。
假设各位已掌握了C语言中的绝大部分文件IO相关知识。打开文件时,如果希望同时进行读写操作,则应以r+、w+、a+模式打开。但因为缓冲的缘故,每次切换读写工作状态时应调用fflush函数。这也会影响基于缓冲的性能提高。而且,为了使用标准IO函数,需要FILE结构体指针(以下简称“FILE指针”)。而创建套接字时默认返回文件描述符,因此需要将文件描述符转化为FILE指针。

使用标准I/O函数


如前所述,创建套接字时返回文件描述符,而为了使用标准IO函数,只能将其转换为FILE
结构体指针。先介绍其转换方法。

利用fdopen函数转换为FILE结构体指针


可以通过fdopen函数将创建套接字时返回的文件描述符转换为标准IO函数中使用的FILE结
构体指针。

#include<stdio.h>
FILE * fdopen(int fildes, const char * mode);
//成功时返回转换的FILE结构体指针,失败时返回NULL。
     fildes     //需要转换的文件描述符。
     mode       //将要创建的FILE结构体指针的模式(mode)信息。

上述函数的第二个参数与fopen函数中的打开模式相同。常用的参数有读模式"r"和写模式"w"。

利用fileno函数转化为文件描述符

接下来介绍与fdopen函数提供功能相反的函数,该函数在有些情况下非常好用。

#include<stdio.h>
int fileno(FILE * stream);//成功时返回转化后的文件描述符,失败时返回-1

此函数的用法也非常简单,向该函数传递FILE指针参数时返回相应文件描述符。

基于套接字的标准I/O函数使用  

前面介绍了标准IO函数的优缺点,同时介绍了文件描述符转换为FILE指针的方法。下面将其适用于套接字。虽然是套接字操作,但并没有需要另外说明的内容,只需简单应用这些函数。接下来将之前的回声服务器端和客户端改为基于标准IO函数的数据交换形式。
无论是服务器端还是客户喘,更改方式并无差异。只需调用fdopen函数并使用标准IO函数,
相信各位也能自行更改。首先给出更改后的服务器端代码。

#include<"头文件声明和之前章节中的回声服务端相同,故省略">
#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc,char *argv[]){
    int serv_sock,clnt_sock;
    char message[BUF_SIZE];
    int str_len,i;

    struct sockaddr_in serv_addr;
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_sz;
    FILE * readfp;
    FILE * writefp;
    if(argc!=2){
         printf("Usage : %s <port>\n",argv[0]);
         exit(1);
    }

    serv_sock=socket(PF_INET,SOCK_STREAM,0);
    if(serv_sock==-1)error_handling("socket() error");

    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_addr.sin_port=htons(atoi(argv[1]));

    if(bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
        error_handling("bind() error");

    if(listen(serv_sock,5)==-1)error_handling("listen() error");

    clnt_addr_sz=sizeof(clnt_sock);
    for(int i=0;i<5;++i){
        clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_sock,&clnt_addr_sz);
        if(clnt_sock==-1)error_handling("accept() error");
        else printf("Connected client %d \n",i+1);

        readfp=fdopen(clnt_sock,"r");
        writefp=fdopen(clnt_sock,"w");
        while(!feof(readfp)){
              fgets(message,BUF_SIZE,readfp);   
              fputs(message,writefp);
              fflush(writefp);
        }
        fclose(readfp);
        fclose(writefp);
    }
    close(serv_sock);
    return 0;
}

void error_handling(char*message){
    //与之前章节的错误处理函数相同,故省略
}

接下来给出回声客户端代码

#include<"与之前章节的头文件声明相同,故省略">
#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc,char *argv[]){
    int sock;
    char message[BUF_SIZE];
    int str_len;
    struct sockaddr_int serv_addr;
    FILE * readfp;
    FILE * writefp;
    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_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_addr.sin_port=htons(atoi(argv[2]));

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

    readfp=fdopen(sock,"r");
    writefp=fdopen(sock,"w");
    while(1){
         fputs("Input message(Q to quite):",stdout);
         fgets(message,BUF_SIZE,stdin);
         if(!strcmp(message,"q\n")||!strcmp(message,"Q\n"))break;

         fputs(message,writefp);
         fflush(writefp);
         fgets(message,BUF_SIZE,readfp);
         printf("Message from server: %s",message);
    }
    fclose(writefp);
    fclose(readfp);
    return 0;
}

void error_handling(char *message){
    //和前面章节的错误处理函数相同,故省略。
}

以上就是标准IO函数在套接字编程中的应用方法,因为需要编写额外的代码,所以并不像想象中那么常用。但某些情况下也是非常有用的,而且可以再次复习标准IO函数,对大家也非常有益。文章来源地址https://www.toymoban.com/news/detail-585705.html

到了这里,关于TCP/IP网络编程 第十五章:套接字和标准I/O的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • TCP/IP 网络编程 第七章:优雅地断开套接字连接

    在前面的章节中,我们都是通过close或者closesocket来断开套接字连接的,但是调用这两个函数导致我们套接字完全断开,套接字将无法接受数据,并且也只能传输完最后余留在缓冲区的数据内容。此时\\\"只关闭一部分数据交换中使用的流\\\"的方法应运而生。 针对优雅断开的shutd

    2024年02月17日
    浏览(36)
  • 《TCP IP网络编程》第十八章

    线程背景:         第 10 章介绍了多进程服务端的实现方法。多进程模型与 select 和 epoll 相比的确有自身的优点,但同时也有问题。如前所述, 创建(复制)进程的工作本身会给操作系统带来相当沉重的负担。而且,每个进程都具有独立的内存空间,所以进程间通信的实

    2024年02月12日
    浏览(38)
  • 《TCP IP网络编程》第十六章

              「分离 I/O 流」是一种常用表达。有 I/O 工具可区分二者,无论采用哪种方法,都可以认为是分离了 I/O 流。 2次 I/O 流分离: 第一种是第 10 章的「TCP I/O 过程」分离。通 过调用 fork 函数复制出一个文件描述符,以区分输入和输出中使用的文件描述符。虽然文件描

    2024年02月13日
    浏览(32)
  • 《TCP IP网络编程》第十三章

    Linux 中的 send recv:          send 函数定义:         recv 函数的定义:         send 和 recv 函数的最后一个参数是收发数据的可选项,该选项可以用位或(bit OR)运算符(| 运算符)同时传递多个信息。send recv 函数的可选项意义: MSG_OOB:发送紧急消息 :     

    2024年02月15日
    浏览(28)
  • 《TCP IP网络编程》第十七章

            select 复用方法由来已久,因此,利用该技术后,无论如何优化程序性能也无法同时介入上百个客户端。这种 select 方式并不适合以 web 服务器端开发为主流的现代开发环境 ,所以需要学习 Linux 环境下的 epoll。 基于 select 的 I/O 复用技术速度慢的原因:        

    2024年02月12日
    浏览(30)
  • 【Java EE初阶十五】网络编程TCP/IP协议(二)

            tcp的socket api和U大片的socket api差异很大,但是和前面所讲的文件操作很密切的联系         下面主要讲解两个关键的类:         1、ServerSocket:给服务器使用的类,使用这个类来绑定端口号         2、Socket:即会给服务器使用,又会给客户端使用;         

    2024年02月20日
    浏览(39)
  • TCP/IP网络编程 第十七章:优于select的epoll

    select复用方法其实由来已久,因此,利用该技术后,无论如何优化程序性能也无法同时接入上百个客户端(当然,硬件性能不同,差别也很大)。这种select方式并不适合以Web服务器端开发为主流的现代开发环境,所以要学习Linux平台下的epoll。 基于select的I/O复用技术速度慢的原

    2024年02月16日
    浏览(27)
  • TCP/IP网络编程 第十九章:Windows平台下线程的使用

    要想掌握Windows平台下的线程,应首先理解“内核对象”(Kernel Objects)的概念。如果仅介绍Windows平台下的线程使用技巧,则可以省略相对陌生的内核对象相关内容。但这并不能使各位深入理解Windows平台下的线程。 内核对象的定义 操作系统创建的资源有很多种,如进程、线程

    2024年02月16日
    浏览(44)
  • TCP/IP网络编程 第十六章:关于IO流分离的其他内容

    两次I/O流分离 我们之前通过2种方法分离过IO流,第一种是第十章的“TCPI/O过程(Routine)分离”。这种方法通过调用fork函数复制出1个文件描述符,以区分输入和输出中使用的文件描述符。虽然文件描述符本身不会根据输入和输出进行区分,但我们分开了2个文件描述符的用途

    2024年02月16日
    浏览(29)
  • 【网络编程】网络编程套接字(三)TCP网络程序

    与前边的UDP网络程序相同,创建套接字的接口都是socket,下边对socket接口进行介绍: 协议家族选择AF_INET,因为我们要进行网络通信。 而第二个参数,为服务类型,传入SOCK_STREAM,我们编写TCP程序,所以要选择流式的服务。 第三个参数默认传入0,由前两个参数就可以推出这是

    2024年02月16日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包