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

一、要求:

 

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

2.设计要求:

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

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

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

二、代码

功能实现类:

 
  1. public class PortScanner{

  2. public static void main(String[] args){

  3. new EditorWin();

  4. }

  5. }

  6. class EditorWin extends JFrame implements ActionListener {

  7. private JLabel startIp,endIp,l_startPort,l_endPort,l_portOfThread ,showResult ,empty,type ,status;

  8. private JTextField f_startIp,f_endIp,f_startPort,f_endPort,f_portOfThread ;

  9. private JScrollPane result ;

  10. private JComboBox comboBox ;

  11. private JButton startScanner,exitScanner ,clear,reset;

  12. private JPanel top,bottom ;

  13. private JTextArea message ;

  14. private String startIpStr ,endIpStr;

  15. private int startPort,endPort,portOfThread ,threadNum ;

  16. public EditorWin(){

  17. this.setTitle("多线程端口扫描器") ;

  18. startIp = new JLabel("扫描的Ip") ;

  19. l_startPort = new JLabel("起始端口") ;

  20. l_endPort = new JLabel("结束端口") ;

  21. l_portOfThread = new JLabel("每个线程扫描端口数") ;

  22. status=new JLabel("未开始扫描") ;

  23. showResult = new JLabel("扫描结果") ;

  24. endIp = new JLabel("结束Ip");

  25. empty = new JLabel(" ") ;

  26. type = new JLabel("选择扫描的类型") ;

  27.  
  28. startScanner = new JButton("扫描");

  29. exitScanner = new JButton("退出");

  30. clear = new JButton("清空") ;

  31. reset = new JButton("重置") ;

  32.  
  33. f_endIp = new JTextField(12) ;

  34. f_startIp = new JTextField(12) ;

  35. f_startPort = new JTextField(5) ;

  36. f_endPort = new JTextField(5) ;

  37. f_portOfThread = new JTextField(5) ;

  38.  
  39. message = new JTextArea(20,20) ;

  40. result = new JScrollPane(message) ;

  41. result.setColumnHeaderView(showResult) ;

  42.  
  43. comboBox = new JComboBox() ;

  44. comboBox.addItem("地址");

  45. comboBox.addItem("地址段");

  46.  
  47. endIp.setVisible(false) ;

  48. f_endIp.setVisible(false) ;

  49. top = new JPanel() ;

  50. top.add(type);

  51. top.add(comboBox) ;

  52. top.add(startIp) ;

  53. top.add(f_startIp) ;

  54. top.add(endIp) ;

  55. top.add(f_endIp) ;

  56. top.add(l_startPort) ;

  57. top.add(f_startPort) ;

  58. top.add(l_endPort) ;

  59. top.add(f_endPort) ;

  60. top.add(l_portOfThread) ;

  61. top.add(f_portOfThread) ;

  62. bottom = new JPanel() ;

  63. bottom.add(status) ;

  64. bottom.add(empty) ;

  65. bottom.add(empty) ;

  66. bottom.add(empty) ;

  67. bottom.add(empty) ;

  68. bottom.add(empty) ;

  69. bottom.add(empty) ;

  70. bottom.add(startScanner) ;

  71. bottom.add(clear);

  72. bottom.add(reset);

  73. bottom.add(exitScanner) ;

  74. this.add(top,BorderLayout.NORTH);

  75. this.add(result,BorderLayout.CENTER) ;

  76. this.add(bottom,BorderLayout.SOUTH) ;

  77. comboBox.addActionListener(this) ;

  78. startScanner.addActionListener(this) ;

  79. exitScanner.addActionListener(this) ;

  80. clear.addActionListener(this) ;

  81. reset.addActionListener(this) ;

  82. setSize(1000, 500);

  83. setVisible(true);

  84. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  85. }

  86. @Override

  87. public void actionPerformed(ActionEvent e) {

  88. if(e.getSource()==startScanner){ //点击扫描按钮

  89. //点击时刻

  90. startIpStr = f_startIp.getText().trim() ; //得到输入的Ip

  91. if(checkIP(startIpStr)){

  92. //判断是否为数字

  93. try{

  94. startPort = Integer.parseInt(f_startPort.getText().trim()) ;

  95. endPort = Integer.parseInt(f_endPort.getText().trim()) ;

  96. portOfThread =Integer.parseInt(f_portOfThread.getText().trim()) ;

  97. threadNum = (endPort-startPort)/portOfThread+1 ;

  98. //普安段端口号的范围

  99. if(startPort<0||endPort>65535||startPort>endPort){

  100. JOptionPane.showMessageDialog(this, "端口号范围:0~65535,并且最大端口号应大于最小端口号!") ;

  101. }

  102. else{

  103. if(portOfThread>endPort-startPort||portOfThread<1){

  104. JOptionPane.showMessageDialog(this, "每个线程扫描的端口数不能大于所有的端口数且不能小于1") ;

  105. }else{

  106. if(((String) comboBox.getSelectedItem()).equals("地址")){

  107. message.append("************************************************************"+"\n") ;

  108. message.append("正在扫描 "+startIpStr+" 每个线程扫描端口个数"+portOfThread+"\n"+"开启的线程数"+threadNum+"\n") ;

  109. message.append("开始端口 "+startPort+" 结束端口" +endPort+"\n") ;

  110. message.append("主机名:"+getHostname(startIpStr)+"\n");

  111. message.append("开放的端口如下:"+"\n") ;

  112. for(int i = startPort;i <= endPort; i++) {

  113. if((i + portOfThread) <= endPort) {

  114. new Scan(i, i + portOfThread,startIpStr).start();

  115. i += portOfThread;

  116. }

  117. else {

  118. new Scan(i, endPort,startIpStr).start();

  119. i += portOfThread;

  120. }

  121. }

  122. }else{

  123. endIpStr = f_endIp.getText() ;

  124. if(checkIP(endIpStr)){

  125. //扫描Ip地址段

  126. Set ipSet = new HashSet<Object>() ;

  127. int start = Integer.valueOf(startIpStr.split("\\.")[3]);

  128. int end = Integer.valueOf(endIpStr.split("\\.")[3]);

  129. String starts = startIpStr.split("\\.")[0]+"."+startIpStr.split("\\.")[1]+"."+startIpStr.split("\\.")[2];

  130. //生成IP地址

  131. for(int i = start;i<=end;i++){

  132. ipSet.add(starts+"."+i) ; //地海段的每个地址存入集合

  133. }

  134. for (Object str : ipSet) {

  135. new ScanIp(str.toString()).start() ;

  136. }

  137. }else{

  138. JOptionPane.showMessageDialog(this, "请输入正确的Ip地址") ;

  139. }

  140.  
  141. }

  142. }

  143. }

  144. }

  145. catch(NumberFormatException e1){

  146. JOptionPane.showMessageDialog(this, "错误的端口号或端口号和线程数必须为整数") ;

  147. }

  148. }

  149. else{

  150. JOptionPane.showMessageDialog(this, "请输入正确的Ip地址") ;

  151. }

  152. }

  153. else if(e.getSource()==reset){

  154. f_startIp.setText("") ;

  155. f_startPort.setText("") ;

  156. f_endPort.setText("") ;

  157. f_portOfThread.setText("") ;

  158. }

  159. else if(e.getSource()==clear){

  160. message.setText("") ;

  161. System.out.println((String) comboBox.getSelectedItem());

  162. }

  163. else if(e.getSource()==exitScanner){

  164. System.exit(1);

  165. }else if(e.getSource()==comboBox){

  166. String type=(String) comboBox.getSelectedItem();

  167. if(type.equals("地址")){

  168. endIp.setVisible(false) ;

  169. f_endIp.setVisible(false) ;

  170. startIp.setText("扫描的Ip") ;

  171. }else{

  172. endIp.setVisible(true) ;

  173. f_endIp.setVisible(true) ;

  174. startIp.setText("开始Ip") ;

  175. }

  176. }

  177. }

  178. //扫描端口地址的线程

  179. class Scan extends Thread{

  180. int maxPort, minPort;

  181. String Ip;

  182. Scan(int minPort, int maxPort,String Ip){

  183. this.minPort=minPort ;

  184. this.maxPort=maxPort ;

  185. this.Ip=Ip;

  186. }

  187. @SuppressWarnings("unchecked")

  188. public void run() {

  189. Socket socket = null ;

  190. for(int i = minPort;i<maxPort;i++){

  191. try {

  192. socket=new Socket(Ip, i);

  193. findInfoByPort(i ,Ip);//通过端口号调用数据库信息

  194. message.append("\n");

  195. socket.close();

  196. } catch (Exception e) {

  197. message.append("");

  198. }

  199. status.setText("正在扫描"+i) ;

  200. }

  201. status.setText("扫描结束") ;

  202. }

  203. }

  204. //扫描Ip地址段查看合法Ip的线程

  205. class ScanIp extends Thread{

  206. String Ip ;

  207. ScanIp(String Ip ){

  208. this.Ip = Ip ;

  209. }

  210. public synchronized void run(){

  211. try {

  212. for(int i = startPort;i <= endPort; i++) {

  213. //扫描开放的Ip

  214. InetAddress.getByName(Ip);

  215. if((i + portOfThread) <= endPort) {

  216. new Scan(i, i + portOfThread,Ip).start();

  217. i += portOfThread;

  218. }

  219. else {

  220. new Scan(i, endPort,Ip).start();

  221. i += portOfThread;

  222. }

  223. }

  224. } catch (Exception e) {

  225. System.out.println(Ip+"\n");

  226. }

  227.  
  228. }

  229. }

  230. //根据端口号,查询数据库中端口号的相应信息并显示在文本域之中

  231. synchronized void findInfoByPort(int port,String Ip){

  232. message.append("-----------------------"+"Ip"+Ip+"的"+"端口号"+port+"------------------------------------"+"\n");

  233. Connection conn ;

  234. PreparedStatement pst ;

  235. ResultSet rs ;

  236. conn = JdbcUtils.getConnection() ;//与数据库建立连接,获取Connection对象

  237. String sql = "Select * from ports where port ="+port;

  238. try {

  239. pst = conn.prepareStatement(sql) ;

  240. rs = pst.executeQuery() ;

  241. String totalStr = null ;

  242. while(rs.next()){

  243. String server = rs.getString("server");

  244. String info = rs.getString("info") ;

  245. message.append("端口信息:"+server+"\n") ;

  246. message.append("端口说明:"+info+"\n") ;

  247. totalStr = totalStr+server ;

  248. }

  249. } catch (Exception e) {

  250. e.printStackTrace();

  251. }

  252. }

  253. // 判断输入的IP是否合法

  254. private boolean checkIP(String str) {

  255. Pattern pattern = Pattern

  256. .compile("^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]"

  257. + "|[*])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]|[*])$");

  258. return pattern.matcher(str).matches();

  259. }

  260. //根据Ip获得主机名、

  261. public static synchronized String getHostname(String host){

  262. InetAddress addr ;

  263. try {

  264. addr = InetAddress.getByName(host);

  265. return addr.getHostName();

  266. } catch (UnknownHostException e) {

  267. return "网络不通或您输入的信息无法构造InetAddress对象!";

  268. }

  269. }

  270. }

  271.  

数据库工具类

 

 
  1. package portScanner;

  2.  
  3.  
  4. import java.sql.Connection;

  5. import java.sql.DriverManager;

  6. import java.sql.PreparedStatement;

  7. import java.sql.ResultSet;

  8. import java.sql.SQLException;

  9. public final class JdbcUtils {

  10. private static String url = "jdbc:mysql://localhost:3306/portInfo?useUnicode=true&characterEncoding=utf8";

  11. private static String user = "root";

  12. private static String psw = "root";

  13. private static Connection conn;

  14. static {

  15. try {

  16. Class.forName("com.mysql.jdbc.Driver");

  17. } catch (ClassNotFoundException e) {

  18. e.printStackTrace();

  19. throw new RuntimeException(e);

  20. }

  21. }

  22. /**

  23. * 获取数据库的连接

  24. * @return conn

  25. */

  26. public static Connection getConnection() {

  27. if(null == conn) {

  28. try {

  29. conn = DriverManager.getConnection(url, user, psw);

  30. } catch (SQLException e) {

  31. e.printStackTrace();

  32. throw new RuntimeException(e);

  33. }

  34. }

  35. return conn;

  36. }

  37. /**

  38. * 释放资源

  39. * @param conn

  40. * @param pstmt

  41. * @param rs

  42. */

  43. public static void closeResources(Connection conn,PreparedStatement pstmt,ResultSet rs) {

  44. if(null != rs) {

  45. try {

  46. rs.close();

  47. } catch (SQLException e) {

  48. e.printStackTrace();

  49. throw new RuntimeException(e);

  50. } finally {

  51. if(null != pstmt) {

  52. try {

  53. pstmt.close();

  54. } catch (SQLException e) {

  55. e.printStackTrace();

  56. throw new RuntimeException(e);

  57. } finally {

  58. if(null != conn) {

  59. try {

  60. conn.close();

  61. } catch (SQLException e) {

  62. e.printStackTrace();

  63. throw new RuntimeException(e);

  64. }

  65. }

  66. }

  67. }

  68. }

  69. }

  70. }

  71. }

  72.  

三、实现功能的界面截图       

         地址:

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

地址

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

(界面过于丑T-T)

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

 

 
  1. for(int i = startPort;i <= endPort; i++) {

  2. //扫描开放的Ip

  3. InetAddress.getByName(Ip);

  4. if((i + portOfThread) <= endPort) {

  5. new Scan(i, i + portOfThread,Ip).start();

  6. i += portOfThread;

  7. }

  8. else {

  9. new Scan(i, endPort,Ip).start();

  10. i += portOfThread;

  11. }

        原理:根据每个线程扫描端口号的个数,从而对端口号进行分段,每个线程执行一段。