在线网络速度测试JAVA程序(一):思路和控制台主程序【转】
来源:http://hancool.blog.51cto.com/1836252/1352228
事情的缘由
因上级公司的信息化主管部门经常被投诉说是各种业务应用系统反映系统使用慢的问题,而都把问题归结于网速不给力。而业务系统一部份是总公司一级部署的,其它很多都是各个业务部门各自为政建设的,不知道从哪里找来的各种小公司,搞的五法八门的技术路线,没得统一标准、技术平台,所以水平就参差不齐。人少的时候还好点,业务和使用人一多,系统慢得象蜗牛。而网络基础设施这几年来改造、提升得差不多了,都是百兆桌面、千兆骨干了。地市级公司内部虽然办公点分散,但都是使用电力专用光纤建设的千兆骨干的MPLSVPN技术,地市公司与网省公司是千兆或万兆专用光纤。因此其实网络基础设施还是绰绰有余。为了自证清白,上级主管部门就盟生了建一个分层分布式的网速测试系统的想法。你说网速慢,那你在客户端上打开网页,测试一下各级网速,就可能看看到底是网速慢,还是应用系统本身太垃圾。
思路
在网络上给出的参考方法,WEB版的大部份是客户端用JAVASCRIPT下载一个大文件,然后根据速度=总下载量/时间,得到网速;专业的开源测试工具是iperf,是c版本的,和业余的相比,专业的不只一点点。于是,了解了一下iperf的基本思路和方法,于是采用JAVA来写,利用socket直接实现通信,服务端和客户端配合,通过TCP通信的数据量和使用时间来计算网络速度,一方面效率高、减少其它因素(如HTTP协议、JAVASCRIPT加载与执行效率等)的影响;另一方面,利用applet可以实现B/S应用,客户端部署使用简单。程序代码参考了OSCHINA的JGroups项目的JPerf.java。项目名称为jperf,使用eclipse开发和测试,目前实现了基本功能(因目前只是进行验证,因此未进行完整的异常处理、验证等)。
1 import java.io.*; 2 import java.net.InetAddress; 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 import java.net.UnknownHostException; 6 import java.text.NumberFormat; 7 /*** 8 * Tool to measure TCP throughput, similar to iperf 9 * @author Bela Ban 10 * @version $Id: JPerf.java,v 1.4 2007/12/28 23:29:17 belaban Exp $ 11 */ 12 public class jperf implements Runnable{ 13 boolean client; 14 boolean direction;//test direction:true-up test,false-down 15 int num; 16 InetAddress local_addr; 17 int local_port; 18 int remote_port; 19 InetAddress remote_addr; 20 int size; 21 int receivebuf=200000; 22 int sendbuf=200000; 23 static NumberFormat f; 24 Socket client_sock; 25 26 static { 27 f=NumberFormat.getNumberInstance(); 28 f.setGroupingUsed(false); 29 f.setMaximumFractionDigits(2); 30 } 31 32 public jperf(){ 33 } 34 35 //实现Thread类的接口Runnable, 用于支持服务端连接的多线程 36 @Override 37 public void run() { 38 // TODO Auto-generated method stub 39 try { 40 server_accept_data(); 41 } catch (IOException e) { 42 // TODO Auto-generated catch block 43 e.printStackTrace(); 44 } catch (ClassNotFoundException e) { 45 // TODO Auto-generated catch block 46 e.printStackTrace(); 47 } 48 49 } 50 //用于applet和GUI调用 51 public jperf(String remote_addr,int remote_port,int num,int size,boolean direction) throws UnknownHostException{ 52 53 this.remote_addr=InetAddress.getByName(remote_addr); 54 this.remote_port=remote_port; 55 this.num=num; 56 this.size=size; 57 this.direction=direction; 58 } 59 60 private void start(boolean client, boolean direction,int num, int size, String local_addr, int local_port, 61 String remote_addr, int remote_port,int receivebuf, int sendbuf) throws IOException, ClassNotFoundException { 62 this.client=client; 63 this.direction=direction; 64 this.num=num; 65 this.size=size; 66 this.local_addr=InetAddress.getByName(local_addr); 67 this.local_port=local_port; 68 this.remote_addr=InetAddress.getByName(remote_addr); 69 this.remote_port=remote_port; 70 this.receivebuf=receivebuf; 71 this.sendbuf=sendbuf; 72 73 if(client) { 74 client(); 75 } 76 else { 77 server(); 78 } 79 } 80 //客户端调用 81 public String client() throws IOException{ 82 83 System.out.println("-- creating socket to " + this.remote_addr + ":" + this.remote_port); 84 client_sock=new Socket(this.remote_addr, remote_port); 85 86 String result=""; 87 if(sendExchangeData()==true){ 88 if(direction==true) result=sendData(num,size); 89 else result=receiveData(num,size); 90 } 91 else{ 92 result="connect to server and exchange data fail!"; 93 } 94 System.out.println(result); 95 client_sock.close(); 96 97 return result; 98 } 99 //客户端向服务端发送测试 的数据参数 100 private boolean sendExchangeData() throws IOException{ 101 boolean ret=true; 102 103 client_sock.setReceiveBufferSize(receivebuf); 104 client_sock.setSendBufferSize(sendbuf); 105 ObjectOutputStream write=new ObjectOutputStream(new BufferedOutputStream(client_sock.getOutputStream())); 106 write.writeObject(num); 107 write.flush(); 108 write.writeObject(size); 109 write.flush(); 110 write.writeObject(direction); 111 write.flush(); 112 113 return ret; 114 } 115 //服务端调用 116 private void server() throws IOException, ClassNotFoundException 117 { 118 ServerSocket srv_sock=new ServerSocket(local_port, 10, this.local_addr); 119 System.out.println("-- waiting for client on " + srv_sock.getLocalSocketAddress()); 120 while(true){ 121 //wait for a client connect 122 Socket client_sock=srv_sock.accept(); 123 //start a new thread deal this connection: 124 jperf thread_client=new jperf(); 125 thread_client.client_sock=client_sock; 126 thread_client.sendbuf=sendbuf; 127 thread_client.receivebuf=receivebuf; 128 //每一个客户端单独一个线程,支持多个客户端同时连接 129 Thread thread=new Thread(thread_client); 130 thread.start(); 131 } 132 133 } 134 //服务器接收和发送测试 数据 135 private void server_accept_data() throws IOException, ClassNotFoundException{ 136 client_sock.setReceiveBufferSize(receivebuf); 137 client_sock.setSendBufferSize(sendbuf); 138 System.out.println("-- accepted data connection from " + client_sock.getRemoteSocketAddress()); 139 ObjectInputStream in=new ObjectInputStream(new BufferedInputStream(client_sock.getInputStream())); 140 141 int num=0,size=0; 142 boolean direction=false; 143 num=(Integer)in.readObject(); 144 size=(Integer)in.readObject(); 145 direction=(Boolean)in.readObject(); 146 147 if(num>0 && size>0) { 148 String result; 149 if(direction==true) result=receiveData(num,size); 150 else result=sendData(num,size); 151 152 System.out.println(result); 153 } 154 else{ 155 System.out.println("-- invalid exchange data! "); 156 } 157 client_sock.close(); 158 } 159 //发送数据,并计算测试结果 160 private String sendData(int num,int size) throws IOException 161 { 162 System.out.println("-- sending data to "+client_sock.getRemoteSocketAddress().toString()+ " total "+num + " messages"); 163 DataOutputStream out=new DataOutputStream(new BufferedOutputStream(client_sock.getOutputStream())); 164 byte[] buf=new byte[size]; 165 for(int i=0;i<buf.length;i++) buf[i]=(byte)(i%128); 166 long start=0, stop; 167 int cnt=1; 168 int incr=num/10; 169 start=System.currentTimeMillis(); 170 for(int i=0; i < num; i++) { 171 out.write(buf, 0, buf.length); 172 out.flush(); 173 if(cnt % incr == 0) 174 System.out.println("-- sent " + cnt + " messages"); 175 cnt++; 176 } 177 stop=System.currentTimeMillis(); 178 long diff=stop-start; 179 String result=report("发送报文至 "+client_sock.getRemoteSocketAddress().toString(),(long)num*(long)size,diff); 180 181 return result; 182 } 183 184 //接收数据,并计算测试结果 185 private String receiveData(int num,int size) throws IOException{ 186 System.out.println("-- accepted data from " + client_sock.getRemoteSocketAddress().toString()+" total "+num+" messages"); 187 DataInputStream in=new DataInputStream(new BufferedInputStream(client_sock.getInputStream())); 188 byte[] buf=new byte[size]; 189 long counter=0; 190 int incr=num/10; 191 long start=0, stop; 192 while(true) { 193 int len=in.read(buf, 0, buf.length); 194 if(len<=0) break; 195 if(start == 0) 196 start=System.currentTimeMillis(); 197 counter+=len; 198 if((counter/size) % incr == 0) 199 System.out.println("-- received " + counter/size); 200 } 201 stop=System.currentTimeMillis(); 202 long diff=stop-start; 203 String result=report("接收报文来自 "+client_sock.getRemoteSocketAddress().toString(),counter,diff); 204 205 return result; 206 } 207 //计算测试结果 208 private String report(String direction,long totalbyte, double diff) 209 { 210 StringBuilder sb=new StringBuilder(); 211 double tbs=totalbyte/(1024*1024); 212 if(tbs<1000) sb.append("\n"+direction+",测试数据总数" + f.format(tbs) + "Mbyte"+" ,用时 " + diff + "毫秒 "); 213 else{ 214 tbs=tbs/1024; 215 sb.append("\n"+direction+",测试数据总数" + f.format(tbs)+ "Gbyte"+" ,用时 " + diff + "毫秒 "); 216 } 217 //tcp throughput: 218 double throughput=totalbyte / (diff / 1000.0) / 1024.0; 219 if(throughput < 1000) 220 sb.append("\n网络吞吐量: " + f.format(throughput) + "KB/秒"); 221 else { 222 throughput/=1024.0; 223 sb.append("\n网络吞吐量: " + f.format(throughput) + "MB/秒"); 224 } 225 //bandwidth 226 double bandwidth=totalbyte / (diff / 1000.0) / 1024.0*8; 227 if(bandwidth < 1000){ 228 sb.append("\n网络带宽: " + f.format(bandwidth) + "Kb/秒"); 229 } 230 else { 231 bandwidth/=1024.0; 232 if(bandwidth>1000){ 233 bandwidth/=1024; 234 sb.append("\n网络带宽: " + f.format(bandwidth) + "Gb/秒"); 235 } 236 else sb.append("\n网络带宽: " + f.format(bandwidth) + "Mb/秒"); 237 } 238 return sb.toString(); 239 } 240 static void help() { 241 System.out.println("JPerf [-help] [-client] [-direction <up|down>] [-num <number of msgs] [-size <bytes>] [-local_addr <interface>] [-local_port <port]" + 242 "[-remote_addr <IP addr>] [-remote_port <port>] [-receivebuf <bytes>] [-sendbuf <bytes>]"); 243 } 244 //主程序执行入口 245 public static void main(String[] args) throws UnknownHostException { 246 boolean client=false; 247 boolean direction=false;//test direction:true-up test,false-down test 248 int num=10000; 249 int size=8192; 250 String local_addr=InetAddress.getLocalHost().getHostAddress(); 251 String remote_addr=local_addr; 252 int local_port=5000; 253 int remote_port=5000; 254 int receivebuf=200000, sendbuf=200000; 255 for(int i=0; i < args.length; i++) { 256 if(args[i].equals("-client")) { 257 client=true; 258 continue; 259 } 260 if(args[i].equals("-num")) { 261 num=Integer.parseInt(args[++i]); 262 continue; 263 } 264 if(args[i].equals("-size")) { 265 size=Integer.parseInt(args[++i]); 266 continue; 267 } 268 if(args[i].equals("-local_addr")) { 269 local_addr=args[++i]; 270 continue; 271 } 272 if(args[i].equals("-remote_addr")) { 273 remote_addr=args[++i]; 274 continue; 275 } 276 if(args[i].equals("-local_port")) { 277 local_port=Integer.parseInt(args[++i]); 278 continue; 279 } 280 if(args[i].equals("-remote_port")) { 281 remote_port=Integer.parseInt(args[++i]); 282 continue; 283 } 284 if(args[i].equals("-receivebuf")) { 285 receivebuf=Integer.parseInt(args[++i]); 286 continue; 287 } 288 if(args[i].equals("-sendbuf")) { 289 sendbuf=Integer.parseInt(args[++i]); 290 continue; 291 } 292 if(args[i].equals("-direction")) { 293 String value=args[++i]; 294 if(value.toLowerCase().equals("up")) direction=true; 295 else direction=false; 296 297 continue; 298 } 299 help(); 300 return; 301 } 302 try { 303 new jperf().start(client, direction,num, size, local_addr, local_port, remote_addr, remote_port, receivebuf, sendbuf); 304 } 305 catch(IOException e) { 306 e.printStackTrace(); 307 } catch (ClassNotFoundException e) { 308 e.printStackTrace(); 309 } 310 } 311 }
编译代码:javac jperf.java
在服务端执行:java jperf,启动后默认在5000端口上进行监听。
在客户端执行:java jperf -client -remote_addr x.x.x.x,默认参数为数据报文数10000,数据包大小8192(针对默认的WINDOWS系统的TCP窗口),端口5000,方向为测试下行。
Java jperf -client -remote_addr x.x.x.x -direction up测试上行。
经过测试,两台通过百兆口交换机连接的计算机,吞吐量为11.3MB/s,带宽为90.4Mb/s左右。
本文出自 “一个业余程序员的历程” 博客,请务必保留此出处http://hancool.blog.51cto.com/1836252/1352228