如何从Unity连接到数据库

11

我正在尝试通过Unity连接到MS SQL数据库。然而,当我尝试打开连接时,我遇到了一个IOException: Connection lost。

我已从Unity\Editor\Data\Mono\lib\mono\2.0导入了System.Data.dll。我正在使用以下代码:

 using UnityEngine;
 using System.Collections;
 using System.Data.Sql;
 using System.Data.SqlClient;

 public class SQL_Controller : MonoBehaviour {

     string conString = "Server=myaddress.com,port;" +
             "Database=databasename;" +
             "User ID=username;" +
             "Password=password;";

     public string GetStringFromSQL()
     {
         LoadConfig();
         string result = "";

         SqlConnection connection = new SqlConnection(conString);
         connection.Open();
         Debug.Log(connection.State);
         SqlCommand Command = connection.CreateCommand();
         Command.CommandText = "select * from Artykuly2";
         SqlDataReader ThisReader = Command.ExecuteReader();
         while (ThisReader.Read())
         {
             result = ThisReader.GetString(0);
         }
         ThisReader.Close();
         connection.Close();

         return result;
     }
 }

我收到的错误信息如下:

IOException: Connection lost
Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacketHeader ()
Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacket ()
Mono.Data.Tds.Protocol.TdsComm.GetByte ()
Mono.Data.Tds.Protocol.Tds.ProcessSubPacket ()
Mono.Data.Tds.Protocol.Tds.NextResult ()
Mono.Data.Tds.Protocol.Tds.SkipToEnd ()
Rethrow as TdsInternalException: Server closed the connection.
Mono.Data.Tds.Protocol.Tds.SkipToEnd ()
Mono.Data.Tds.Protocol.Tds70.Connect (Mono.Data.Tds.Protocol.TdsConnectionParameters connectionParameters)
Mono.Data.Tds.Protocol.Tds80.Connect (Mono.Data.Tds.Protocol.TdsConnectionParameters connectionParameters)
请忽略这种方法可能存在的安全风险,我需要用它来进行测试,安全问题稍后再处理。 感谢您的时间。
3个回答

26
请忽略此方法可能存在的安全风险。不要像这样做。无论安全性是前还是后,您最终都将重新编写整个代码,因为密码是硬编码在应用程序中的,可以轻松反编译和检索。现在以正确的方式进行连接,这样您就不必重新编写整个应用程序。使用php、perl或者您熟悉的任何语言在服务器上运行数据库命令。

从Unity开始,使用WWWUnityWebRequest类与该脚本通信,然后,您将能够在Unity和服务器之间发送和接收信息。有许多示例可供参考。即使如此,您仍需要实现自己的安全性,但这比您现在拥有的要好得多。

您还可以使用json接收多个数据。

以下是Unity wiki的完整示例。它展示了如何在客户端使用Unity + C#和服务器端php交互数据库。

服务器端:

使用PDO添加分数:

<?php
        // Configuration
        $hostname = 'localhot';
        $username = 'yourusername';
        $password = 'yourpassword';
        $database = 'yourdatabase';

        $secretKey = "mySecretKey"; // Change this value to match the value stored in the client javascript below 

        try {
            $dbh = new PDO('mysql:host='. $hostname .';dbname='. $database, $username, $password);
        } catch(PDOException $e) {
            echo '<h1>An error has ocurred.</h1><pre>', $e->getMessage() ,'</pre>';
        }

        $realHash = md5($_GET['name'] . $_GET['score'] . $secretKey); 
        if($realHash == $hash) { 
            $sth = $dbh->prepare('INSERT INTO scores VALUES (null, :name, :score)');
            try {
                $sth->execute($_GET);
            } catch(Exception $e) {
                echo '<h1>An error has ocurred.</h1><pre>', $e->getMessage() ,'</pre>';
            }
        } 
?>

使用PDO检索分数

<?php
    // Configuration
    $hostname = 'localhost';
    $username = 'yourusername';
    $password = 'yourpassword';
    $database = 'yourdatabase';

    try {
        $dbh = new PDO('mysql:host='. $hostname .';dbname='. $database, $username, $password);
    } catch(PDOException $e) {
        echo '<h1>An error has occurred.</h1><pre>', $e->getMessage() ,'</pre>';
    }

    $sth = $dbh->query('SELECT * FROM scores ORDER BY score DESC LIMIT 5');
    $sth->setFetchMode(PDO::FETCH_ASSOC);

    $result = $sth->fetchAll();

    if(count($result) > 0) {
        foreach($result as $r) {
            echo $r['name'], "\t", $r['score'], "\n";
        }
    }
?>

在服务器上启用跨域策略:

该文件应命名为“crossdomain.xml”,并放置在您的Web服务器的根目录中。Unity要求您想通过WWW请求访问的网站具有跨域策略。

<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>

客户端/Unity端:

Unity的客户端代码连接到服务器,与PDO进行交互,并根据调用的函数添加或检索分数。这个客户端代码稍作修改就可以编译最新的Unity版本。

private string secretKey = "mySecretKey"; // Edit this value and make sure it's the same as the one stored on the server
public string addScoreURL = "http://localhost/unity_test/addscore.php?"; //be sure to add a ? to your url
public string highscoreURL = "http://localhost/unity_test/display.php";

//Text to display the result on
public Text statusText;

void Start()
{
    StartCoroutine(GetScores());
}

// remember to use StartCoroutine when calling this function!
IEnumerator PostScores(string name, int score)
{
    //This connects to a server side php script that will add the name and score to a MySQL DB.
    // Supply it with a string representing the players name and the players score.
    string hash = Md5Sum(name + score + secretKey);

    string post_url = addScoreURL + "name=" + WWW.EscapeURL(name) + "&score=" + score + "&hash=" + hash;

    // Post the URL to the site and create a download object to get the result.
    WWW hs_post = new WWW(post_url);
    yield return hs_post; // Wait until the download is done

    if (hs_post.error != null)
    {
        print("There was an error posting the high score: " + hs_post.error);
    }
}

// Get the scores from the MySQL DB to display in a GUIText.
// remember to use StartCoroutine when calling this function!
IEnumerator GetScores()
{
    statusText.text = "Loading Scores";
    WWW hs_get = new WWW(highscoreURL);
    yield return hs_get;

    if (hs_get.error != null)
    {
        print("There was an error getting the high score: " + hs_get.error);
    }
    else
    {
        statusText.text = hs_get.text; // this is a GUIText that will display the scores in game.
    }
}

public string Md5Sum(string strToEncrypt)
{
    System.Text.UTF8Encoding ue = new System.Text.UTF8Encoding();
    byte[] bytes = ue.GetBytes(strToEncrypt);

    // encrypt bytes
    System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
    byte[] hashBytes = md5.ComputeHash(bytes);

    // Convert the encrypted bytes back to a string (base 16)
    string hashString = "";

    for (int i = 0; i < hashBytes.Length; i++)
    {
        hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');
    }

    return hashString.PadLeft(32, '0');
}

这只是一个正确实现的示例。如果您需要实现会话功能并关心安全性,请查看OAuth 2.0协议。应该存在现有的库,可以帮助开始使用OAuth协议。

我已经成功地根据你的第一个示例建立了连接,并且能够从表中进行POST和GET操作。谢谢你。 - MLazowski
太棒了。很高兴你没有坚持那种方式去做。 - Programmer

1
另一个选择是在命令提示符中创建自己的专用服务器来进行通信,并将其连接到Unity以处理多人游戏和SQL通信。这样你就可以坚持使用同一种语言来创建所有内容。但这需要相当陡峭的学习曲线。

-1

Unity是游戏引擎。

所以答案说得没错。

但是,有些领域需要直接连接数据库。

在游戏领域中不应该直接访问数据库。

无论如何,问题是由非英语计算机名称引起的。

我在之前的项目中遇到了类似以下错误。

IOException: Connection lost
Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacketHeader ()
Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacket ()
Mono.Data.Tds.Protocol.TdsComm.GetByte ()
Mono.Data.Tds.Protocol.Tds.ProcessSubPacket ()
Mono.Data.Tds.Protocol.Tds.NextResult ()
Mono.Data.Tds.Protocol.Tds.SkipToEnd ()
Rethrow as TdsInternalException: Server closed the connection.
Mono.Data.Tds.Protocol.Tds.SkipToEnd ()
Mono.Data.Tds.Protocol.Tds70.Connect (Mono.Data.Tds.Protocol.TdsConnectionParameters connectionParameters)
Mono.Data.Tds.Protocol.Tds80.Connect (Mono.Data.Tds.Protocol.TdsConnectionParameters connectionParameters)

在将计算机名称更改为ENGLISH后,它可以正常工作。

我不知道它是如何运作的。但它可以正常工作。

Mono的System.Data.dll在具有NON-ENGLISH计算机名称的PC上存在一些问题。

所以,至少对于Unity项目。

您应该告诉客户不要将计算机名称设置为NON-ENGLISH。

我不知道mono中的人是否知道这些问题。

---------- 在2018版本之后,它可以正常工作 ----------

Api兼容级别>.Net 4.x

您可以在非英语计算机名称的计算机上连接数据库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接