多线程端口扫描器的实现(java)

这是我大三下学期课程设计的题目,没有想象中的那么难。

一、要求:

1.利用Socket通信机制实现一个多线程的端口扫描器。

2.设计要求:

      2.1用户界面:用户可以输入IP地址或IP地址段;输入端口号或端口号范围;列表显示主机名、开放的端口及开放端口上相应的服务或恶意程序的名称;功能按钮。

      2.2使用多线程机制对某一地址(段)的主机端口进行扫描;说明开放端口的类型(如UDP端口还是TCP端口);查询数据库,对开放的端口进行说明(如提供的服务或存在的风险)。

      2.3有关端口与服务或恶意程序的映射关系保存为数据库表,以扫描出的开放端口号为关键字查询表,将端口的说明显示在界面的列表框中。

二、代码

功能实现类:

public class PortScanner{
	public static void main(String[] args){
		new EditorWin();
	}
}
class EditorWin extends JFrame implements  ActionListener {	
	private JLabel startIp,endIp,l_startPort,l_endPort,l_portOfThread ,showResult ,empty,type ,status;
	private JTextField f_startIp,f_endIp,f_startPort,f_endPort,f_portOfThread ;
	private JScrollPane result ;
	private JComboBox comboBox ;
	private JButton startScanner,exitScanner ,clear,reset;
	private JPanel top,bottom ;
	private JTextArea message ;
	private String startIpStr ,endIpStr;
	private int startPort,endPort,portOfThread ,threadNum ;
	public EditorWin(){
		this.setTitle("多线程端口扫描器") ;
		startIp = new JLabel("扫描的Ip") ;
		l_startPort = new JLabel("起始端口") ;
		l_endPort = new JLabel("结束端口") ;
		l_portOfThread = new JLabel("每个线程扫描端口数") ;
		status=new JLabel("未开始扫描") ;
		showResult = new JLabel("扫描结果") ;
		endIp = new JLabel("结束Ip"); 
		empty = new JLabel("                                                            ") ;
		type = new JLabel("选择扫描的类型") ;
		
		startScanner = new JButton("扫描");
		exitScanner = new  JButton("退出");
		clear = new JButton("清空") ;
		reset = new JButton("重置") ;
		
		f_endIp = new JTextField(12) ;
		f_startIp = new JTextField(12) ;
		f_startPort = new JTextField(5) ;
		f_endPort = new JTextField(5) ;
		f_portOfThread = new JTextField(5) ;
		
		message = new JTextArea(20,20) ;
		result = new JScrollPane(message) ;
		result.setColumnHeaderView(showResult) ;
		
		comboBox = new JComboBox() ;
		comboBox.addItem("地址");
		comboBox.addItem("地址段");
		
		endIp.setVisible(false) ;
		f_endIp.setVisible(false) ;
		top = new JPanel() ;
		top.add(type);
		top.add(comboBox) ;
		top.add(startIp) ;
		top.add(f_startIp) ;
		top.add(endIp) ;
		top.add(f_endIp) ;
		top.add(l_startPort) ;
		top.add(f_startPort) ;
		top.add(l_endPort) ;	
		top.add(f_endPort) ;
		top.add(l_portOfThread) ;
		top.add(f_portOfThread) ; 		
		bottom = new JPanel() ;
		bottom.add(status) ;
		bottom.add(empty) ;
		bottom.add(empty) ;
		bottom.add(empty) ;
		bottom.add(empty) ;
		bottom.add(empty) ;
		bottom.add(empty) ;
		bottom.add(startScanner) ;	
		bottom.add(clear);
		bottom.add(reset);
		bottom.add(exitScanner) ;	
		this.add(top,BorderLayout.NORTH);
		this.add(result,BorderLayout.CENTER) ;
		this.add(bottom,BorderLayout.SOUTH) ;
		comboBox.addActionListener(this) ;
		startScanner.addActionListener(this) ;
		exitScanner.addActionListener(this) ;
		clear.addActionListener(this) ;
		reset.addActionListener(this) ;
		setSize(1000, 500);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource()==startScanner){ //点击扫描按钮
			//点击时刻
			startIpStr = f_startIp.getText().trim() ;   //得到输入的Ip
			if(checkIP(startIpStr)){
				//判断是否为数字
				try{
					startPort = Integer.parseInt(f_startPort.getText().trim()) ;
					endPort =  Integer.parseInt(f_endPort.getText().trim()) ;
					portOfThread  =Integer.parseInt(f_portOfThread.getText().trim())  ;
					threadNum = (endPort-startPort)/portOfThread+1 ;
					//普安段端口号的范围
					if(startPort<0||endPort>65535||startPort>endPort){
						JOptionPane.showMessageDialog(this, "端口号范围:0~65535,并且最大端口号应大于最小端口号!") ;
						}
					else{
						if(portOfThread>endPort-startPort||portOfThread<1){
							JOptionPane.showMessageDialog(this, "每个线程扫描的端口数不能大于所有的端口数且不能小于1") ;
						}else{
							if(((String) comboBox.getSelectedItem()).equals("地址")){
								message.append("************************************************************"+"\n") ;
								message.append("正在扫描  "+startIpStr+"          每个线程扫描端口个数"+portOfThread+"\n"+"开启的线程数"+threadNum+"\n") ;
								message.append("开始端口  "+startPort+"         结束端口" +endPort+"\n") ;	
								message.append("主机名:"+getHostname(startIpStr)+"\n");
								message.append("开放的端口如下:"+"\n") ;
								for(int i = startPort;i <= endPort; i++) {
									if((i + portOfThread) <= endPort) {
										new Scan(i, i + portOfThread,startIpStr).start();
										i += portOfThread;
									}
									else {
										new Scan(i, endPort,startIpStr).start();
										i += portOfThread;
									}			
								}			
							}else{
								endIpStr = f_endIp.getText() ;
								if(checkIP(endIpStr)){
									//扫描Ip地址段	
									Set ipSet = new HashSet<Object>() ;
									int start = Integer.valueOf(startIpStr.split("\\.")[3]);
									int end = Integer.valueOf(endIpStr.split("\\.")[3]);
									String starts = startIpStr.split("\\.")[0]+"."+startIpStr.split("\\.")[1]+"."+startIpStr.split("\\.")[2];
							    	//生成IP地址
							    	for(int i = start;i<=end;i++){
							    		ipSet.add(starts+"."+i) ;    //地海段的每个地址存入集合					
							    	}
							    	for (Object str : ipSet) {
							    		new ScanIp(str.toString()).start() ;
							    	}
								}else{		
							    	JOptionPane.showMessageDialog(this, "请输入正确的Ip地址") ;
								}
								
							}										
						}				
					}
				}
				catch(NumberFormatException e1){
					JOptionPane.showMessageDialog(this, "错误的端口号或端口号和线程数必须为整数") ;				
				}
			}	
			else{
				JOptionPane.showMessageDialog(this, "请输入正确的Ip地址") ;
			   }
			}			
		else if(e.getSource()==reset){
			 f_startIp.setText("") ;
			 f_startPort.setText("") ;
			 f_endPort.setText("") ;
			 f_portOfThread.setText("") ; 
		}
		else if(e.getSource()==clear){
			message.setText("") ;
			System.out.println((String) comboBox.getSelectedItem());
		}
		else if(e.getSource()==exitScanner){
			System.exit(1);
		}else if(e.getSource()==comboBox){
			String type=(String) comboBox.getSelectedItem();
			if(type.equals("地址")){
				endIp.setVisible(false) ;
				f_endIp.setVisible(false) ;
				startIp.setText("扫描的Ip") ;
			}else{
				endIp.setVisible(true) ;
				f_endIp.setVisible(true) ;
				startIp.setText("开始Ip") ;
			}
		}		
	}
	//扫描端口地址的线程
	class Scan extends Thread{
		int maxPort, minPort;
		String Ip;
		Scan(int minPort, int maxPort,String Ip){
			this.minPort=minPort ;
			this.maxPort=maxPort ;
			this.Ip=Ip;
		}
		@SuppressWarnings("unchecked")
		public  void run() {
				 Socket socket = null ;				
				 for(int i = minPort;i<maxPort;i++){
					try {						
						socket=new Socket(Ip, i);					
						findInfoByPort(i ,Ip);//通过端口号调用数据库信息	
						message.append("\n");
		                socket.close();
					} catch (Exception e) {
						message.append("");
					}  
	    			 status.setText("正在扫描"+i) ;
				 }		
				 status.setText("扫描结束") ;
		   }
	} 
	//扫描Ip地址段查看合法Ip的线程
	class ScanIp extends Thread{
    	String  Ip ;
    	ScanIp(String  Ip ){
    		this.Ip = Ip ;
    	}  	   	
    	 public synchronized void run(){
    	 try {		
    		 for(int i = startPort;i <= endPort; i++) {
	   			//扫描开放的Ip
    			 InetAddress.getByName(Ip);
				if((i + portOfThread) <= endPort) {
					new Scan(i, i + portOfThread,Ip).start();
					i += portOfThread;
				}
				else {
					new Scan(i, endPort,Ip).start();
					i += portOfThread;
				}				
			}   		
		 } catch (Exception e) {		
			System.out.println(Ip+"\n");
		 }
    	 
    	}
    }
	//根据端口号,查询数据库中端口号的相应信息并显示在文本域之中
	synchronized void findInfoByPort(int port,String Ip){	
		message.append("-----------------------"+"Ip"+Ip+"的"+"端口号"+port+"------------------------------------"+"\n");
		Connection conn ;
		PreparedStatement pst  ;
		ResultSet rs  ;
		conn = JdbcUtils.getConnection() ;//与数据库建立连接,获取Connection对象
		String sql = "Select * from ports where port ="+port;
		try {
			pst = conn.prepareStatement(sql) ;
			rs = pst.executeQuery() ;
			String totalStr = null ;
				while(rs.next()){
					String server = rs.getString("server");
					String info = rs.getString("info") ;
					message.append("端口信息:"+server+"\n") ;
					message.append("端口说明:"+info+"\n") ;					
					totalStr =  totalStr+server ;
				}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}	
	// 判断输入的IP是否合法
    private boolean checkIP(String str) {
        Pattern pattern = Pattern
                .compile("^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]"
                        + "|[*])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]|[*])$");
        return pattern.matcher(str).matches();
    }
   //根据Ip获得主机名、
    public static  synchronized String getHostname(String host){
		InetAddress addr ;
		try {
			addr = InetAddress.getByName(host);		
			return addr.getHostName();
		} catch (UnknownHostException e) {
			return "网络不通或您输入的信息无法构造InetAddress对象!";
		}		
	}
}
     
数据库工具类

package portScanner;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public final class JdbcUtils {  
    private static String url = "jdbc:mysql://localhost:3306/portInfo?useUnicode=true&characterEncoding=utf8";  
    private static String user = "root";  
    private static String psw = "root";       
    private static  Connection conn;        
    static {  
        try {  
            Class.forName("com.mysql.jdbc.Driver");  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
            throw new RuntimeException(e);  
        }  
    }             
    /** 
     * 获取数据库的连接 
     * @return conn 
     */  
    public static Connection getConnection() {  
        if(null == conn) {  
            try {  
                conn = DriverManager.getConnection(url, user, psw);  
            } catch (SQLException e) {  
                e.printStackTrace();  
                throw new RuntimeException(e);  
            }  
        }  
        return conn;  
    }        
    /** 
     * 释放资源 
     * @param conn 
     * @param pstmt 
     * @param rs 
     */  
    public static void closeResources(Connection conn,PreparedStatement pstmt,ResultSet rs) {  
        if(null != rs) {  
            try {  
                rs.close();  
            } catch (SQLException e) {  
                e.printStackTrace();  
                throw new RuntimeException(e);  
            } finally {  
                if(null != pstmt) {  
                    try {  
                        pstmt.close();  
                    } catch (SQLException e) {  
                        e.printStackTrace();  
                        throw new RuntimeException(e);  
                    } finally {  
                        if(null != conn) {  
                            try {  
                                conn.close();  
                            } catch (SQLException e) {  
                                e.printStackTrace();  
                                throw new RuntimeException(e);  
                            }  
                        }  
                    }  
                }  
            }  
        }  
    }  
}  

三、实现功能的界面截图

     

         地址:

   

地址


(界面过于丑T-T)

四、多线程扫描端口算法的说明:

 for(int i = startPort;i <= endPort; i++) {
	   			//扫描开放的Ip
    			 InetAddress.getByName(Ip);
				if((i + portOfThread) <= endPort) {
					new Scan(i, i + portOfThread,Ip).start();
					i += portOfThread;
				}
				else {
					new Scan(i, endPort,Ip).start();
					i += portOfThread;
				}				
        原理:根据每个线程扫描端口号的个数,从而对端口号进行分段,每个线程执行一段。


public static JFrame main=new JFrame("JAVA端口扫描"); //显示扫描结果 public static JTextArea Result=new JTextArea("",4,40); //滚动条面板 public static JScrollPane resultPane = new JScrollPane(Result,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); //输入主机名文本框 public static JTextField hostname=new JTextField("localhost",8); //输入ip地址前3位的输入框 public static JTextField fromip1=new JTextField("0",3); //输入ip地址4~6位的输入框 public static JTextField fromip2=new JTextField("0",3); //输入ip地址7~9位的输入框 public static JTextField fromip3=new JTextField("0",3); //输入起始ip地址最后4位的输入框 public static JTextField fromip4=new JTextField("0",3); //输入目标ip地址最后4位的输入框 public static JTextField toip=new JTextField("0",3); //输入最小端口的输入框 public static JTextField minPort=new JTextField("0",4); //输入最大端口的输入框 public static JTextField maxPort=new JTextField("1000",4); //输入最大线程数量的输入框 public static JTextField maxThread=new JTextField("100",3); //错误提示框 public static JDialog DLGError=new JDialog(main,"错误!"); public static JLabel DLGINFO=new JLabel(""); public static JLabel type=new JLabel("请选择:"); //扫描类型 public static JRadioButton radioIp = new JRadioButton("IP地址:"); public static JRadioButton radioHost = new JRadioButton("主机名:",true); //单选框组 public static ButtonGroup group = new ButtonGroup(); public static JLabel P1=new JLabel("端口范围:"); public static JLabel P2=new JLabel("~"); public static JLabel P3=new JLabel("~"); public static JLabel Pdot1 = new JLabel("."); public static JLabel Pdot2 = new JLabel("."); public static JLabel Pdot3 = new JLabel("."); public static JLabel TNUM=new JLabel("线程数:"); public static JLabel RST=new JLabel("扫描结果: "); public static JLabel con=new JLabel(" "); //定义按钮 public static JButton OK = new JButton("确定"); public static JButton Submit = new JButton("开始扫描"); public static JButton Cancel = new JButton("退出"); public static JButton saveButton = new JButton("保存扫描结果"); //菜单栏 public static JMenuBar myBar = new JMenuBar(); public static JMenu myMenu = new JMenu("文件(F)"); public static JMenuItem saveItem = new JMenuItem("保存扫描结果(S)"); public static JMenuItem exitItem = new JMenuItem("退出(Q)"); public static JMenu myMenu2 = new JMenu("帮助"); public static JMenuItem helpItem = new JMenuItem("阅读"); public static void main(String[] args){ main.setSize(500,400); main.setLocation(300,300); main.setResizable(false); main.setLayout(new GridBagLayout()); main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); DLGError.setSize(300,100); DLGError.setLocation(400,400); //添加“菜单栏” myMenu.add(saveItem); myMenu.add(exitItem); myMenu2.add(helpItem); myBar.add(myMenu); myBar.add(myMenu2); main.setJMenuBar(myBar); //设置热键 myMenu.setMnemonic('F'); saveItem.setMnemonic ('S'); //为“另存为”组件设置快捷键为ctrl+s saveItem.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_S,InputEvent.CTRL_MASK)); exitItem.setMnemonic('Q'); exitItem.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_E,InputEvent.CTRL_MASK)); //采用表格包型布局 Container mPanel = main.getContentPane(); GridBagConstraints c = new GridBagConstraints(); c.insets = new Insets(10,0,0,10); c.gridx = 0; c.gridy = 0; c.gridwidth = 10; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(type,c); group.add(radioIp); group.add(radioHost); c.gridx = 0; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(radioIp,c); c.gridx = 1; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(fromip1,c); c.gridx = 2; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(Pdot1,c); c.gridx = 3; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(fromip2,c); c.gridx = 4; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(Pdot2,c); c.gridx = 5; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(fromip3,c); c.gridx = 6; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(Pdot3,c); c.gridx = 7; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(fromip4,c); c.gridx = 8; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(P2,c); c.gridx = 9; c.gridy = 1; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(toip,c); c.gridx = 0; c.gridy = 2; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(radioHost,c); c.gridx = 1; c.gridy = 2; c.gridwidth = 3; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(hostname,c); c.gridx = 0; c.gridy = 3; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(P1,c); c.gridx = 1; c.gridy = 3; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(minPort,c); c.gridx = 2; c.gridy = 3; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(P3,c); c.gridx = 3; c.gridy = 3; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(maxPort,c); c.gridx = 0; c.gridy = 4; c.gridwidth = 1; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(TNUM,c); c.gridx = 1; c.gridy = 4; c.gridwidth = 3; c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; mPanel.add(maxThread,c);
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值