1

これは、MoSync アプリケーションと外部 DLL 間のローカル通信用です。MoSync ではサード パーティの DLL を使用できません。そのため、DLL への単純な呼び出しを使用する代わりに、このブリッジ ソフトウェアを実装する必要があります。 xml から DLL メッセージ形式に変換し、再度 XML に変換します。残念ながら、アーキテクチャを変更する柔軟性はありません。最初はリクエストが1つしかないと思っていたのでSync comsを持っていましたが、複数のリクエストが存在する可能性があることがわかったので、Asyncを再度実装する必要があります。

私は C# を初めて使用するため、時々スローされる例外があります。メモリ リークを見つけることができません。

ソースコード:

私は次のコードを書きました。私は C# とソケットにまったく慣れていないので、経験豊富な目だけが検出できる大きな間違いを犯した可能性があります。これは Windows Mobile 6.1 デバイスで使用されるため、多くのスレッドを使用しないようにしています。

using System;

using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;

namespace SmartDevice_Server
{
    //ClientConnection saves connection information is used to keep context in Async and Event calls
    public class ClientConnection : EventArgs
    {
        public NetworkStream NetworkStream { get; private set; }
        public byte[] Data { get; private set; }
        public int byteReadCount { get; set; }

        public ClientConnection(NetworkStream networkStream, byte[] data)
        {
            NetworkStream = networkStream;
            Data = data;
        }
    }

    //MySocket - Is a server that listens for events and triggers Events upon Request Completion 
    public class MySocketTCP
    {
        #region Class Members
        TcpListener myTcpListener;
        TcpClient myTcpClient;
        NetworkStream myNetworkStream;

        const string localHost = "127.0.0.1";
        IPAddress myAddress = IPAddress.Parse(localHost);
        int myPortNumber = 58889;
        byte[] myData;

        int bytesReadCount;
        const int MIN_REQUEST_STRING_SIZE = 10;

        int TimeStart;

        //Event
        public event socketReadCompleteHandler socketReadCompleteEvent;
        public EventArgs eventArguments = null;
        public delegate void socketReadCompleteHandler(MySocketTCP myTcpSocket, ClientConnection eventArguments);

        #endregion

        //Constructor
        public MySocketTCP()
        {
            Init();
        }

        //Constructor overloaded to receive IPAdress Host, and Port number
        public MySocketTCP(IPAddress hostAddress, int portNumber)
        {
            myAddress = hostAddress;
            myPortNumber = portNumber;

            Init();
        }

        //Initializes the TCPListner
        public void Init()
        {
            try
            {
                myTcpListener = new TcpListener(myAddress, myPortNumber);

                //myNetworkStream = myTcpClient.GetStream();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /*TODO_Listener_Timer: After you accept a connection you wait for data to be Read indefinitely
         *Possible solution: Use a timeout to close the socket connection.
         *Check WIKI, TODOS
         * */
        //Listens Asynchronously to Clients, class a recieveMessageHandler to process the read
        public void ListenAsync()
        {
            myTcpListener.Start();

            while (true)
            {
                //blocks until a client has connected to the server
                myTcpClient = myTcpListener.AcceptTcpClient();

                var client = new ClientConnection(myTcpClient.GetStream(), new byte[myTcpClient.ReceiveBufferSize]);

                // Capture the specific client and pass it to the receive handler
                client.NetworkStream.BeginRead(client.Data, 0, client.Data.Length, r => receiveMessageHandler(r, client), null);
            }
        }

        //Callback is used to Process the request Asynchronously, triggers socketReadCompleteEvent
        public void receiveMessageHandler(IAsyncResult asyncResult, ClientConnection clientInstance)
        {
            bytesReadCount = 0;

            lock (clientInstance.NetworkStream)
            {
                try
                {
                    bytesReadCount = clientInstance.NetworkStream.EndRead(asyncResult);
                    clientInstance.byteReadCount = bytesReadCount;
                }
                catch (Exception exc)
                {
                    throw exc;
                }
            }

            if (bytesReadCount < MIN_REQUEST_STRING_SIZE)
            {
                //Could not read form client.
                Debug.WriteLine("NO DATA READ");
            }
            else
            {
                if (socketReadCompleteEvent != null)
                {
                    socketReadCompleteEvent(this, clientInstance);
                }
            }
        }

        //Reads the request, uses the ClientConnection for context
        public string ReadAsync(ClientConnection connObj)
        {
            int bytesReadCount = connObj.byteReadCount;
            byte[] myData = connObj.Data;

            string xmlMessage;

            try
            {
                xmlMessage = Encoding.ASCII.GetString(myData, 0, bytesReadCount);
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return xmlMessage;
        }

        //Deprecated
        public string Read()
        {
            string xmlMessage;

            try
            {
                xmlMessage = Encoding.ASCII.GetString(myData, 0, bytesReadCount);
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return xmlMessage;
        }

        //Deprecated
        public void Write(byte[] outBytes)
        {
            try
            {
                myNetworkStream.Write(outBytes, 0, outBytes.Length);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        //Deprecated
        public void Write(string outMessage)
        {
            byte[] outBytes = Encoding.ASCII.GetBytes(outMessage);

            try
            {
                myNetworkStream.Write(outBytes, 0, outBytes.Length);
            }
            catch (Exception ex)
            {
                throw ex;
            }

            int TimeEnd = Environment.TickCount;
            int TimeResult = TimeEnd - TimeStart;
        }

        //Is used to send the message to the correct socket
        public void WriteAsync(ClientConnection connObj, string outMessage)
        {
            byte[] outBytes = Encoding.ASCII.GetBytes(outMessage);

            try
            {
                connObj.NetworkStream.Write(outBytes, 0, outBytes.Length);
            }
            catch (Exception ex)
            {
                throw ex;
            }

            int TimeEnd = Environment.TickCount;
            int TimeResult = TimeEnd - TimeStart;
        }

        //Closes the client
        public void Close()
        {
            //myNetworkStream.Close();
            try
            {
                myTcpClient.Close();
            }
            catch (Exception ex)
            {

                throw ex;
            }
        }
    }
}
4

2 に答える 2

6

最も可能性の高い問題は、クライアントが行った 3 回の「書き込み」に対して正確に 3 回の「読み取り」を行うことを期待していることです。

TCP ソケットはバイト ストリームであり、アプリケーション メッセージの境界を保持しないため、これは間違った想定です。サーバーは、クライアントから送信された 3 つの「メッセージ」を 1 回、2 回、または 17 回の読み取りで消費する可能性があります。

メッセージがバイトストリームのどこで終了するかをサーバーに伝える必要があります。通常の選択肢は、固定長のメッセージ、デリミタ、ペイロードの長さを示すメッセージ ヘッダー、XML のような自己記述形式などです。

したがって、処理する完全なメッセージが得られるまでストリームからの読み取りを続けますが、同時に、次のメッセージの一部が既にバッファーに読み込まれている可能性があります。

于 2012-12-19T17:22:10.800 に答える
3

NetworkStreamここでの問題は、単一の( )しか保持していないことだと思います。myNetworkStream最初のクライアントがデータを送信する前に 2 番目のクライアントが接続すると、受け入れループがmyNetworkStream2 番目の接続のストリームで上書きされます。最初のクライアントがデータを送信すると、2 番目の接続 (2 番目のクライアントが接続したときに格納された) で呼び出されます、1receiveMessageHandler番目EndReadクライアントの読み取りから渡されます。これにより、指定した例外が発生します。具体的にテストしたところ、次のメッセージが表示されました。NetworkStreammyNetworkStreamasyncResult

トランスポート接続からデータを読み取れません: このクラスの対応する非同期メソッドから IAsyncResult オブジェクトが返されませんでした。パラメータ名: asyncResult.

次の変更を加えてみてください。

// Create a class to hold details related to a client connection
public class ClientConnection
{
    public ClientConnection(NetworkStream networkStream, byte[] data)
    {
        NetworkStream = networkStream;
        Data = data;
    }

    public NetworkStream NetworkStream { get; private set; }
    public byte[] Data { get; private set; }
}

public void Listen()
{
    myTcpListener.Start();

    while (true)
    {
        //blocks until a client has connected to the server
        myTcpClient = myTcpListener.AcceptTcpClient();

        var client = new ClientConnection(myTcpClient.GetStream(), new byte[myTcpClient.ReceiveBufferSize]);

        // Capture the specific client and pass it to the receive handler
        client.NetworkStream.BeginRead(client.Data, 0, client.Data.Length, r => receiveMessageHandler(r, client), null);
    }
}

public void receiveMessageHandler(IAsyncResult asyncResult, ClientConnection client)
{
    var byteReadCount = client.NetworkStream.EndRead(asyncResult);

    if (byteReadCount < MIN_REQUEST_STRING_SIZE)
    {
        //Could not read form client.
        //Erro - Como tratar? Close()
    }
    else
    {
        if (socketReadCompleteEvent != null)
        {
            socketReadCompleteEvent(this, eventArguments);
        }
    }
}

他の人が言及しているように、送信者と受信者の間で読み取り/書き込みが一致するという期待に関連する追加の問題がありますが、これが実際の問題の原因のようです。

編集:

callback以下は、データを受信し、完全なメッセージを受信したときにメソッドを呼び出すサーバーです。は文字列を返します。このcallback文字列はクライアントに送り返され、クライアントreplyCallbackは応答データを使用して自分自身を呼び出します。接続ごとに 1 つの要求応答のみが送信されます (これはかなり非効率的ですが、出発点として役立つはずです)。

public static class Server
{
    public static void Run(int port, Action<string> callback)
    {
        var listener = new TcpListener(IPAddress.Loopback, port);
        listener.Start();

        while (true)
        {
            using (var client = listener.AcceptTcpClient())
            {
                try
                {
                    var buffer = new byte[2048];
                    using (var memoryStream = new MemoryStream())
                    {
                        using (var stream = client.GetStream())
                        {
                            stream.ReadTimeout = 1000; // 1 second timeout
                            int bytesRead;
                            // Loop until Read returns 0, signalling the socket has been closed
                            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                memoryStream.Write(buffer, 0, bytesRead);
                            }
                        }

                        // Pass the client's message to the callback and use the response as the reply message to the client.
                        var reply = Encoding.UTF8.GetBytes(callback(Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length)));
                        stream.Write(reply, 0, reply.Length);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error: {0}", e.Message);
                }
            }
        }
    }
}

これは、接続してデータを送信し、応答を待つ小さなクライアント プログラムです。応答が受信されるreplyCallbackと、サーバーの応答とともに呼び出しが渡されます。

public static class Client
{
    public static void Run(string hostname, int port, string dataToSend, Action<string> replyCallback)
    {
        using (var client = new TcpClient(hostname, port))
        {
            using (var stream = client.GetStream())
            {
                var buffer = Encoding.UTF8.GetBytes(dataToSend);
                stream.Write(buffer, 0, buffer.Length);
                // Shutdown the send side of the socket, indicating to the server we're done sending our message
                client.Client.Shutdown(SocketShutdown.Send);
                using (var memoryStream = new MemoryStream())
                {
                    stream.ReadTimeout = 1000; // 1 second timeout
                    int bytesRead;
                    // Loop until Read returns 0, signalling the socket has been closed
                    while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        memoryStream.Write(buffer, 0, bytesRead);
                    }
                    replyCallback(Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length));
                }
            }
        }
    }
}

そして、すべてを結び付けるための小さなテストハーネス:

static class Program
{
    static void Main(string[] args)
    {
        var port = 12345;
        ThreadPool.QueueUserWorkItem(o => Server.Run(port, ProcessClientMessage));
        while (true)
        {
            Console.WriteLine("Enter a message to send and hit enter (or a blank line to exit)");
            var data = Console.ReadLine();
            if (string.IsNullOrEmpty(data)) break;
            Client.Run("localhost", port, data, m => Console.WriteLine("Client received server reply: {0}", m));
        }
    }

    private static string ProcessClientMessage(string clientMessage)
    {
        Console.WriteLine("Server received client message: {0}", clientMessage);
        // This callback would ordinarily process the client message, then return a string that will be sent back to the client in response.
        // For now, we'll just return a fixed string value...
        return "This is the server reply...";
    }
}
于 2012-12-19T17:27:16.497 に答える