Chinaunix首页 | 论坛 | 博客
  • 博客访问: 334379
  • 博文数量: 214
  • 博客积分: 4258
  • 博客等级: 上校
  • 技术积分: 2021
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-02 09:16
个人简介

http://blog.csdn.net/ly21st http://ly21st.blog.chinaunix.net

文章分类

全部博文(214)

文章存档

2018年(16)

2015年(1)

2014年(2)

2012年(22)

2011年(173)

分类: LINUX

2011-10-10 21:24:01

15.5 非阻塞connectWeb客户程序
// 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_CONNECTING 1 /* connect() in progress */
#define F_READING  2 /* connect() complete; now 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 *);
******************************************* //home_page.c #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);
 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);
}
**********************************************
//start_connect.c #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);
 fd = Socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 fptr->f_fd = fd;
 printf("start_connect for %s, fd %d\n", fptr->f_name, fd);
  /* 4Set socket nonblocking */
 flags = Fcntl(fd, F_GETFL, 0);
 Fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  /* 4Initiate nonblocking connect to the server. */
 if ( (n = connect(fd, ai->ai_addr, ai->ai_addrlen)) < 0) {
  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;
 } else if (n >= 0)    /* connect is already done */
  write_get_cmd(fptr); /* write() the GET command */
}
*********************************************
//write_get_cmd.c #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;
}
*******************************************
//web.c /* include web1 */
#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> ...");
 maxnconn = atoi(argv[1]);
 nfiles = min(argc - 4, MAXFILES);
 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]);
 FD_ZERO(&rset);
 FD_ZERO(&wset);
 maxfd = -1;
 nlefttoread = nlefttoconn = nfiles;
 nconn = 0;
/* end web1 */
/* include web2 */
 while (nlefttoread > 0) {
  while (nconn < maxnconn && nlefttoconn > 0) {
    /* 4find 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);
    }
     /* 4connection 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);
}
/* end web2 */
 
 

    这个例子中红色部分由两点没有优化(以避免使它变得更复杂)。首先,处理完select返回的已就绪的描述字后可以终止for循环。其次,可以尽可能地减少maxfd的值,省下select检查那些不再设置的描述字的时间。因为这段代码处理的描述字的数目在任何时候都小于10,而不是成千上万,所以这些优化与附加的复杂性相比是否值得,令人怀疑。

 

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

15.6 非阻塞accept

#include  "unp.h"

 

int

main(int argc, char **argv)

{

   int            sockfd;

   struct linger     ling;

   struct sockaddr_in  servaddr;

 

   if (argc != 2)

     err_quit("usage: tcpcli ");

 

   sockfd = Socket(AF_INET, SOCK_STREAM, 0);

 

   bzero(&servaddr, sizeof(servaddr));

   servaddr.sin_family = AF_INET;

   servaddr.sin_port = htons(SERV_PORT);

   Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

 

   Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

 

   ling.l_onoff = 1;   /* cause RST to be sent on close() */

   ling.l_linger = 0;

   Setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));

   Close(sockfd);

 

   exit(0);

}

       程序部分:

ling.l_onoff = 1;   /* cause RST to be sent on close() */

   ling.l_linger = 0;

   Setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));

       一旦连接建立,我们设置SO_LINGER选项,把l_onoff标志置为1l_linger时间置为0,这样会导致在关闭连接时在TCP套接口上发送一个RST。我们随后关闭该套接口。

   假如服务器的部分代码为:

   if (FD_ISSET(listenfd, &rset))  {

  +     printf(“listening  socket  readable\n”);

  +     sleep(5)

        client = sizeof(cliaddr);

        connfd = Accept(listenfd, (SA * ) &cliaddr , &clien);

       这里模拟的是一个繁忙的服务器,它不能做到在select一返回套接口可读时就调用accept。服务器的这种延迟一般情况下不会有问题(实际上这是为什么维护一个已完成连接队列的原因),但是如果在连接建立后又有从客户来的RST,就会出现问题。

       服务器会一直阻塞在accept调用上,直到其他某个客户建立一个连接为止。但是在这期间,假定这个服务器由于某种原因仍会阻塞在accept调用上,不处理任何其他已就绪的描述字。

       解决这个问题的办法是:

1        如果用select来获知何时有连接已就绪可以accept时,总是把监听套接口置为非阻塞,并且

2        在后面的accept调用中忽略以下错误:EWOULDBLOCK(源自Berkeley的实现在客户放弃连接时出现的错误)。ECONNABORTED(Posix.1g的实现在客户放弃连接时出现的错误)EPROTOSVR4的实现在客户放弃连接时出现的错误)和EINTR(如果信号被捕获)。

 


阅读(362) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~