实验 1 黑盒测试
一、实验目的
1、 掌握黑盒测试的基础知识;
2、 掌握黑盒测试的检查内容及测试目的;
3、 掌握黑盒测试的几种基本测试方法:等价类划分方法、边界值分析方法、因果图法和决策表法;
二、实验要求
1、 复习有关内容,理解黑盒测试;
2、 掌握等价类划分、边界值分析方法、因果图法和决策表法,并能设计出测试用例;
3、 对具体软件,能分别使用相应的黑盒测试方法设计测试用例,并实施测试、分析测试结果。
三、实验内容
1、设计函数实现输入日期显示星期几,并用等价类及边界值法测试
实验步骤:
① 设计程序
② 划分等价类,得到等价类表。等价类表格式如下:
输入条件 |
有效等价类 |
唯一标识 |
无效等价类 |
唯一标识 |
年 |
1900到2050内的闰年 |
(1) |
Year<1900 |
(10) |
1900到2050内的平年 |
(2) |
Year>2050 |
(11) |
|
非数字 |
(12) |
|||
月 |
1,3,5,7,8,10,12 |
(3) |
Month<1 |
(13) |
4,6,9,11 |
(4) |
Month>12 |
(14) |
|
2 |
(5) |
非数字 |
(15) |
|
日 |
1~28 |
(6) |
Day<1 |
(16) |
29 |
(7) |
Day>31 |
(17) |
|
30 |
(8) |
Year为闰年 且Month 为2时,Day>29 |
(18) |
|
31 |
(9) |
Year为平年 且Month 为2时,Day>28 |
(19) |
|
Month=1,3,5,7,8,10,12时,Day>31 |
(20) |
|||
Month=4,6,9,11 时,Day>30 |
(21) |
|||
非数字 |
(22) |
③ 运用等价类划分法设计测试用例,得到测试用例表。测试用例表格式如下:
④ 运用边界值法设计测试用例。
2、找零钱最佳组合
假设商店货品价格(R) 都不大于100元(且为整数),若顾客付款(P)在100元内,现有一个程序能在每位顾客付款后给出找零钱的最佳组合(找给顾客货币张数最少)。假定此商店的货币面值只包括:50元(N50)、10元(N10)、 5元(N5)、1元(N1) 四种。
请结合等价类划分法和边界值分析法为上述程序设计出相应的测试用例。
实验步骤:
等价类表
测试用例
边界值测试
3、有一个饮料自动售货机(处理单价为5角钱)的控制处理软件,它的软件规格说明如下:
若投入5角钱的硬币,按下“橙汁”或“啤酒”的按钮,则相应的饮料就送出来。若投入1元钱的硬币,同样也是按“橙汁”或“啤酒”的按钮,则自动售货机在送出相应饮料的同时退回5角钱的硬币。
用因果图法测试该程序,并撰写实验报告。
实验步骤:
①编写程序
②分析原因与结果
③画出因果图
④转化为决策表
⑤根据决策表设计测试用例,得到测试用例表
因果图
决策表
测试用例
4、航空服务查询问题:根据航线,仓位,飞行时间查询航空服务。
假设一个中国的航空公司规定:
① 中国去欧美的航线所有座位都有食物供应,每个座位都可以播放电影。
② 中国去非欧美的国外航线都有食物供应,只有商务仓可以播放电影。
③ 中国国内的航班的商务仓有食物供应,但是不可以播放电影
④ 中国国内航班的经济仓只有当飞行时间大于2小时时才有食物供应,但是不可以播放电影。
请用程序实现上述功能,并用决策表法设计测试用例,再执行测试,撰写实验报告。
实验步骤:
① 编写程序
② 构造决策表
③ 根据决策表设计测试用例,得到测试用例表
因果图
决策表
测试用例
四、实验思考
① 在实际的测试中,如何设计测试用例才能达到用最少的测试用例检测出最多的缺陷;
② 在进行用例设计时,如何考虑软件测试用例的充分性和减少软件测试用例的冗余性;
1.使用等价类不同的测试用例来进行测试,做到减少测试用例的冗余性;
2.进行边界测试查找程序缺陷;
3.根据过往经验及个人直觉推测出软件可能存在的缺陷,从而有针对性的设计测试用例。
五、代码
import java.util.Scanner;
public class Day {
public static int which_week(int y, int m, int d){
int w = ((d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) % 7) +1;
return w;
}
public static void main(String[] args) {
int year,month,day;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入年");
try {
year = scanner.nextInt();
} catch (Exception e){
System.out.println("输入日期无效");
return;
}
System.out.println("请输入月");
try {
month = scanner.nextInt();
} catch (Exception e){
System.out.println("输入日期无效");
return;
}
System.out.println("请输入日");
try {
day = scanner.nextInt();
} catch (Exception e){
System.out.println("输入日期无效");
return;
}
if (year < 1900 || year > 2050){
System.out.println("输入日期无效");
return;
}
if (month < 1 || month > 12){
System.out.println("输入日期无效");
return;
}
if (day < 1 || day > 31){
System.out.println("输入日期无效");
return;
}
if (year % 4 == 0 && month == 2 && day > 29){
System.out.println("输入日期无效");
return;
}
if (year % 4 != 0 && month == 2 && day > 28){
System.out.println("输入日期无效");
return;
}
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10){
if (day >31){
System.out.println("输入日期无效");
return;
}
}
if (month == 4 || month == 6 || month == 9 || month == 11){
if (day > 30) {
System.out.println("输入日期无效");
return;
}
}
int week = which_week(year,month,day);
System.out.println("星期"+week);
}
}
Day
import java.util.Scanner;
public class Back {
public static void main(String[] args) {
int N50 = 0,N10 = 0,N5 = 0,N1=0;
double price,pay,value;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入商品价格");
try {
price = scanner.nextDouble();
} catch (Exception e){
System.out.println("输入价格无效");
return;
}
System.out.println("请输入顾客支付金额");
try {
pay = scanner.nextDouble();
} catch (Exception e){
System.out.println("输入支付金额无效");
return;
}
if (price > pay){
System.out.println("无效,顾客付款小于商品价格");
return;
}
if ( price>100 || pay > 100 || pay < 0 || price < 0){
System.out.println("无效输入");
return;
}
else {
value = pay - price;
if (value / 50 >= 1){
N50 = (int) (value / 50);
value = value - 50 * N50;
}
if (value / 10 >= 1){
N10 = (int) (value / 10);
value = value - 10 * N10;
}
if (value / 5 >= 1){
N5 = (int) (value / 5);
value = value - 5 * N5;
}
N1 = (int) (value);
}
System.out.println("1元" + N1 + "张");
System.out.println("5元" + N5 + "张");
System.out.println("10元" + N10 + "张");
System.out.println("50元" + N50 + "张");
}
}
Back
import java.util.Scanner;
public class Drinks {
public static void main(String[] args) {
int button1,button2;
Scanner scanner = new Scanner(System.in);
System.out.println("请投币(一元请输入1,五角请输入2)");
try {
button1 = scanner.nextInt();
if (button1 != 1 && button1 != 2){
System.out.println("输入无效");
return;
}
} catch (Exception e){
System.out.println("输入无效");
return;
}
System.out.println("请选择商品(啤机请输入1,橙汁请输入2)");
try {
button2 = scanner.nextInt();
if (button2 != 1 && button2 != 2){
System.out.println("输入无效");
return;
}
} catch (Exception e){
System.out.println("输入无效");
return;
}
if (button1 == 2 && button2 == 1 ){
System.out.println("请取走您的啤酒");
}
else if (button1 == 2 && button2 == 2 ){
System.out.println("请取走您的橙汁");
}
else if (button1 == 1 && button2 == 1 ){
System.out.println("请取走您的啤酒,将找零五角");
}
else if (button1 == 1 && button2 == 2 ){
System.out.println("请取走您的橙汁,将找零五角");
}
}
}
Drinks
import java.util.Scanner;
public class Air {
public static void query(int b1,int b2,int b3){
if (b1 == 1){
System.out.println("享受服务:食物供应、播放电影");
return;
}
if (b1 == 2 && b2 == 1){
System.out.println("享受服务:食物供应、播放电影");
return;
}
if (b1 == 2 && b2 == 2){
System.out.println("享受服务:食物供应");
return;
}
if (b1 == 3 && b2 == 1){
System.out.println("享受服务:食物供应");
return;
}
if (b1 == 3 && b2 == 2 && b3 == 2){
System.out.println("享受服务:食物供应");
return;
}
else
System.out.println("享受服务:无");
}
public static void main(String[] args) {
int button1,button2,button3;
System.out.println("欢迎使用航空服务查询系统");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入您的航线(欧美请输入1,国外非欧美请输入2,国内请输入3)");
try {
button1 = scanner.nextInt();
if (button1 != 1 && button1 != 2 && button1 !=3){
System.out.println("输入无效");
return;
}
} catch (Exception e){
System.out.println("输入无效");
return;
}
System.out.println("请输入您的舱位(商务舱请输入1,经济舱请输入2)");
try {
button2 = scanner.nextInt();
if (button2 != 1 && button2 != 2){
System.out.println("输入无效");
return;
}
} catch (Exception e){
System.out.println("输入无效");
return;
}
System.out.println("请输入您的飞行时间(两小时以内请输入1,超过两小时请输入2)");
try {
button3 = scanner.nextInt();
if (button3 != 1 && button3 != 2){
System.out.println("输入无效");
return;
}
} catch (Exception e){
System.out.println("输入无效");
return;
}
query(button1,button2,button3);
}
}
Air
import java.util.Scanner; public class Day { public static int which_week(int y, int m, int d){ int w = ((d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) % 7) +1; return w; } public static void main(String[] args) { int year,month,day; Scanner scanner = new Scanner(System.in); System.out.println("请输入年"); try { year = scanner.nextInt(); } catch (Exception e){ System.out.println("输入日期无效"); return; } System.out.println("请输入月"); try { month = scanner.nextInt(); } catch (Exception e){ System.out.println("输入日期无效"); return; } System.out.println("请输入日"); try { day = scanner.nextInt(); } catch (Exception e){ System.out.println("输入日期无效"); return; } if (year < 1900 || year > 2050){ System.out.println("输入日期无效"); return; } if (month < 1 || month > 12){ System.out.println("输入日期无效"); return; } if (day < 1 || day > 31){ System.out.println("输入日期无效"); return; } if (year % 4 == 0 && month == 2 && day > 29){ System.out.println("输入日期无效"); return; } if (year % 4 != 0 && month == 2 && day > 28){ System.out.println("输入日期无效"); return; } if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10){ if (day >31){ System.out.println("输入日期无效"); return; } } if (month == 4 || month == 6 || month == 9 || month == 11){ if (day > 30) { System.out.println("输入日期无效"); return; } } int week = which_week(year,month,day); System.out.println("星期"+week); } }
import java.util.Scanner; public class Back { public static void main(String[] args) { int N50 = 0,N10 = 0,N5 = 0,N1=0; double price,pay,value; Scanner scanner = new Scanner(System.in); System.out.println("请输入商品价格"); try { price = scanner.nextDouble(); } catch (Exception e){ System.out.println("输入价格无效"); return; } System.out.println("请输入顾客支付金额"); try { pay = scanner.nextDouble(); } catch (Exception e){ System.out.println("输入支付金额无效"); return; } if (price > pay){ System.out.println("无效,顾客付款小于商品价格"); return; } if ( price>100 || pay > 100 || pay < 0 || price < 0){ System.out.println("无效输入"); return; } else { value = pay - price; if (value / 50 >= 1){ N50 = (int) (value / 50); value = value - 50 * N50; } if (value / 10 >= 1){ N10 = (int) (value / 10); value = value - 10 * N10; } if (value / 5 >= 1){ N5 = (int) (value / 5); value = value - 5 * N5; } N1 = (int) (value); } System.out.println("1元" + N1 + "张"); System.out.println("5元" + N5 + "张"); System.out.println("10元" + N10 + "张"); System.out.println("50元" + N50 + "张"); } }
import java.util.Scanner; public class Drinks { public static void main(String[] args) { int button1,button2; Scanner scanner = new Scanner(System.in); System.out.println("请投币(一元请输入1,五角请输入2)"); try { button1 = scanner.nextInt(); if (button1 != 1 && button1 != 2){ System.out.println("输入无效"); return; } } catch (Exception e){ System.out.println("输入无效"); return; } System.out.println("请选择商品(啤机请输入1,橙汁请输入2)"); try { button2 = scanner.nextInt(); if (button2 != 1 && button2 != 2){ System.out.println("输入无效"); return; } } catch (Exception e){ System.out.println("输入无效"); return; } if (button1 == 2 && button2 == 1 ){ System.out.println("请取走您的啤酒"); } else if (button1 == 2 && button2 == 2 ){ System.out.println("请取走您的橙汁"); } else if (button1 == 1 && button2 == 1 ){ System.out.println("请取走您的啤酒,将找零五角"); } else if (button1 == 1 && button2 == 2 ){ System.out.println("请取走您的橙汁,将找零五角"); } } }
import java.util.Scanner; public class Air { public static void query(int b1,int b2,int b3){ if (b1 == 1){ System.out.println("享受服务:食物供应、播放电影"); return; } if (b1 == 2 && b2 == 1){ System.out.println("享受服务:食物供应、播放电影"); return; } if (b1 == 2 && b2 == 2){ System.out.println("享受服务:食物供应"); return; } if (b1 == 3 && b2 == 1){ System.out.println("享受服务:食物供应"); return; } if (b1 == 3 && b2 == 2 && b3 == 2){ System.out.println("享受服务:食物供应"); return; } else System.out.println("享受服务:无"); } public static void main(String[] args) { int button1,button2,button3; System.out.println("欢迎使用航空服务查询系统"); Scanner scanner = new Scanner(System.in); System.out.println("请输入您的航线(欧美请输入1,国外非欧美请输入2,国内请输入3)"); try { button1 = scanner.nextInt(); if (button1 != 1 && button1 != 2 && button1 !=3){ System.out.println("输入无效"); return; } } catch (Exception e){ System.out.println("输入无效"); return; } System.out.println("请输入您的舱位(商务舱请输入1,经济舱请输入2)"); try { button2 = scanner.nextInt(); if (button2 != 1 && button2 != 2){ System.out.println("输入无效"); return; } } catch (Exception e){ System.out.println("输入无效"); return; } System.out.println("请输入您的飞行时间(两小时以内请输入1,超过两小时请输入2)"); try { button3 = scanner.nextInt(); if (button3 != 1 && button3 != 2){ System.out.println("输入无效"); return; } } catch (Exception e){ System.out.println("输入无效"); return; } query(button1,button2,button3); } }
原文地址:https://www.cnblogs.com/Arisf/p/16034437.html
上一篇: 整洁代码之道边界、单元测试、类
下一篇: 功能测试 - 边界测试
推荐阅读
-
算法实验室 1(全部三遍)
-
阿里云服务器速度测试,1M带宽,怎么这么慢,我太失望了
-
[数字电路与系统] [北京航空航天大学] 实验:时序逻辑设计 - 三色灯开关 (III),功能仿真测试
-
数字信号处理系列实验 1--信号采样
-
epoll简介及触发模式(accept、read、send)-epoll的简单介绍 epoll在LT和ET模式下的读写方式 一、epoll的接口非常简单,一共就三个函数:1. int epoll_create(int size);创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close关闭,否则可能导致fd被耗尽。2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epoll的事件注册函数,它不同与select是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create的返回值,第二个参数表示动作,用三个宏来表示:EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */};events可以是以下几个宏的集合:EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭); EPOLLIN事件:EPOLLIN事件则只有当对端有数据写入时才会触发,所以触发一次后需要不断读取所有数据直到读完EAGAIN为止。否则剩下的数据只有在下次对端有写入时才能一起取出来了。现在明白为什么说epoll必须要求异步socket了吧?如果同步socket,而且要求读完所有数据,那么最终就会在堵死在阻塞里。 EPOLLOUT:表示对应的文件描述符可以写; EPOLLOUT事件:EPOLLOUT事件只有在连接时触发一次,表示可写,其他时候想要触发,那要先准备好下面条件:1.某次write,写满了发送缓冲区,返回错误码为EAGAIN。2.对端读取了一些数据,又重新可写了,此时会触发EPOLLOUT。简单地说:EPOLLOUT事件只有在不可写到可写的转变时刻,才会触发一次,所以叫边缘触发,这叫法没错的!其实,如果真的想强制触发一次,也是有办法的,直接调用epoll_ctl重新设置一下event就可以了,event跟原来的设置一模一样都行(但必须包含EPOLLOUT),关键是重新设置,就会马上触发一次EPOLLOUT事件。1. 缓冲区由满变空.2.同时注册EPOLLIN | EPOLLOUT事件,也会触发一次EPOLLOUT事件这个两个也会触发EPOLLOUT事件 EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);EPOLLERR:表示对应的文件描述符发生错误;EPOLLHUP:表示对应的文件描述符被挂断;EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);等待事件的产生,类似于select调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。-------------------------------------------------------------------------------------------- 从man手册中,得到ET和LT的具体描述如下EPOLL事件有两种模型:Edge Triggered (ET)Level Triggered (LT)假如有这样一个例子:1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符2. 这个时候从管道的另一端被写入了2KB的数据3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作4. 然后我们读取了1KB的数据5. 调用epoll_wait(2)......Edge Triggered 工作模式:如果我们在第1步将RFD添加到epoll描述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait(2)之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件。因此在第5步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。在上面的例子中,会有一个事件产生在RFD句柄上,因为在第2步执行了一个写操作,然后,事件将会在第3步被销毁。因为第4步的读取操作没有读空文件输入缓冲区内的数据,因此我们在第5步调用 epoll_wait(2)完成后,是否挂起是不确定的。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口,在后面会介绍避免可能的缺陷。 i 基于非阻塞文件句柄 ii 只有当read(2)或者write(2)返回EAGAIN时才需要挂起,等待。但这并不是说每次read时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。Level Triggered 工作模式相反的,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll(2),并且无论后面的数据是否被使用,因此他们具有同样的职能。因为即使使用ET模式的epoll,在收到多个chunk的数据的时候仍然会产生多个事件。调用者可以设定EPOLLONESHOT标志,在 epoll_wait(2)收到事件后epoll会与事件关联的文件句柄从epoll描述符中禁止掉。因此当EPOLLONESHOT设定后,使用带有 EPOLL_CTL_MOD标志的epoll_ctl(2)处理文件句柄就成为调用者必须作的事情。然后详细解释ET, LT:LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认(这句话不理解)。在许多测试中我们会看到如果没有大量的idle -connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当我们遇到大量的idle- connection(例如WAN环境中存在大量的慢速连接),就会发现epoll的效率大大高于select/poll。(未测试)另外,当使用epoll的ET模型来工作时,当产生了一个EPOLLIN事件后,读数据的时候需要考虑的是当recv返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取: 这里只是说明思路(参考《UNIX网络编程》) while(rs) {buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);if(buflen < 0){// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读// 在这里就当作是该次事件已处理处.if(errno == EAGAIN)break; else return; }else if(buflen == 0) { // 这里表示对端的socket已正常关闭. } if(buflen == sizeof(buf) rs = 1; // 需要再次读取 else rs = 0; } 还有,假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由于是非阻塞的socket,那么send函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发,当缓冲区满后会产生EAGAIN错误(参考man send),同时,不理会这次请求发送的数据.所以,需要封装socket_send的函数用来处理这种情况,该函数会尽量将数据写完再返回,返回-1表示出错。在socket_send内部,当写缓冲已满(send返回-1,且errno为EAGAIN),那么会等待后再重试.这种方式并不很完美,在理论上可能会长时间的阻塞在socket_send内部,但暂没有更好的办法. ssize_t socket_send(int sockfd, const char* buffer, size_t buflen) { ssize_t tmp; size_t total = buflen; const char *p = buffer; while(1) { tmp = send(sockfd, p, total, 0); if(tmp < 0) { // 当send收到信号时,可以继续写,但这里返回-1. if(errno == EINTR) return -1; // 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满, // 在这里做延时后再重试. if(errno == EAGAIN) { usleep(1000); continue; } return -1; } if((size_t)tmp == total) return buflen; total -= tmp; p += tmp; } return tmp; } 二、epoll在LT和ET模式下的读写方式 在一个非阻塞的socket上调用read/write函数, 返回EAGAIN或者EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK) 从字面上看, 意思是: * EAGAIN: 再试一次 * EWOULDBLOCK: 如果这是一个阻塞socket, 操作将被block * perror输出: Resource temporarily unavailable 总结: 这个错误表示资源暂时不够, 可能read时, 读缓冲区没有数据, 或者, write时,写缓冲区满了 。 遇到这种情况, 如果是阻塞socket, read/write就要阻塞掉。 而如果是非阻塞socket, read/write立即返回-1, 同 时errno设置为EAGAIN. 所以, 对于阻塞socket, read/write返回-1代表网络出错了. 但对于非阻塞socket, read/write返回-1不一定网络真的出错了. 可能是Resource temporarily unavailable. 这时你应该再试, 直到Resource available. 综上, 对于non-blocking的socket, 正确的读写操作为: 读: 忽略掉errno = EAGAIN的错误, 下次继续读 写: 忽略掉errno = EAGAIN的错误, 下次继续写 对于select和epoll的LT模式, 这种读写方式是没有问题的. 但对于epoll的ET模式, 这种方式还有漏洞. epoll的两种模式 LT 和 ET
-
振动测试规范比较 - 解振动力学方程第 1 部分
-
黑盒测试--边界值分析
-
黑盒测试方法--边界值分析
-
软件测试说明:黑盒测试 - 边界测试
-
软件测试 3(边界值)--稳健性测试-1 基本概念