#define _GNU_SOURCE #include #include #include #include #include #include #include #include struct sigio { int maxfds; int signum; sigset_t ss; }; int sigio_init(struct sigio* s,int maxfds) { s->signum=SIGRTMIN+1; sigemptyset(&s->ss); sigaddset(&s->ss,s->signum); sigaddset(&s->ss,SIGIO); sigprocmask(SIG_BLOCK,&s->ss,0); s->maxfds=maxfds; return 0; } void sigio_done(struct sigio* s) { } int sigio_add_fd(struct sigio* s,int fd) { static pid_t mypid=0; if (!mypid) mypid=getpid(); fcntl(fd,F_SETOWN,mypid); fcntl(fd,F_SETSIG,s->signum); fcntl(fd,F_SETFL,fcntl(fd,F_GETFL)|O_NONBLOCK|O_ASYNC); } int sigio_rm_fd(struct sigio* s,int fd) { fcntl(fd,F_SETFL,fcntl(fd,F_GETFL)&(~O_ASYNC)); } #ifndef MAX #define MAX 10000 #endif static char request[1000]; static int sockets[MAX]; static enum { CLOSED, CONNECTING, CONNECTED } state[MAX]; static int nconnected,nclosed,nconnecting=0; static int turnover=0; void handle_io(int fd) { int r; char buf[1024]; if (state[fd]==CONNECTING) { r=read(fd,buf,1); if (r==-1 && errno==EAGAIN) { /* OK */ // printf("fd #%d is now connected.\n",fd); state[fd]=CONNECTED; ++nconnected; --nconnecting; ++turnover; } else if (r==-1) { perror("connect"); close(fd); state[fd]=CLOSED; ++nclosed; --nconnecting; ++turnover; } else if (r==0) { /* EOF. Whew, that was quick. */ // printf("fd #%d got EOF.\n",fd); close(fd); state[fd]=CLOSED; ++nclosed; --nconnecting; ++turnover; } else { /* huh? we're not supposed to read anything on HTTP */ puts("HTTP protocol error!"); exit(1); } } else if (state[fd]==CONNECTED) { // printf("huh? input on fd #%d\n",fd); r=read(fd,buf,1); if (r==-1 && errno==EAGAIN) { // printf("huh? got read signal but then EAGAIN on fd #%d\n",fd); } else if (r==-1) { perror("read"); close(fd); state[fd]=CLOSED; ++nclosed; --nconnected; ++turnover; } else if (r==0) { // printf("fd #%d got EOF.\n",fd); close(fd); state[fd]=CLOSED; ++nclosed; --nconnected; ++turnover; } else { /* huh? we're not supposed to read anything on HTTP */ puts("HTTP protocol error!"); exit(1); } } else { // printf("got signal for fd #%d, which is not active?!\n",fd); // exit(1); } } static struct sockaddr_in si; void measure() { int s; struct timeval a,b; char buf[1024]; int i,j,k; unsigned long l; switch (fork()) { case -1: perror("fork"); return; case 0: /* child */ break; default: return; } l=0; for (k=0; k<5; ++k) { gettimeofday(&a,0); if ((s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==-1) { perror("measure socket"); _exit(0); } switch (connect(s,(struct sockaddr*)&si,sizeof si)) { case -1: perror("measure connect"); close(s); _exit(0); } assert(write(s,request,strlen(request))==strlen(request)); for (j=0; i=read(s,buf,sizeof buf)>0; j+=i) ; if (i==-1) { perror("measure read"); close(s); _exit(0); } close(s); gettimeofday(&b,0); l+=(b.tv_sec-a.tv_sec)*1000000+b.tv_usec-a.tv_usec; } printf("%d %lu\n",nconnected,l/5); fflush(stdout); _exit(0); } main(int argc,char* argv[]) { struct sigio s; siginfo_t info; struct timespec timeout; int r,max; time_t last,now; int outstanding=100; si.sin_family=PF_INET; if (!inet_aton(argv[1]?argv[1]:"127.0.0.1",&si.sin_addr)) { usage: puts("usage: get 127.0.0.1 80 1000 100 /uri"); puts("where: 80 == port number (http)\n" " 1000 == number of connections\n" " 100 == number of parallel outstanding connections\n"); return 1; } si.sin_port=htons(argc>1?atoi(argv[2]):80); if (argc>=3) { max=atoi(argv[3]); if (max<1 || max>MAX) goto usage; } else max=MAX; nclosed=max; nconnected=nconnecting=0; if (argc>=4) { outstanding=atoi(argv[4]); if (outstanding<1 || outstanding>max) goto usage; } snprintf(request,sizeof request,"GET %s HTTP/1.0\r\nHost: %s:%d\r\n\r\n", argc>=5?argv[5]:"/",argv[1]?argv[1]:"127.0.0.1",ntohs(si.sin_port)); sigio_init(&s,10000); // sigio_add_fd(&s,0); for (r=0; routstanding) j=outstanding; j-=nconnecting; // printf("opening another %d connections...\n",j); for (i=0; i