线程化同步XNA网络

时间:2012-02-21 14:37:23

标签: c# multithreading xna

我一直在研究一个我认为非常微不足道的话题。到目前为止,我空手而归,想看看你们的想法。我正在搞乱XNA(现在我认为它实际上是无关紧要的)为“游戏”项目构建客户端/服务器架构。真的只不过是在玩网络,动画等等。

无论如何,我已经阅读了很多关于同步和异步网络之间的差异以及线程同步应用程序来模拟异步行为的可行性,并决定这样做。现在我知道我的代码并不漂亮,但我现在正在测试。以下是它的设置方式:

游戏在主线程中运行。 在初始化 - >连接到服务器。 将玩家类的精灵对象的x位置发送到服务器。 服务器接收,通过打印到控制台确认并发回相同的数据。 数据被读入日志文件。

我已经开始研究一个信使类,它最终将读取(或嗅探)来自服务器的数据包并相应地发送它们,根据需要进行绘制/更新调用。我的问题是,我无法弄清楚如何正确地连接连接方法以使该运行(然后阻止?)与发送/接收循环(或我想成为连续循环)分开。

就像我说我不是专家一样,我只是为了好玩而这样做,所以我可能到处都是。无论如何,这里是代码的基本要素:

Networking.cs

using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;


namespace DrawTest
{
class Networking
{
    public void StartClient(Messenger m)
    {

    // Data buffer for incoming data.
    StreamWriter _con = new StreamWriter("data.txt");


    // Connect to a remote device.
    try {
        // Establish the remote endpoint for the socket.
        // This example uses port 11000 on the local computer.


        IPHostEntry ipHostInfo = Dns.Resolve("127.0.0.1");
        IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPEndPoint remoteEP = new IPEndPoint(ipAddress,3000);

        // Create a TCP/IP  socket.
        Socket sender = new Socket(AddressFamily.InterNetwork, 
            SocketType.Stream, ProtocolType.Tcp );

        // Connect the socket to the remote endpoint. Catch any errors.
        try {

            sender.Connect(remoteEP);

            _con.WriteLine("Socket connected to {0}",
                sender.RemoteEndPoint.ToString());
            while (m.isAlive)
            {
                m.SocketStream(sender, _con);
            }

            if (Messenger.mPacket() == "close_socket")
            {
                _con.WriteLine("Connection closed by client.");
                sender.Shutdown(SocketShutdown.Both);
                sender.Close();
            }

        } catch (ArgumentNullException ane) {
            _con.WriteLine("ArgumentNullException : {0}",ane.ToString());
        } catch (SocketException se) {
            _con.WriteLine("SocketException : {0}",se.ToString());
        } catch (Exception e) {
            _con.WriteLine("Unexpected exception : {0}", e.ToString());
        }
        _con.Flush();
    } 

    catch (Exception e) {
            _con.WriteLine(e.ToString());
            _con.Flush();
        }
    }
}
}

Messenger.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace DrawTest
{
public class Messenger
{
    byte[] bytes = new byte[1024];
    byte[] incBuffer = new byte[1024];
    public bool isAlive = true;
    Vector2 position = new Vector2(0.0f, 0.0f);

    public Vector2 getPos()
    {
        return position;
    }
    public void setPos(Vector2 pos)
    {
        position = pos;
    }

    public void SocketStream(Socket s, StreamWriter logfile)
    {
        byte[] msg = null;
        int bytesSent = 0;
        int bytesRec = 0;
        msg = BitConverter.GetBytes(position.X);
        // Encode the data string into a byte array.
        bytesSent = s.Send(msg);

        // Receive the response from the remote device.
        bytesRec = s.Receive(incBuffer);
        //logfile.WriteLine(Messenger.mDecoder(incBuffer, bytesRec));
    }

    public string mDecoder(byte[] msg, int size)
    {
        string DecodedMessage;
        byte[] bytes = new byte[1024];
        DecodedMessage = Encoding.ASCII.GetString(msg, 0, size);
        if (DecodedMessage == "close_socket")
        {
            isAlive = false;
            return DecodedMessage;
        }
        return DecodedMessage;            
    }

    public static string mPacket()
    {
        return null;
    }

}
}

认为应该这样做。其他代码相对不言自明(抽象播放器/精灵类和典型的XNA Game.cs)

提前感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

您可以采取以下措施:

public void SendData(Socket s)
{
    byte[] msg = null;
    int bytesSent = 0;

    msg = BitConverter.GetBytes(position.X);

    // Encode the data string into a byte array.
    bytesSent = s.Send(msg);

}

void ReceiveData(Socket s)
{
    int bytesExpected = 1024; // somehow specify the number of bytes expected
    int totalBytesRec = 0; // adds up all the bytes received
    int bytesRec = -1; // zero means that you're done receiving

    while(bytesRec != 0 && totalBytesRec <  bytesExpected )
    {
        // Receive the response from the remote device.
        bytesRec = s.Receive(incBuffer);
        totalBytesRec += bytesRec;
    }
}

回到StartClient类,首先应该启动接收线程,然后发送数据:

// Start your receive thread first
Thread t = new Thread(()=>{ReceiveData(sender);});
t.IsBackground = true;
t.Start();

// Then send the data
SendData(sender);

// Wait for the thread to terminate (if you need to)
t.Join(30000);

// Once you close the socket, then it will throw an exception
// in the receive thread (which you should catch) and you can
// exit the thread, thus terminating the thread.

这大致是你如何启动执行接收的线程。

更新(基于评论)

我建议您查看一些Patterns for Multithreaded Network Server in C#

服务器端应该为接受的每个客户端连接启动一个新线程,并且“连接处理程序”应该接管并管理从那里发送/接收数据:

while(serverRunning)
{
    Socket clientSocket = serverSocket.Accept();

    // You can write your own connection handler class that automatically
    // starts a new ReceiveData thread when it gets a client connection
    ConnectionHandler chandler = new ConnectionHandler(clientSocket);

    // Have an on-client-disconnected event which you can subscribe to
    // and remove the handler from your list when the client is disconnected
    chandler.OnClinetDisconnectedEvent += new OnClientDisconnectedDelegate(OnClientDisconnected);

    mHandlerList.Add(chandler);

}

// When you're terminating the program, then just go through 
// the list of active ConnectionHandlers and call some method
// which tells them to close their connections with the clients
// and terminates the thread.

更准确地说,您可能与客户端和服务器的ReceiveData方法具有非常相似的行为:即,只要收到某些消息,就会同步发送消息。这是一个更实际的例子,可以帮助您更好地概念化它:

void ReceiveData(Socket s)
{
    int bytesExpected = 1024; // somehow specify the number of bytes expected
    int totalBytesRec = 0; // adds up all the bytes received
    int bytesRec = -1; // zero means that you're done receiving

    while(bytesRec != 0 && totalBytesRec <  bytesExpected )
    {
        // Receive the response from the remote device.
        bytesRec = s.Receive(incBuffer);
        totalBytesRec += bytesRec;

        if(needToReply)
        {
            // Send another message
            SendData(s);
        }
    }
}

这是一个长期运行的线程,当然,只要播放器连接到互联网,您通常会希望它运行。关闭连接和终止线程的注释特别适用于需要优雅退出的情况(即玩家退出游戏或者服务器需要关闭的极少数情况)。