通过InetSocketAddress InetAddress 确定目标主机,Selector选择器 SelectionKey可选择通道到选择器的注册 SocketChannel对面向流的连接套接字的可选择通道 。从开始连接计时,到连接完成停止,如果连接成功返回连接所消耗的时间,否则返回timeout,若果连接过程中出现错误,怎返回该错误。
package com.xie.test;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Label;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.*;
import java.nio.channels.*;
import java.util.*;
import java.util.regex.*;
public class Ping extends Frame{
Label label1;
TextField tf0;
TextArea tf1;
Button btnSend,btnClear;
String to;
//默认连接端口
static int DAYTIME_PORT = 80;
// 实际应用的连接端口
static int port = DAYTIME_PORT;
Ping(){
setTitle("ping的java实现");
setLayout(null);
setBounds(400, 400, 320, 240);
label1=new Label("请输入ping的地址:");
label1.setBounds(0, 40, 100, 20);
tf0=new TextField("www.baidu.com");
to=tf0.getText();
tf0.setBounds(100, 40, 120, 20);
tf1=new TextArea(5, 1);
tf1.setBounds(0, 60, 320, 160);
btnSend=new Button("ping");
btnSend.setBounds(280, 220, 40, 20);
btnSend.addActionListener(new PingSend());
btnClear=new Button("清空");
btnClear.addActionListener(new ClearPing());
btnClear.setBounds(250, 40, 40, 20);
add(label1);
add(tf0);
add(btnSend);
add(btnClear);
add(tf1);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
setResizable(false);
setVisible(true);
}
/**
* 准备要ping的目标主机
* @author centre
*/
class Target {
InetSocketAddress address;
SocketChannel channel;
String host;
Exception failure;
long connectStart;
long connectFinish = 0;
boolean shown = false;
Target(String host) {
this.host=host;
String m="^[w]{3}//.//w*//..*";
String m1="(^http://)([w]{3}//.//w*//..*)";
String m2="//d{1,3}//.//d{1,3}//.//d{1,3}//.//d{1,3}";
Pattern p=Pattern.compile(m);
Pattern p1=Pattern.compile(m1);
Pattern p2=Pattern.compile(m2);
Matcher m01=p.matcher(host);
Matcher m02=p1.matcher(host);
Matcher m03=p2.matcher(host);
try {
if (m01.find()) {
//System.out.println("ping-> "+host);
address = new InetSocketAddress(InetAddress.getByName(host),port);
}else if (m02.find()) {
//System.out.println("ping-> "+host);
address = new InetSocketAddress(InetAddress.getByName(m02.group(2)),port);
}else if(m03.find()){
//System.out.println("ping-> "+host);
address = new InetSocketAddress(host,port);
}
} catch (IOException x) {
failure = x;
}
}
/**
* 显示连接主机的相关信息,如连接时间,出错提示。
* @author centre
*/
void show() {
String m2="//d{1,3}//.//d{1,3}//.//d{1,3}//.//d{1,3}";
Pattern p2=Pattern.compile(m2);
Matcher m03=p2.matcher(host);
String result;
if (tf1.getText()!="") {
tf1.setText(tf1.getText()+"/n"+"ping->"+host);
}else {
tf1.setText("ping->"+host);
}
if (connectFinish != 0)
result = Long.toString(connectFinish - connectStart) + "ms";
else if (failure != null)
result = failure.toString();
else
result = "Timed out";
for(int i=0;i<4;i++){
if (m03.find()) {
tf1.setText(tf1.getText()+"/n"+address.getHostName()+"/"+address + " : " + result);
//System.out.println(address.getHostName()+"/"+address + " : " + result);
}else {
tf1.setText(tf1.getText()+"/n"+address + " : " + result);
//System.out.println(address + " : " + result);
}
}
shown = true;
}
}
/**
* 打印目标主机线程监听到的信息
* @author centre
*
*/
class Printer extends Thread {
LinkedList<Target> pinging = new LinkedList<Target>();
Printer() {
setName("Printer");//改变线程名字,使之与参数相同
setDaemon(true);//标志该线程为守护线程
}
void add(Target t) {
synchronized (pinging) {
pinging.add(t);
pinging.notify();
}
}
public void run() {
try {
for (;;) {
Target t = null;
synchronized (pinging) {
while (pinging.size() == 0)
pinging.wait();
t = (Target)pinging.removeFirst();//从pinging中移除目标主机,并将该目标主机返回
}
t.show();
}
} catch (InterruptedException x) {
return;
}
}
}
// 通过单选择方式连接并行的目标主机线程
class Connector extends Thread{
Selector sel;
Printer printer;
/*
*pinging目标的集合 ,我们用这个集合是因为如果我们要用一个selector来注册一个channel
*当connector线程在selector中被阻塞,那么我们可以阻塞它。
*/
LinkedList<Target> pinging = new LinkedList<Target>();
Connector(Printer pr) throws IOException {
printer = pr;
sel = Selector.open();
setName("Connector");
}
//初始化一个Connection序列给指定的target,同时把该target加到pinging目标集合
void add(Target t) {
SocketChannel sc = null;
try {
//打开一个频道,把它设置为非阻塞的,同时初始化连接
sc = SocketChannel.open();
sc.configureBlocking(false);
boolean connected = sc.connect(t.address);
//记录开始连接时的时间
t.channel = sc;
t.connectStart = System.currentTimeMillis();
if (connected) {
t.connectFinish = t.connectStart;
sc.close();
printer.add(t);
} else {
//如果为连接,则把新的channel加入到pinging集合
synchronized (pinging) {
pinging.add(t);
}
}
// 使尚未返回的第一个选择操作立即返回
sel.wakeup();
}catch (IOException x) {
if (sc != null) {
try {
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
t.failure = x;
printer.add(t);
}
}
// 处理任何在pinging集合中的目标
void processpingingTargets() throws IOException {
synchronized (pinging) {
while (pinging.size() > 0) {
Target t = (Target)pinging.removeFirst();//取出第一个目标
try {
/*向给定的选择器注册此通道,返回一个选择键
*/
t.channel.register(sel, SelectionKey.OP_CONNECT, t);
} catch (IOException x) {
//如果发生异常,将channel关闭,同时记录异常。
t.channel.close();
t.failure = x;
printer.add(t);
}
}
}
}
//处理选择的key
void processSelectedKeys() throws IOException {
for (Iterator i = sel.selectedKeys().iterator(); i.hasNext();) {
// 索引下一个key,同时将其从set中移除。
SelectionKey sk = (SelectionKey)i.next();
i.remove();
// 找到目标和channel
Target t = (Target)sk.attachment();
SocketChannel sc = (SocketChannel)sk.channel();
// 试图完成连接序列
try {
if (sc.finishConnect()) {
sk.cancel();
t.connectFinish = System.currentTimeMillis();
sc.close();
printer.add(t);
}
} catch (IOException x) {
sc.close();
t.failure = x;
printer.add(t);
}
}
}
volatile boolean shutdown = false;
// 当target线程关闭时,调用主线程
void shutdown() {
shutdown = true;
sel.wakeup();
}
//循环连接
public void run() {
for (;;) {
try {
int n = sel.select();
if (n > 0)
processSelectedKeys();
processpingingTargets();
if (shutdown) {
sel.close();
return;
}
} catch (IOException x) {
x.printStackTrace();
}
}
}
}
class ClearPing implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
tf0.setText("");
}
}
class PingSend implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
if (to!="") {
to=tf0.getText();
}
// 创建线程同时将它们启动
Printer printer = new Printer();
printer.start();
Connector connector = null;
try {
connector = new Connector(printer);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
connector.start();
// 创建目标,同时把它们加入集合
LinkedList<Target> targets = new LinkedList<Target>();
for(int i=0;i<1;i++){
Target t = new Target(to);
targets.add(t);
connector.add(t);
}
// 等待结束
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
connector.shutdown();
try {
connector.join();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 打印目标没显示的状态
for (Iterator i = targets.iterator(); i.hasNext();) {
Target t = (Target)i.next();
while(!t.shown)
t.show();
t.shown = true;
}
}
}
public static void main(String[] args)throws InterruptedException, IOException{
Ping p=new Ping();
if (p.to=="") {
p.to="www.baidu.com";
p.tf0.setText(p.to);
}
port=80;
}
}