涉及的IO类型:
基本堵塞IO版本、
select下堵塞IO版本、
非堵塞IO版本、
fork版本、
线程化版本。
服务器核心代码:
dg_echo.c
#include "unp.h"void
str_echo(int sockfd)
{ssize_t n;char buf[MAXLINE];int counter = 0;
again:while ( (n = read(sockfd, buf, MAXLINE)) > 0){Writen(sockfd, buf, n);//printf("counter = %dn", ++counter);}if (n < 0 && errno == EINTR)goto again;else if (n < 0)err_sys("str_echo: read error");
}
本次试验是基于TCP回射服务器分析, 通过发送100MB的数据, 然后回射接受100MB, 并且计算其运行时间, 服务器程序将会统一运行,以此来分析,在堵塞IO, select下堵塞IO, 非堵塞IO的select版本, 以及堵塞IO的fork版本的运行时间和代码分析
源代码如下所示:
dg_cli01.c
#include "unp.h"#define NUM 1048576
#define LENGTH 1024
void
str_cli(FILE *fp, int sockfd)
{char sendline[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):%lfn", (double)((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec))/1000000);printf("GET: %fn", (double)total/(NUM * LENGTH));
}
分析:
1、每次代码发送1KB的数据, 然后堵塞等待服务器回射。
2、socket的发送空间每次也只是利用了1024kb,利用不充分
3、本代码中通过接受的总字节数,与发送的总字节数作比较。通常会计算式数据的获得的率。是一个小于等于1的整数
运行结果:
结果分析:本次结果,可以判断出, 通常情况下是在30-31ms之间, 如果接受幅度变动较大可能根据网络负载有很大的关系。
核心源代码分如下:
dg_cli_blockio_select01.c
#include "unp.h"
#define NUM 1048576
#define LENGTH 1024
void 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):%lfn", (double)((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec))/1000000);printf("GET: %fn", (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 socketn");}else{sendtotalsize += nwritten;if(sendtotalsize == NUM * LENGTH){ // write 100MB data finished.stdineof = 1;shutdown(sockfd, SHUT_WR);}}}}
}
分析:
1、当socket不可读的时候, 可以持续往socket里面发送数据, 这样不会因为read堵塞导致进程休眠,提高了效率。
2、在read读取数据的时候, 可以读取socket接受缓冲区中所有的数据。
3、当数据发送完毕后, 调用shutdown函数, 发送FIN分节
4、read返回0, 代表对FIN分节做出了应答, 服务器关闭。本次结束。
运行结果如下:
结果分析:相对于一中的基本堵塞IO模型,效率大大的提高了三倍, 因为通过select使得进程因为堵塞而休眠的时间更少。
源代码如下:
dg_cli_fork01.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 socketn");}else{total += n;if(total == NUM * LENGTH){break;}}}gettimeofday(&end, NULL);printf("use times(s):%lfn", (double)((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec))/1000000);printf("GET: %fn", (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、父进程发送数据完毕后,强制通过shutdown发送FIN分节,如果是调用close也仅仅只是引用计数减一
3、当子进程接收到了为0的时候,那么则数据传输完毕,子进程结束, 并且发送SIGTERM给父进程
结果:传输100MB的数据在回射回来,明显效率明显高于1、2中的模型。
#include "unp.h"
#define NUM 1048576
#define LENGTH 1024
void 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):%lfn", (double)((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec))/1000000);printf("GET: %fn", (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 socketn");}else{sendtotalsize += nwritten;if(sendtotalsize == NUM * LENGTH){ // write 100MB data finished.stdineof = 1;shutdown(sockfd, SHUT_WR);}}}}
}
分析如下:
1、代码与三中的代码区别唯一的区别就是,调用fcntl函数,设置IO类型
运行结果:从理论上的非堵塞IO的select类型的效率是明显高于堵塞IO类型的, 但是实际的结果是比fork的堵塞io类型效率低, 可能是因为网络负载的问题, 也可能是本程序设计不够充分,有待提高。(具体分析任然还有待考验,请等待试验更新)
本文发布于:2024-02-02 09:48:58,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170683854142993.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |