码迷,mamicode.com
首页 > 其他好文 > 详细

第三章-套接字编程

时间:2016-08-10 06:26:49      阅读:229      评论:0      收藏:0      [点我收藏+]

标签:

套接字结构一般从内核到进程.从进程到内核,其中从内核到进程是值-结果参数的例子
地址转换函数推荐使用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自动分配),该缓冲区位于用户态内存 区。当缓冲区满时(行缓冲遇到‘\n‘,全缓冲满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自动分配),该缓冲区位于用户态内存 区。当缓冲区满时(行缓冲遇到‘\n‘,全缓冲满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这个命令。








第三章-套接字编程

标签:

原文地址:http://www.cnblogs.com/zengyiwen/p/5755212.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!