Java連接遠程Mysql過程中遇到的各種問題


2018-11-16 10:46

2018-11-19 21:35

 

前言

  本篇文章記錄的是本人在使用Java程序連接另一台電腦(同一局域網)上的Mysql數據庫的過程中遇到的各種問題及解決方案。希望能給遇到同樣問題的人提供一些幫助。當初本人遇到這些問題的時候,也是在各位大神的博客中找到的答案,本篇博客在某種程度上也算是這幾個問題的答案集合吧。

 

軟件環境

  客戶端: Windows10  +  Java    [ IP: 192.168.76.10 ]

  數據庫服務端: Ubuntu 16.04  +  Mysql 14.14      [  IP: 192.168.76.1 ]

  服務端的Mysql就是用最普通的 apt-get install 命令來安裝的。安裝完以后沒有其它任何設置操作。

 

客戶端使用的Java代碼

 1         final String driver = "org.gjt.mm.mysql.Driver";
 2         final String url = "jdbc:mysql://192.168.76.1:3306/mysql";
 3         final String user = "root";
 4         final String password = "123456";
 5         Connection conn = null;
 6         PreparedStatement pstmt = null;
 7         ResultSet rs = null;
 8         
 9         try{
10             Class.forName(driver);
11             conn = DriverManager.getConnection(url,user,password);
12             String sql = "SELECT * FROM user"; 
13             pstmt = conn.prepareStatement(sql);
14             rs = pstmt.executeQuery();
15             while(rs.next()){
16                 String h = rs.getString(1);
17                 String u = rs.getString(2);
18                 System.out.println("host:" + h + ",user:" + u);
19             }
20         }catch(Exception e){
21             e.printStackTrace();
22         }

 

  數據庫服務端確認Mysql正常運行后,便可以嘗試運行上面的代碼去連接了。此時,便有幾率遇到下面一系列問題。

 

問題一:報“連接超時”錯誤

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:981)
    at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:339)
    at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2253)
    at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2286)
    at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2085)
    at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:795)
    at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:44)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
    at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:400)
    at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:327)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at Main.init(Main.java:25)
    at Main.main(Main.java:10)
Caused by: java.net.ConnectException: Connection timed out: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:211)
    at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:298)
    ... 16 more

  這種問題出現的幾率比較小,本人也是在一次測試中偶然發現有這個問題的。如果很不幸你遇到了這個問題,那想也不用想,原因肯定是客戶端到數據庫服務端的網絡不通。網絡不通的原因就多種多樣了,解決辦法也因問題根源而異。本文也沒辦法羅列各種導致網絡不通的情況並給出解決辦法。只能講個大概。

  1、確認兩台機器是否處於同一局域網段(本文討論的環境就是基於同一網段的遠程訪問);

  2、檢查客戶端防火牆;

  3、檢查路由器;

  4、檢查數據庫服務器防火牆;

  5、檢查Mysql服務端程序運行情況及端口監聽情況;

  6、檢查服務器端防火牆;

  一般問題的答案都包含在上述幾個步驟中了。上述步驟6中檢查Linux的防火牆除了檢查 ufw 以外還一定要檢查一下 iptables 。本人遇到這個問題的原因就是因為在 iptables 中配置了禁止外部數據進入Linux的規則。關於 ufw 與 iptables 的配置,大家要根據自己的實際需要來獨立配置,同時配置Linux防火牆也比較復雜,這部分知識就不在本文的討論范圍之內了。本人的ubuntu是完全放開 iptables 限制的,並且還把 ufw 卸載掉了。

 

 

問題二:報“拒絕連接”錯誤

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:981)
    at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:339)
    at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2253)
    at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2286)
    at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2085)
    at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:795)
    at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:44)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
    at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:400)
    at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:327)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at Main.init(Main.java:24)
    at Main.main(Main.java:10)
Caused by: java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:211)
    at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:298)
    ... 16 more

  從這條錯誤異常中我們知道是Mysql服務拒絕了我們的連接請求,這至少說明我們客戶端與服務端是能夠成功建立通信的,和網絡無關。問題可能就出在Mysql的安全配置上了。我們先來看看服務端Mysql端口的監聽情況。

netstat -ntlp | grep 3306

圖1  服務端Mysql端口監聽情況

  由上圖1可知,目前我們的Mysql服務端僅監聽來自ubuntu本機3306端口上的數據庫請求。這也就是我們從遠端嘗試連接時報出一個拒絕連接錯誤的原因。解決的方案也很清晰:取消僅監聽本機3306端口的方式,改為監聽指定目標網址或者監聽全網址。

  我這邊發現,可能不同版本的Mysql關於設置監聽網址的位置不一樣。但都在Mysql程序的安裝目錄下。所以這里推薦使用grep命令來查找自己的Mysql程序是在哪個文件中設置了綁定IP監聽的。

  我這邊是通過apt-get命令安裝的Mysql,因此,先進到下面目錄下

/etc/mysql

  然后搜索哪里有綁定127.0.0.1的網址

grep -nr "127.0.0.1"

圖2  Mysql綁定網址的設定

  由上圖2,直接去修改mysql.cnf文件即可。

圖3  bind-address的描述

  由上圖3所示,這個bind-address默認設為監聽回環地址的目的是為了兼容與安全。這里我們可管不了這么多,直接將它改成監聽我們ubuntu服務器本機的固定IP地址,如上圖所示。 這個192.168.76.1是我在自己的路由器上設置過的,固定分配給這台ubuntu機器使用所以才這么寫,你們可以將它換成你自己的IP地址。如果不想寫死,直接將bind-address屬性注釋掉即可

  修改完保存后要重啟一下Mysql服務。

service mysql restart

圖4  重啟Mysql服務

  重啟完成后可以再查詢一下3306端口的監聽情況以確定修改成功與否。

圖5  再次確認3306端口的監聽地址

  此時,我們再運行一下我們的Java程序,不出意外,就能發現先前的“拒絕連接”異常已經沒有了。但是,它又報了另一個異常。 ^_^

 

 

問題三:報“主機不被允許訪問”

java.sql.SQLException: null,  message from server: "Host '192.168.76.10' is not allowed to connect to this MySQL server"
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:957)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:896)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:885)
    at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1038)
    at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2255)
    at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2286)
    at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2085)
    at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:795)
    at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:44)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
    at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:400)
    at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:327)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at Main.init(Main.java:24)
    at Main.main(Main.java:10)

  上面的異常信息講的也夠直白。客戶端不被允許接入到服務端中去。我們的Mysql服務在默認情況下確實是不接受任何外部主機的訪問請求的。這點可以查我們Mysql中的配置數據得知。

1、進入到mysql控制台。 本人是通過 mysql -u root -p 的方式進入的。

2、use mysql

3、select*from user;

圖6  Mysql中默認的允許接入帳戶

  由上圖6可知,在本人的Mysql默認配置中,僅三個帳戶被允許訪問,分別是:1、root; 2、mysql.sys; 3、debian-sys-maint; 並且這三個帳戶均綁定為在ubuntu本機上登錄才擁有接入權限。

  那么,解決這個外部主機沒權限接入數據庫的方案一般有三種:1、直接通過update語句修改user表中現有的信息,使僅監聽localhost變為監聽全網址或指定網址。 2、 通過insert語句在user表中新增項,增加允許訪問的帳戶及主機域名。  3、通過grant語句新增允許訪問的帳戶及主機域名。  在這三種方式中,前面兩種本人極力不推薦並且鄙視這兩種做法。這里僅介紹一下第3種解決方案。

  可以在Mysql控制台上執行下面語句:

GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.76.10' IDENTIFIED BY '123456' WITH GRANT OPTION;

  上面的語句表示允許IP為“192.168.76.10”的主機通過“root”帳戶以“123456”為密碼訪問Mysql數據庫。

  如果不想寫死主機IP,可以改用下面的語句:

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;

  如果不想使用密碼,可以使用下面的語句:

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;

  

  執行完上面的語句后,一定不要忘記還要再執行一下刷新配置的語句

FLUSH PRIVILEGES;

 

  當上面的語句都執行成功以后,可以再查詢一下user表中的信息以確認操作是否成功。

圖7  確認user表

  上圖7中展示了我已經給我的客戶端主機開放了權限。現在我們再去跑一下Java程序。

圖8  運行成功

  終於成功從遠端主機上查詢到了服務端上Mysql的數據了。

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM