net api

来源:互联网 发布:网络兼职执业药师 编辑:程序博客网 时间:2024/05/17 06:35
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如
  connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等
  待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。
  可是使用Select就可以完成非阻塞(所谓非阻塞方式non-

  block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int
main(void) {
     fd_set rfds;
     struct timeval tv;
     int retval;

     /* Watch stdin (fd 0) to see when it has input. */
     FD_ZERO(&rfds);//// 如果不初始化,会导致不可预期的后果
     FD_SET(0, &rfds);//打开描叙符第0位

     /* Wait up to five seconds. */
     tv.tv_sec = 5;
     tv.tv_usec = 0;
//  原型
//    int select (int maxfdp1,fd_set *readset,fd_set *writeset,   fd_set *exceptset,const struct timeval * timeout);
//  参数
//   参数一:为了保持与早期的Berkeley套接字应用程序兼容,一般忽略它,置为0.    参数二:用于检查可读性,   参数三:用于检查可写性,   参数四:用于检查带外数据,   参数五:一个指向timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待。timeval结构的定义:struct timeval{   long tv_sec; // seconds   long tv_usec; // microseconds   }

     retval = select(1, &rfds, NULL, NULL, &tv);
     /* Don鈥檛 rely on the value of tv now! */

     if (retval == -1)
         perror("select()");
      else if (retval)
         printf("Data is available now.\n");
   /* FD_ISSET(0, &rfds) will be true. */
     else
         printf("No data within five seconds.\n");

     return 0;
 }

int select (int maxfdp1,fd_set *readset,fd_set *writeset,

  fd_set *exceptset,const struct timeval * timeout);

fd_set*readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

  fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

  fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。

 

第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符

在有了select后可以写出像样的网络程序来!举个简单的例子,就是从网络上接受数据写入一个文件中。

  例子:

  main()

  {

  int sock; int fd;

  fd_set fds;

  struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0

  char buffer[256]={0}; //256字节的接收缓冲区

  /* 假定已经建立UDP连接,具体过程不写,简单,当然TCP也同理,主机ip和port都已经给定,要写的文件已经打开

  sock=socket(...);

  bind(...);

  fp=fopen(...); */

  while⑴

  {

  FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化

  FD_SET(sock,&fds); //添加描述符

  FD_SET(fp,&fds); //同上

  maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1

  switch(select(maxfdp,&fds,&fds,NULL,&timeout)) //select使用

  {

  case -1: exit(-1);break; //select错误,退出程序

  case 0:break; //再次轮询

  default:

  if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据

  {

  recvfrom(sock,buffer,256,.....);//接受网络数据

  if(FD_ISSET(fp,&fds)) //测试文件是否可写

  fwrite(fp,buffer...);//写入文件

  buffer清空;

  }// end if break;

  }// end switch

  }//end while

  }//end main

select()函数与Linux驱动程序的关系

  当用户调用select系统调用时,select系统调用会先调用poll_initwait(&table);,然后调用驱动程序中 struct file_operations下的fop->poll函数,在这个函数里应该调用poll_wait(),将current加到某个等待队列(这里调用poll_wait()),并检查是否有效,如果无效就调用schedule_timeout();去睡眠。事件发生后,schedule_timeout()回来,,调用fop->poll();,检查到可以运行,就调用poll_freewait(&table);从而完成select系统调用。重要的是fop->poll()里面要检查是否就绪,如果是,要返回相应标志。

原创粉丝点击