基于TCP的不同IO版本的时间性能分析

一、关于各种IO的执行情况分析
涉及的IO类型:
基本堵塞IO版本、
下堵塞IO版本、
非堵塞IO版本、
fork版本、
线程化版本 。
服务器核心代码:
.c
#include"unp.h"voidstr_echo(int sockfd){ssize_tn;charbuf[MAXLINE];int counter = 0;again:while ( (n = read(sockfd, buf, MAXLINE)) > 0){Writen(sockfd, buf, n);//printf("counter = %d\n", ++counter);}if (n < 0 && errno == EINTR)goto again;else if (n < 0)err_sys("str_echo: read error");}
本次试验是基于TCP回射服务器分析, 通过发送100MB的数据, 然后回射接受100MB, 并且计算其运行时间, 服务器程序将会统一运行,以此来分析,在堵塞IO, 下堵塞IO, 非堵塞IO的版本, 以及堵塞IO的fork版本的运行时间和代码分析
1、基本堵塞IO
源代码如下所示:
.c
#include"unp.h"#define NUM 1048576#define LENGTH 1024voidstr_cli(FILE *fp, int sockfd){charsendline[MAXLINE], recvline[MAXLINE];struct timeval start, end;gettimeofday(&start, NULL);int n = 0 ;int total = 0;int i;for(i = 0; i < NUM; ++i){write(sockfd, sendline, LENGTH);if ((n = read(sockfd, recvline, MAXLINE)) == 0)err_quit("str_cli: server terminated prematurely");if (n >= 0) total += n;//Fputs(recvline, stdout);}gettimeofday(&end, NULL);printf("use times(s):%lf\n", (double)((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec))/1000000);printf("GET: %f\n", (double)total/(NUM * LENGTH));}
分析:
1、每次代码发送1KB的数据, 然后堵塞等待服务器回射 。

基于TCP的不同IO版本的时间性能分析

文章插图
2、的发送空间每次也只是利用了,利用不充分
3、本代码中通过接受的总字节数,与发送的总字节数作比较 。通常会计算式数据的获得的率 。是一个小于等于1的整数
运行结果:
基于TCP的不同IO版本的时间性能分析

文章插图
结果分析:本次结果,可以判断出, 通常情况下是在30-31ms之间, 如果接受幅度变动较大可能根据网络负载有很大的关系 。
2、下的堵塞IO模型
核心源代码分如下:
t01.c
#include "unp.h"#define NUM 1048576#define LENGTH 1024void str_cli(FILE *fp, int sockfd){ssize_t n, nwritten;char sendbuf[MAXLINE], recvbuf[MAXLINE];int sendtotalsize = 0;int recvtotalsize = 0;struct timeval start, end;gettimeofday(&start, NULL);fd_set rset, wset;int stdineof = 0;for(;;){FD_ZERO(&rset);FD_ZERO(&wset);if(stdineof == 0)FD_SET(sockfd, &wset);FD_SET(sockfd, &rset);select(sockfd + 1, &rset, &wset, NULL, NULL);if(FD_ISSET(sockfd, &rset)){n = read(sockfd, recvbuf, MAXLINE);if(n < 0){if(errno != EWOULDBLOCK)fprintf(stderr, "read error from socket.\n");}else if(n == 0){gettimeofday(&end, NULL);printf("use times(s):%lf\n", (double)((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec))/1000000);printf("GET: %f\n", (double)recvtotalsize/(NUM * LENGTH));return;}else{recvtotalsize += n;}}if(stdineof == 0 && FD_ISSET(sockfd, &wset)){ // writenwritten = write(sockfd, sendbuf, LENGTH);if(nwritten < 0){if(errno != EWOULDBLOCK) fprintf(stderr, "write error to socket\n");}else{sendtotalsize += nwritten;if(sendtotalsize == NUM * LENGTH){// write 100MB data finished.stdineof = 1;shutdown(sockfd, SHUT_WR);}}}}}
分析:
1、当不可读的时候, 可以持续往里面发送数据, 这样不会因为read堵塞导致进程休眠,提高了效率 。
2、在read读取数据的时候, 可以读取接受缓冲区中所有的数据 。
3、当数据发送完毕后, 调用函数, 发送FIN分节
4、read返回0, 代表对FIN分节做出了应答, 服务器关闭 。本次结束 。
运行结果如下:
基于TCP的不同IO版本的时间性能分析

文章插图
结果分析:相对于一中的基本堵塞IO模型,效率大大的提高了三倍, 因为通过使得进程因为堵塞而休眠的时间更少 。
3、fork下的堵塞IO
源代码如下:
.c
#include "unp.h"#define LENGTH 1024#define NUM 1048576void str_cli(FILE *fp, int sockfd){pid_t child;char sendline[MAXLINE],recvline[MAXLINE];struct timeval start, end;gettimeofday(&start, NULL);int n = 0 ;int total = 0;int i;if((child = fork()) == 0){// childfor(;;){if ((n = read(sockfd, recvline, MAXLINE)) == 0){err_quit("str_cli: server terminated prematurely");break;}else if(n < 0){fprintf(stderr, " read error from socket\n");}else{total += n;if(total == NUM * LENGTH){break;}}}gettimeofday(&end, NULL);printf("use times(s):%lf\n", (double)((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec))/1000000);printf("GET: %f\n", (double)total/(NUM * LENGTH));//Fputs(recvbuf, stdout);kill(getppid(), SIGTERM);// in case parents kill runningexit(0);}for(i = 0; i < NUM; ++i){write(sockfd, sendline, LENGTH);}Shutdown(sockfd, SHUT_WR);pause();}
分析如下;
1、通过子进程接受来至服务器的数据, 父进程想服务器发送数据 。
2、父进程发送数据完毕后,强制通过发送FIN分节,如果是调用close也仅仅只是引用计数减一
3、当子进程接收到了为0的时候,那么则数据传输完毕,子进程结束, 并且发送给父进程
基于TCP的不同IO版本的时间性能分析

文章插图
结果:传输100MB的数据在回射回来,明显效率明显高于1、2中的模型 。
4、下非堵塞IO
源代码如下:
.c
#include "unp.h"#define NUM 1048576#define LENGTH 1024void str_cli(FILE *fp, int sockfd){ssize_t n, nwritten;char sendbuf[MAXLINE], recvbuf[MAXLINE];int sendtotalsize = 0;int recvtotalsize = 0;struct timeval start, end;gettimeofday(&start, NULL);fd_set rset, wset;int flag = Fcntl(sockfd, F_GETFL, 0);Fcntl(sockfd, F_SETFL, flag | O_NONBLOCK);int stdineof = 0;for(;;){FD_ZERO(&rset);FD_ZERO(&wset);if(stdineof == 0)FD_SET(sockfd, &wset);FD_SET(sockfd, &rset);select(sockfd + 1, &rset, &wset, NULL, NULL);if(FD_ISSET(sockfd, &rset)){n = read(sockfd, recvbuf, MAXLINE);if(n < 1){if(errno != EWOULDBLOCK)fprintf(stderr, "read error from socket.\n");}else if(n == 0){gettimeofday(&end, NULL);printf("use times(s):%lf\n", (double)((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec))/1000000);printf("GET: %f\n", (double)recvtotalsize/(NUM * LENGTH));return;}else{recvtotalsize += n;}}if(stdineof == 0 && FD_ISSET(sockfd, &wset)){ // writenwritten = write(sockfd, sendbuf, LENGTH);if(nwritten < 0){if(errno != EWOULDBLOCK) fprintf(stderr, "write error to socket\n");}else{sendtotalsize += nwritten;if(sendtotalsize == NUM * LENGTH){// write 100MB data finished.stdineof = 1;shutdown(sockfd, SHUT_WR);}}}}}
分析如下:
1、代码与三中的代码区别唯一的区别就是,调用fcntl函数,设置IO类型
基于TCP的不同IO版本的时间性能分析

文章插图
【基于TCP的不同IO版本的时间性能分析】运行结果:从理论上的非堵塞IO的类型的效率是明显高于堵塞IO类型的, 但是实际的结果是比fork的堵塞io类型效率低, 可能是因为网络负载的问题, 也可能是本程序设计不够充分,有待提高 。(具体分析任然还有待考验,请等待试验更新)