How to connect to database from Unity

2019-01-01 04:24发布

问题:

I am trying to connect to a MS SQL database through Unity. However, when I try to open a connection, I get an IOException: Connection lost.

I have imported System.Data.dll from Unity\\Editor\\Data\\Mono\\lib\\mono\\2.0. I am using the following code:

 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;
     }
 }

This is the error I get:

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)

Please disregard any security risks with this approach, I NEED to do this for testing, security will come later. Thank you for your time.

回答1:

Please disregard any security risks with this approach

Do not do it like this. It doesn\'t matter if security will come before or after. You will end of re-writing the whole code because the password is hard-coded in your application which can be decompiled and retrieved easily. Do the connection the correct way now so that you won\'t have to re-write the whole application.

Run your database command on your server with php, perl or whatever language you are comfortable with but this should be done on the server.

From Unity, use the WWW or UnityWebRequest class to communicate with that script and then, you will be able to send and receive information from Unity to the server. There are many examples out there. Even with this, you still need to implement your own security but this is much more better than what you have now.

You can also receive data multiple with json.

Below is a complete example from this Unity wiki. It shows how to interact with a database in Unity using php on the server side and Unity + C# on the client side.

Server Side:

Add score with 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>\';
            }
        } 
?>

Retrieve score with 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\";
        }
    }
?>

Enable cross domain policy on the server:

This file should be named \"crossdomain.xml\" and placed in the root of your web server. Unity requires that websites you want to access via a WWW Request have a cross domain policy.

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

Client/Unity Side:

The client code from Unity connects to the server, interacts with PDO and adds or retrieves score depending on which function is called. This client code is slightly modified to compile with the latest Unity version.

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\');
}

This is just an example on how to properly do this. If you need to implement session feature and care about security, look into the OAuth 2.0 protocol. There should be existing libraries that will help get started with the OAuth protocol.



回答2:

An alternative would be to create your own dedicated server in a command prompt to do your communication and connecting it to unity to Handel multiplayer and SQL communication. This way you can stick with it all being created in one language. But a pretty steep learning curve.