非阻塞connect:Web客户程序

非阻塞connect:Web客户程序

复制代码
//web.h头文件
#include "unp.h"
#define MAXFILES   20
#define SERV          "80" /* port number or service name */
struct file
{
  char * f_name; /* filename */
  char * f_host; /* hostname or IPv4/IPv6 address */
  int f_fd; /* descriptor */
  int f_flags; /* F_xxx below */
} file[MAXFILES];

#define F_CONNECTIONG  1 /* connect() in progress */
#define F_READING            2 /* connect() complete; no reading */
#define F_DONE                  4 /* all done */
#define GET_CMD               "GET %s HTTP/1.0 \r\n\r\n"

/* globals */
int nconn, nfiles, nlefttoconn, nlefttoread, maxfd;
fd_set rset, wset;

/* function prototypes */
void home_page(const char *, const char *);
void start_connect(struct file *);
void write_get_cmd(struct file *);
复制代码

main函数

复制代码
#include "web.h"
int main(int argc, char * * argv)
{
  int i, fd, n, maxnconn, flags, error;
  char buf[MAXLINE];
  fd_set rs, ws;
  if(argc < 5)
    err_quit("usage: web < # conns> < hostname> < homepage > < file1 > ...");
  maxnconn = atoi(argv[1]);
  nfiles = min(argc-4, MAXFILES); //用从命令行参数来的相关信息填写file结构
  for( i = 0; i < nfiles; i++)
  {
    file[i].f_name = argv[i+4];
    file[i].f_host = argv[2];
    file[i].f_flags = 0;
  }
  printf("nfiles = %d \n", nfiles);
  home_page(argv[2], argv[3]);//home_page函数创建一个TCP连接,向服务器发出一个命令,然后读主页,这是第一个连接,在开始并行建立多条连接之前自己
  FD_ZERO(&rset);//初始化读描述字集
  FD_ZERO(&wset);//初始化写描述字集
  maxfd = -1;//maxfd是select使用的最大描述字,初始化成-1,因为描述字是非负的
  nlefttoread = nlefttoconn = nfiles; //nlefttoread是剩余要读的文件数(当它到0时程序就结束), nlefttoconn是还需要一个TCP连接的文件数
  nconn = 0; //当前打开的连接数(它不能超过第一个命令行参数)
  
  while(nlefttoread > 0)
  {
    while(nconn < maxnconn && nlefttoconn > 0)
    { /* find a file to read */
      for( i = 0; i < nfiles; i++ )
        if(file[i].f_flags == 0)
          break;
      if(i == nfiles)
        err_quit("nlefttoconn = %d but nothing found", nlefttoconn);
      start_connect(&file[i]);
      nconn++;
      nlefttoconn--;
    }
    rs = rset;
    ws = wset;
    n = Select(maxfd+1, &rs, &ws, NULL, NULL);
    for(i = 0; i < nfiles; i++)
    {
      flags = file[i].f_flags;
      if(flags == 0 || flags & F_DONE)
        continue;
      fd = file[i].f_fd;
      if(flags & F_CONNECTING && (FD_ISSET(fd, &rs) || FD_ISSET(fd, &ws)))
      {
        n = sizeof(error);
        if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n) < 0 || error != 0)
        {
          err_ret("nonblocking connect failed for %s", file[i].f_name);
        }
        /* connection established */
        printf("connection established for %s \n", file[i].f_name);
        FD_CLR(fd, &wset); /* no more writeability test */
        write_get_cmd(&file[i]); /* write() the GET command */
      }
      else if(flags & F_READING && FD_ISSET(fd, &rs))
              {
                if((n = Read(fd, buf, sizeof(buf))) == 0)
                {
                  printf("end-of-file on %s \n", file[i].f_name);
                  Close(fd);
                  file[i].f_flags = F_DONE; /* clears F_READING */
                  FD_CLR(fd, &rset);
                  nconn--;
                  nlefttoread--;
                }
                else
                {
                  printf("read %d bytes from %s \n", n, file[i].f_name);
                }
              }
    }
  }
  exit(0);
}
复制代码

下面是在main函数开始时调用了一次的home_page函数

复制代码
#include "web.h"
void home_page(const char * host, const char * fname)
{
  int fd, n;
  char line[MAXLINE];
  fd = Tcp_connect(host, SERV); /* blocking connect() */
  n = snprintf(line, sizeof(line), GET_CMD, fname); /* 发出一个HTTP GET命令以获取主页 */
  Writen(fd, line, n);
  for( ; ; )
  {
    if((n = Read(fd, line, MAXLINE)) == 0)
      break; /* server closed connection */
    printf("read %d bytes of home page \n", n);
    /* do whatever with data */
  }
  printf("end-of-file on home page \n");
  Close(fd);
}
复制代码

创建套接口,设置非阻塞方式

复制代码
#include "web.h"
void start_connect(struct file * fptr)
{
  int fd, flags, n;
  struct addrinfo * ai;
  ai = Host_serv(fptr->f_host, SERV, 0, SOCK_STREAM); /* 调用host_serv函数查找和转换主机名和服务名,返回一个指向addrinfo结构的数组的指针 */
  fd = Socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); /* 创建TCP套接口 */
  fptr->f_fd = fd;
  printf("start_connect for %s, fd %d \n", fptr->f_name, fd);
  /* Sect socket nonblocking */
  flags = Fcntl(fd, F_GETFL, 0); 
  Fcntl(fd, F_SETFL, flags | O_NONBLOCK); //设置为非阻塞
  /* initiate nonblocking connect to the server */
  if((n = connect(fd, ai->ai_addr, ai->ai_addrlen)) < 0)
  {/* 启动非阻塞connect,并将相应文件的标志置为F_CONNECTING */
    if(errno != EINPROGRESS)
      err_sys("nonblocking connect error");
    fptr->f_flags = F_CONNECTING;
    FD_SET(fd, &rset); /* select for reading and writing */
    FD_SET(fd, &wset); /* 在读集合和写集合中都打开这个套接口描述字对应的位 */
    if(fd > maxfd)
      maxfd = fd;
  }/* 如果connect返回成功,那么连接已经完成,write_get_cmd函数向服务器发出一个命令 */
  else if(n >= 0) /* connect is already done */
            write_get_cmd(fptr); /* write() the GET command */
}
复制代码

我们把connect所用的套接口设置为非阻塞,但永远不恢复它缺省的阻塞模式。这样做没有问题是因为只向该套接口写了少量的数据,从而可以认为这个命令大大小于套接口的发送缓冲区。即使write因为非阻塞标志造成返回的数目小于要写的数目,writen函数也会对此进行处理。让这个套接口非阻塞对后面的read没有什么影响,因此我们总是调用select等待它变为可读。

复制代码
//函数 write_get_cmd, 它向服务器发出一个HTTP的GET命令
#include "web.h"
void write_get_cmd(struct file * fptr)
{
  int n;
  char line[MAXLINE];
  n = snprintf(line, sizeof(line), GET_CMD, fptr->f_name);
  Writen(fptr->f_fd, line, n);
  printf("wrote %d bytes for %s \n", n, fptr->f_name);
  fptr->f_flags = F_READING; /* clears F_CONNECTING */
  FD_SET(fptr->f_fd, &rset); /* will read server's reply */
  if(fptr->f_fd > maxfd)
    maxfd = fptr->f_fd;
}
复制代码

参考:《unix网络编程》卷一
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值