zoukankan      html  css  js  c++  java
  • 第三章-套接字编程

    套接字结构一般从内核到进程.从进程到内核,其中从内核到进程是值-结果参数的例子
    地址转换函数推荐使用inet_ntop,inet_pton适用于ipv4跟ipv6

    套接字地址结构
    struct sockaddr_in{
     uint8_t sin_len;//长度 posix规范不需要这个
    sa_family_t sin_family; //协议族,无符号短整数.一般表示AF_INET
    in_port_t  sin_port;//端口
    struct in_addr sin_addr;//32位ip地址
    char sin_zero[8];//没用
    }
    通用套接字地址结构
    struct sockaddr{
    uint8_t sa_len;
    sa_family_t sa_family;
    char sa_data[14];
    }

    值-结果参数传递
    一个套接字函数传递一个套接字结构一般是用指针.不过结构的长度其传递方式取决于是从进程到内核还是内核到进程
    (1) 从进程到内核传递函数:bind.connect,sendto.这些函数一个参数是套接字地址指针.一个是该结构整数大小
    exmple
    struct sockaddr_in serv;
    connect(sockfd,(SA*)&serv,sizeof(serv));
    (2)从内核到进程传递结构体函数4个:accept,recvform,getsockname,getpeername,一个参数为套接字指针,一个参数为int*指针
    值-结果最常见的例子:返回套接字地址结构的长度.比如select函数.getsockopt函数的参数等
    ------------------以上看到I/O缓冲有很多疑惑.查博客得知-----------------------
    stdio缓冲带来的弊端
    stdio缓冲区是没法看到的

    标准IO库提供缓冲的目的是尽可能减少使用read和write调用的次数,降低执行IO的时间,它提供三种类型的缓冲:

    1. 全缓冲。在填满标准IO缓冲区4096Bytes后(缓冲区已满)才进行实际IO操作(通过write系统调用,将数据传递到内核高速缓冲区,最终内核将数据写入磁盘),对于磁盘文件通常就是全缓冲,上面的示例就是采用缓冲。
    2. 行缓冲。在输入和输出中遇到换行符时(缓冲区已满)进行实际的IO操作(通过write系统调用,将数据传递到内核高速缓冲区,最终内核将数据写入磁盘),当涉及到一个终端时,通常使用行缓冲。使用最频繁的printf函数就是采用行缓冲,所以感觉不出缓冲的存在。
    3. 不带缓冲。标准IO库不对字符进行缓冲存储。标准出错流stderr通常是不带缓冲的。
    4.  补充一下知识点:

        read()和write()系统调用在操作磁盘文件时不会直接发起磁盘请求,而是仅仅在用户空间缓冲区与内核缓冲区高速缓存之间复制数据。例如下面调用将3个字节的数据从用户空间内存传递到内核空间的缓冲区中。

        write(fd,"abc",3);

        write()随机返回。在后续某个时刻,内核会将其缓冲区中的数据写入(刷新至)磁盘。(因此,可以说系统调用与磁盘操作并不同步)

        与此同理,对输入而言,内核从磁盘中读取数据并存储到内核缓冲区中。read()调用将从该缓冲区中读取数据,直至把缓冲区中的数据读完,这时,内核会将文件的下一段内容读入缓冲区高速缓存。

        这样设计,使得read()和write()很快,不需要等待(缓慢的)磁盘操作。同时,这一设计也极为高效,因为这减少了内核必须执行的磁盘传输次数。(预读和满写)

        两句话:

        1.read()和write()负责在用户空间缓冲区和内核高速缓冲区高速缓存复制数据。

        2.内核负责从磁盘读数据到内核高速缓冲区(预读),以及当内核高速缓冲区满了,写到磁盘中去(满写)。



      自 上而下,首先是通过stdio库将用户数据传递到stdio缓冲区(一般是4096Bytes,或者也可以有标准IO自动分配),该缓冲区位于用户态内存 区。当缓冲区满时(行缓冲遇到‘ ',全缓冲满4096Bytes),stdio库会调用write()系统调用,将数据传递到内核高速缓冲区(位于内 核态内存区)。最终,内核发起磁盘操作,将数据传递到磁盘。

      使用fflush()强制刷新stdio缓冲区(通过write()调用),将数据传递到内核高速缓冲区中。

      fsync() syn()系统调用将使缓冲数据和与打开文件描述符fd相关的所有元数据都刷新到磁盘上。

      首先要明白不带缓冲的概念:所谓不带缓冲,并不是指内核不提供缓冲,而是只单 纯的系统调用,不是函数库的调用。系统内核对磁盘的读写都会提供一个块缓冲,当用write函数对其写数据时,直接调用系统调用,将数据写入到块缓冲进行 排队,当块缓冲达到一定的量时,才会把数据写入磁盘。因此所谓的不带缓冲的I/O是指进程不提供缓冲功能。每调用一次write或read函数,直接系统 调用。
      而带缓冲的I/O是指进程对输入输出流进行了改进,提供了一个流缓冲,当用fwrite函数网磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。
      因此,带缓冲的I/O在往磁盘写入相同的数据量时,会比不带缓冲的I/O调用系统调用的次数要少。

      自 上而下,首先是通过stdio库将用户数据传递到stdio缓冲区(一般是4096Bytes,或者也可以有标准IO自动分配),该缓冲区位于用户态内存 区。当缓冲区满时(行缓冲遇到‘ ',全缓冲满4096Bytes),stdio库会调用write()系统调用,将数据传递到内核高速缓冲区(位于内 核态内存区)。最终,内核发起磁盘操作,将数据传递到磁盘。

      使用fflush()强制刷新stdio缓冲区(通过write()调用),将数据传递到内核高速缓冲区中。

      fsync() syn()系统调用将使缓冲数据和与打开文件描述符fd相关的所有元数据都刷新到磁盘上。

      首先要明白不带缓冲的概念:所谓不带缓冲,并不是指内核不提供缓冲,而是只单 纯的系统调用,不是函数库的调用。系统内核对磁盘的读写都会提供一个块缓冲,当用write函数对其写数据时,直接调用系统调用,将数据写入到块缓冲进行 排队,当块缓冲达到一定的量时,才会把数据写入磁盘。因此所谓的不带缓冲的I/O是指进程不提供缓冲功能。每调用一次write或read函数,直接系统 调用。
      而带缓冲的I/O是指进程对输入输出流进行了改进,提供了一个流缓冲,当用fwrite函数网磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。
      因此,带缓冲的I/O在往磁盘写入相同的数据量时,会比不带缓冲的I/O调用系统调用的次数要少。

    fread就是通过read来实现的,fread是C语言的库,而read是系统调用
    但是差别在read每次读的数据是调用者要求的大小,比如调 用要求读取10个字节数据,read就会读10个字节数据到数组中,而fread不一样,为了加快读的速度,fread每次都会读比要求更多的数据,然后 放到缓冲区中,这样下次再读数据只需要到缓冲区中去取就可以了。
    fread每次会读取一个缓冲区大小的数据,32位下一般是4096个字节,相当于调用了read(fd,buf,4096)
    比如需要读取512个字节数据,分4次读取,调用read就是:
    for(i=0; i<4; ++i)
    read(fd,buf,128)
    一共有4次系统调用

    而fread一次就读取了4096字节放到缓冲区了,所以省事了
    比如用fgetc读一个字节,fgetc有可能从内核中预读1024个字节到I/O缓冲区中,再返回第一个字节,这时该文件在内核中记录的读写位置是1024,而在FILE结构体中记录的读写位置是1
     
    1. #include<unistd.h>
    2. #include<stdlib.h>
    3. int main()
    4. {
    5. char buf[10];
    6. int n;
    7. n=read(STDIN_FILENO,buf,10);
    8. if(n<0){
    9. perror("read stdin_fileno");
    10. exit(0);
    11. }
    12. write(STDOUT_FILENO,buf,n);
    13. return 0;
    14. }
    在终端输入
    1. [root@localhost test]# ./a.out
    2. hello world //从键盘输入
    3. hello worl[root@localhost test]# d
    4. bash: d: command not found
    5. Shell进程创建a.out进程,a.out进程开始执行,而Shell进程睡眠等待a.out进程退出。
    6. a.out调用read时睡眠等待,直到终端设备输入了换行符才从read返回,read只读走10个字符,剩下的字符仍然保存在内核的终端设备输入缓冲区中
    7. a.out进程打印并退出,这时Shell进程恢复运行,Shell继续从终端读取用户输入的命令,于是读走了终端设备输入缓冲区中剩下的字符d和换行符,把它当成一条命令解释执行,结果发现执行不了,没有d这个命令。








  • 相关阅读:
    ural(Timus) 1019 Line Painting
    ACMICPC Live Archive 2031 Dance Dance Revolution
    poj 3321 Apple Tree
    其他OJ 树型DP 选课
    poj 3548 Restoring the digits
    ACMICPC Live Archive 3031 Cable TV Network
    递归循环获取指定节点下面的所有子节点
    手动触发asp.net页面验证控件事件
    子级Repeater获取父级Repeater绑定项的值
    没有列名的数据绑定
  • 原文地址:https://www.cnblogs.com/zengyiwen/p/5755212.html
Copyright © 2011-2022 走看看