`
shihuan830619
  • 浏览: 573943 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Java Socket长连接示例代码

    博客分类:
  • J2SE
阅读更多
SocketListenerPusher.java代码如下:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.configuration.ConfigurationException;
import org.directwebremoting.impl.DaemonThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.shihuan.dragonkeeper.common.utils.PropertiesUtil;
import com.shihuan.dragonkeeper.global.ConfigFile;

public class SocketListenerPusher implements Runnable {

	protected static Logger logger = LoggerFactory.getLogger(SocketListenerPusher.class);
	
	public static String socketlistenerserver_CONFIG = ConfigFile.SOCKETLISTENERSERVER__CONFIG + ConfigFile.SUFFIX_NAME;
	
	private ServerSocket serverSocket;
    private ExecutorService pool;
    
	
	public SocketListenerPusher() {
		int port = 0;
		int poolsize = 0;
		
		try {
			port = Integer.parseInt(PropertiesUtil.getPropertiesValue(socketlistenerserver_CONFIG, "serverport"));
			poolsize = Integer.parseInt(PropertiesUtil.getPropertiesValue(socketlistenerserver_CONFIG, "poolsize"));
			
			serverSocket = new ServerSocket();
			serverSocket.setReuseAddress(true);
			serverSocket.bind(new InetSocketAddress(port));
		    pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * poolsize);
		    
		    //下面两句循环执行run()方法, 相当于while(true){...}
		    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory());
			executor.scheduleAtFixedRate(this, 1L, 1L, TimeUnit.MILLISECONDS);
			
		} catch (NumberFormatException e) {
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		} catch (ConfigurationException e) {
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		}

	}
	
	public void run() {
		Socket socket = null;
		try {
			socket = serverSocket.accept();
			pool.execute(new SocketListenerHandler(socket));
		} catch (IOException e) {
			System.out.println("线程池被关闭!!!!!!!!!!!");
			pool.shutdown();
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		}
		
	}

SocketListenerHandler.java代码如下:
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.io.IOUtils;
import org.directwebremoting.Browser;
import org.directwebremoting.ScriptSessions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.shihuan.dragonkeeper.common.dto.DataSourceInfo;
import com.shihuan.dragonkeeper.common.utils.ByteArrayUtil;
import com.shihuan.dragonkeeper.common.utils.DataSourceMapUtil;
import com.shihuan.dragonkeeper.common.utils.DateFormatterUtil;
import com.shihuan.dragonkeeper.common.utils.PropertiesUtil;
import com.shihuan.dragonkeeper.global.ConfigFile;
import com.shihuan.dragonkeeper.server.bean.ActivityServiceBean;

public class SocketListenerHandler implements Runnable {

	protected static Logger logger = LoggerFactory.getLogger(SocketListenerHandler.class);
	
	private static String jdbc_CONFIG = ConfigFile.JDBC_CONFIG + ConfigFile.SUFFIX_NAME;
    
    public static final int timeOut = 0*1000 ;  //设置读取操作异常为1秒
    private final String dataRealTimeAction_id = "Agentdata_" + Math.random();
    
    private static final String noData = "{'nodata':'心跳信息'}";
	private static final String errorData = "{'error':'无法解析的请求'}";
    
	
	private Socket connectedsocket = null;
	
	public SocketListenerHandler(Socket socket){
		this.connectedsocket = socket;
	}

@Override
	public void run() {
		BufferedReader in = null;
String resultData = "";
		
		try {
			connectedsocket.setSoTimeout(timeOut);  //表示接收数据时的等待超时数据, 此方法必须在接收数据之前执行才有效. 此外, 当输入流的 read()方法抛出 SocketTimeoutException后, Socket仍然是连接的, 可以尝试再次读数据, 单位为毫秒, 它的默认值为 0(表示会无限等待, 永远不会超时)
			connectedsocket.setKeepAlive(false);   //表示对于长时间处于空闲状态的Socket, 是否要自动把它关闭.
			
			in = new BufferedReader(new InputStreamReader(connectedsocket.getInputStream()));
if (in.ready()) {  //判断流中是否有数据
				
				resultData = getNoHeadData(in.readLine());   //从Agent端接收到的数据
				logger.info("#### 结果DATA = "+resultData);
				
				if (resultData==null || "".equals(resultData)) {
					logger.info(dataRealTimeAction_id + " -->>> " + "内容为空!");
				} else if (resultData.charAt(0) != '{') {  //要在客户端定时维持心跳信息
					logger.info(dataRealTimeAction_id + " -->>> " + noData);
				} else {
					ActivityServiceBean asb = JSON.parseObject(resultData, ActivityServiceBean.class);
					System.out.println("打印预处理信息Start......");
					System.out.println(asb.getProxyname() + " -- " + asb.getIp() + " -- " + asb.getCalltime() + " -- " + asb.getAnswertime() + " -- " + asb.getCpu() + " -- " + asb.getThread() + " -- " + asb.getStatus() + " -- " + asb.getAccessaddress() + " -- " + asb.getAccessfilename() + " -- " + asb.getSql() + " -- " + asb.getContent());
					System.out.println("打印预处理信息End......");
//					parseData(ois);
					
					logger.info(dataRealTimeAction_id + ": 成功处理了接收到的数据!");
				}
				
			}
} catch (IOException e) {
			logger.error(e.getMessage() + " " + errorData, e);
			e.printStackTrace();
		} catch (NumberFormatException e) {
			logger.error(e.getMessage(), e);
			e.printStackTrace();
		} finally {
			
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
					e.printStackTrace();
				}
			}
}
		
	}


TestSocketListenerPusher.java请求端代码如下:
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;

import org.apache.commons.configuration.ConfigurationException;

import com.alibaba.fastjson.JSON;
import com.shihuan.dragonkeeper.common.utils.ByteArrayUtil;
import com.shihuan.dragonkeeper.common.utils.PropertiesUtil;
import com.shihuan.dragonkeeper.global.ConfigFile;
import com.shihuan.dragonkeeper.server.bean.ActivityServiceBean;

public class TestSocketListenerPusher implements Runnable {

	private static String socketlistenerserver_CONFIG = ConfigFile.SOCKETLISTENERSERVER__CONFIG + ConfigFile.SUFFIX_NAME;
	
	private Socket socketclient = null;
	
	@Override
	public void run() {
		
		String serverip = "";
		int port = 0;
		
		OutputStream os = null;
		
		try {
			serverip = PropertiesUtil.getPropertiesValue(socketlistenerserver_CONFIG, "serverip");
			port = Integer.parseInt(PropertiesUtil.getPropertiesValue(socketlistenerserver_CONFIG, "serverport"));
			
			
			ActivityServiceBean asb = null;
			
			for (int i=0; i<2; i++) {
				asb = new ActivityServiceBean();
				asb.setProxyname("testProxyname"+i);
				asb.setIp("testIp"+i);
				Date curdate = new Date();
				asb.setCalltime(curdate);
				asb.setAnswertime(curdate);
				asb.setCpu("testCpu"+i);
				asb.setThread("testThread"+i);
				asb.setStatus("testStatus"+i);
				asb.setAccessaddress("testAccessaddress"+i);
				asb.setAccessfilename("testAccessfilename"+i);
				asb.setSql("testSql"+i);
				asb.setContent("testContent"+i);
				
				String jsonStr = JSON.toJSONString(asb).trim();
				byte[] information = (new String(ByteArrayUtil.getIntToByte(jsonStr.length()))+jsonStr).getBytes();
				
				System.out.println(information.length);
				
				socketclient = new Socket(serverip, port);
				socketclient.setSoTimeout(0);
				socketclient.setKeepAlive(false);
				
				os = new BufferedOutputStream(socketclient.getOutputStream());
				os.write(information);
				os.flush();
				
				System.out.println("Client" + i + " -->>> " + new String(ByteArrayUtil.getIntToByte(jsonStr.length()))+jsonStr);
				
				os.close();
				Thread.sleep(3000);
			}
			
			
		} catch (ConfigurationException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			/*
			try {
				if (os != null) {
					os.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			*/
		}
		
		
	}
	
	public static void main(String[] args) {
		Thread t = new Thread(new TestSocketListenerPusher());
		t.start();
	}

}



源代码在笔者shihuan8@163.com邮箱网盘中J2EE代码文件夹里。

----------------------------------------------------------------------------------
如果是按byte[]传输数据的情况,请参考如下代码:

SimpleSocketServer.java代码如下:
package com.shihuan.socket;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class SimpleSocketServer {

	public static void main(String[] args) {
		try {
			ServerSocket ss = new ServerSocket();
			ss.setReuseAddress(true);  //两个进程共用同一个端口的时候,一个进程关闭后,另一个进程还能够立刻重用相同端口
            ss.setReceiveBufferSize(128*1024);  //缓冲区中允许接收的最大字节数,默认是8192
            ss.bind(new InetSocketAddress(19990)); 
			
			Socket client = ss.accept();
			InputStream in = new BufferedInputStream(client.getInputStream());
			
			byte tmpb = (byte)in.read();
			System.out.println("第一个字节的byte值 --->> " + tmpb);
			System.out.println("接收字节 --->> " + in.available());
			byte[] bc = new byte[in.available()+1];
			bc[0] = tmpb;
			in.read(bc, 1, in.available());
			
			System.out.println(bc.length);
			System.out.println(new String(bc));
			in.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}

	}

}


SimpleSocketClient.java代码如下:
package com.shihuan.socket;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class SimpleSocketClient {

	public static void main(String[] args) throws UnknownHostException {
		try {
			Socket s = new Socket("192.168.1.10", 19990);
			OutputStream os = new BufferedOutputStream(s.getOutputStream());
			String info = "abc!";
			info = "大家好!";
			byte[] bi = info.getBytes();
			os.write(bi);
			os.flush();
			os.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
	}

}


稍微复杂一点儿代码示例,处理了粘包问题:
StartListenerTcpThread.java代码:
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Vector;
import java.util.concurrent.ExcutorService;
import java.util.concurrent.Excutors;

import org.apache.commons.io.IUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.shihuan.dragonkeeper.common.utils.ByteArrayUtil;
import com.shihuan.dragonkeeper.global.ConfigFile;

public class StartListenerTcpThread implements Runnable {
   public static Logger logger = LoggerFactory.getLogger(StartListenerTcpThread.class);

   private static ExcutorService Threadpool = Excutors.newCachedThreadPool();
   private static boolean businessflag = true;
   private static final int receiveBufferSize = 128;
   private static Vector<byte[]> tmpbytes = new Vector<byte[]>();
   private ServerSocket serverSocket = null;

   public StartListenerTcpThread(String ip, int port){
      try{
         serverSocket = new ServerSocket();
         serverSocket.setReuseAddress(true);
         serverSocket.setReceiveBufferSize(receiveBufferSize*1024);
         serverSocket.setSoTimeout(0);
         SocketAddress sa = new InetSocketAddress(port);
         serverSocket.bind(sa, 20);
      }catch(IOException e){
         logger.error(e.getMessage(), e);
      }
   }

   public void run(){
      Socket socket = null;
      while(true){
         if(businessflag){
            try{
               socket = serverSocket.accept();
               System.out.println("New connection accepted " + socket.getInetAddress() + ":" + socket.getPort());
               InputStream socketIn = new BufferedInputStream(socket.getInputStream());
               byte tmpb = (byte)socketIn.read();
               byte[] currentbytes = null;
               if(tmpbytes.size() > 0){  //上一次IO流中有未处理的剩余包
                  int oldBytesLen = tmpbytes.get(0).length;
                  int socketBytesLen = socketIn.available()+1;
                  int currentLength = oldByteLen + socketBytesLen;
                  currentbytes = new byte[currentLength];
                  System.arraycopy(tmpbytes.get(0), 0, currentbytes, oldBytesLen);
                  currentbytes[oldBytesLen] = tmpb;
                  socketIn.read(currentbytes, oldBytesLen+1, socketBytesLen-1);
                  socketIn.close();
                  splitInputStreamByte(currentbytes);
               }else{  //正常未粘包情况
                  int socketBytesLen = socketIn.available()+1;
                  currentbytes = new byte[socketBytesLen];
                  currentbytes[0] = tmpb;
                  socketIn.read(currentbytes, 1, socketBytesLen-1);
                  socketIn.close();
                  splitInputStreamByte(currentbytes);
               }
            }catch(IOException e){
               logger.error(e.getMessage(), e);
            }
         }
      }
   }

   /**
    * 拆分byte数组并分多线程处理
    * @param parambytes 原byte数组
    * @return 处理后剩余部分的byte数组
   */
   private static void splitInputStreamByte(byte[] parambytes) {
      if(parambytes != null){
         if(parambytes.length > 4){
            byte[] head = new byte[4];  //单包长度
            System.arraycopy(parambytes, 0, head, 0, 4);
            int bodyLength = ByteArrayUtil.getint(head);
            if(bodyLength <= parambytes.length-4){
               final byte[] body = new byte[bodyLength];
               System.arraycopy(parambytes, 4, body, 0, bodyLength);
               ThreadPool.execute(new Runnable(){
                  public void run(){
                     byte[] processDatas = body;
                     try{
                        System.out.println(IOUtils.toString(processDatas, "UTF-8").trim());
                     }catch(IOException e){
                        logger.error(e.getMessage(), e);
                     }
                  }
               });

               int resultLen = parambytes.length-4-bodyLength;
               if(resultLen == 0){
                  splitInputStreamByte(null);
               }else{
                  byte[] resultbytes = new byte[resultLen];
                  System.arraycopy(parambytes, 4+bodyLength, resultbytes, 0, resultLen);
                  splitInputStreamByte(resultbytes);
               }
            }else{
               tmpbytes.clear();
               tmpbytes.add(parambytes);
            }
         }else{
            tmpbytes.clear();
            tmpbytes.add(parambytes);
         }
      }
   }

   public static void openflag(){
      businessflag = true;
   }

   public static void closeflag(){
      businessflag = false;
   }

}


TestTcpSocket.java代码:
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import com.shihuan.dragonkeeper.common.utils.ByteArrayUtil;
import com.shihuan.dragonkeeper.global.ConfigFile;

public class TestTcpSocket implements Runnable{
   private Socket socketClient = null;

   public void run(){
      String serverip = "192.168.1.10";
      int port = 19990;
      try{
         while(true){
           System.out.println("SocketClient start......");
           String mystr = "hello everyone!";
           socketClient = new Socket(serverip, port);
           OutputStream os = socketClient.getOutputStream();
           byte[] head = ByteArrayUtil.int2byte(mystr.length());
           byte[] body = mystr.getBytes();
           byte[] total = ByteArrayUtil.byteMerge(head, body);

           os.write(total);
           os.flush();
           os.close();

           Thread.sleep(1000);

           System.out.println("SocketClient end......");
         }
      }catch(Exception e){
         logger.error(e.getMessage(), e);
      }
   }

   public static void main(String[] args){
      Thread t = new Thread(new TestTcpSocket());
      t.start();
   }
} 


下面写ByteArrayUtil.java代码:
package com.shihuan.dragonkeeper.common.utils;

public class ByteArrayUtil {

   /**
    * 将int型的数据类型转换成byte[]类型
   */
   public static final byte[] int2byte(int paramInt){
      byte[] resultByte = new byte[4];
      resultByte[3] = ((byte)(paramInt & 0xFF));
      resultByte[2] = ((byte)(paramInt >>> 8 & 0xFF));
      resultByte[1] = ((byte)(paramInt >>> 16 & 0xFF));
      resultByte[0] = ((byte)(paramInt >>> 24 & 0xFF));

      return resultByte;
   }

   /**
    * 将byte型的数据类型转换成int类型
   */
   public static final int getint(byte[] paramArrayOfByte){
      int result = (paramArrayOfByte[0] & 0xFF) << 24 | (paramArrayOfByte[1] & 0xFF) << 16 | (paramArrayOfByte[2] & 0xFF) << 8 | paramArrayOfByte[3] & 0xFF;
      return result;
   }

   /**
    * 合并两个byte数组到一个byte数组中
   */
   public static byte[] byteMerge(byte[] byte1, byte[] byte2){
      byte[] result = new byte[byte1.length+byte2.length];
      System.arraycopy(byte1, 0, result, 0, byte1.length);
      System.arraycopy(byte2, 0, result, byte1.length, byte2.length);
      return result;
   }
}


http://blog.csdn.net/defonds/article/details/8782785
分享到:
评论
5 楼 wdloyeu 2016-11-12  
shihuan8@163.com邮箱网盘在哪,没找到。
能给份源码吗:发我的邮箱:2017-93-2088@qq.com
谢谢!
4 楼 开奔驰遛宝马 2016-08-31  
能分享一份源码吗? feng2400650@163.com
3 楼 romi0913 2016-07-13  
求源码,yannan_mail@163.com,拜托大神
2 楼 liu129jun 2016-06-05  
能给份源码吗?274474385@qq.com
1 楼 jethai 2015-08-17  
大神能不能给个源码,我的邮箱jethai@126.com

相关推荐

    Java 长链接示例

    JAVA实现长连接(含心跳检测)Demo,基于https://blog.csdn.net/zmx729618/article/details/54378259该作者的博文实现的代码示例。

    Java通过socket客户端保持连接服务端实现代码

    主要介绍了Java通过socket客户端保持连接服务端实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    websocket通信

    websocket通信,支持客户端和服务端互发信息,简单例子

    java源码包---java 源码 大量 实例

    Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密  Java非对称加密源程序代码实例,本例中使用RSA加密技术,...

    JAVA上百实例源码以及开源项目源代码

    Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密  Java非对称加密源程序代码实例,本例中使用RSA加密技术,...

    JAVA上百实例源码以及开源项目

    Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密  Java非对称加密源程序代码实例,本例中使用RSA加密技术,...

    如何解决线程太多导致java socket连接池出现的问题

    主要介绍了如何解决线程太多导致socket连接池出现的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    socket心跳功能

    客户端与服务端保持在线的心跳连接代码示例。一般用于保持socket长连接或者周期性上传客户端状态。

    java源码包2

    Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密  Java非对称加密源程序代码实例,本例中使用RSA加密技术...

    一个服务器对多个客户端的MFC Socket编程示例(实现简单的即时通讯功能)

    思想与Java中的编程思想一样,只不过Java中会使用多线程技术,在Vector集合保存客户端的socket对象,而MFC框架提供了CSocket类,它是一个异步通信的类,所以看上去代码比较Java的多线程代码简单的实现了一个对多的...

    java源码包4

    Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密  Java非对称加密源程序代码实例,本例中使用RSA加密技术...

    JAVA上百实例源码以及开源项目源代码 java开源包2

    Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密  Java非对称加密源程序代码实例,本例中使用RSA加密技术,...

    java源码包3

    Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密  Java非对称加密源程序代码实例,本例中使用RSA加密技术...

    java范例开发大全源代码

    第1篇 Java编程基础  第1章 Java开发环境的搭建(教学视频:9分钟) 2  1.1 理解Java 2  1.2 搭建Java所需环境 3  1.2.1 下载JDK 3  1.2.2 安装JDK 4  1.2.3 配置环境 5  1.2.4 测试JDK配置...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密 Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义...

    Java SE实践教程 源代码 下载

    Java SE实践教程 源代码 2010-9-13 声明:在网上找了好久才找到这个源码,真不容易啊,请大家多多支持。 2010-9-13 内容简介:此书结合具体实例讲解,通俗易懂,又不乏深度。我觉得这本书写的确实不错,堪称经典,...

    Java网络编程(第三版)中文版.part09.rar

    本书有很多示例,包含了几千行可以实际工作的代码(所有代码都可以在线获得),实现了功能完整的网络客户端和服务器。无论是希望编写特殊用途的web服务器、安全的在线订单接收程序、简单的组播代理还是电子邮件...

    Java范例开发大全 (源程序)

    第1篇 Java编程基础  第1章 Java开发环境的搭建(教学视频:9分钟) 2  1.1 理解Java 2  1.2 搭建Java所需环境 3  1.2.1 下载JDK 3  1.2.2 安装JDK 4  1.2.3 配置环境 5  1.2.4 测试JDK配置是否成功 7...

Global site tag (gtag.js) - Google Analytics