1

TCP/IP を介してシリアル化されたコマンドを通信するプロジェクトに取り組んでいます。ローカル ホストにいるときは動作していますが、別のサーバーでリスナーを実行すると、リスナー側でコマンドを逆シリアル化しようとするとランダムに失敗します。スローされる例外は次のとおりです。「空のストリームを逆シリアル化しようとしています。」および「解析が完了する前にストリームの終わりが検出されました。」シリアライザから

一連のコマンドを個別に実行すると正常に動作しますが、スレッドを作成して複数のシーケンスを同時に実行すると失敗します。

リスナーは 4 つの異なるポートでリスナーを作成し、クライアントはポートごとに 1 つのスレッドを実行します。スレッドの 1 つがシーケンスの最後に到達すると、そのスレッドは終了します。

クライアントのシングルトンを作成しようとし、Mutex も試しました。しかし、それでも同じ問題です。

これが私のクライアントです:

public class TcpIpCommunicator : ICommunicator, IDisposable
{
    private Dictionary<int,TcpClient> clientSockets = new Dictionary<int,TcpClient>();
    public IInverterCommand ReadAsyncCommand { set; get; }
    private static TcpIpCommunicator tcpIpCommunicator;

    private TcpIpCommunicator()
    {
    }

    public static TcpIpCommunicator GetInstance()
    {
        if(tcpIpCommunicator == null)
            tcpIpCommunicator = new TcpIpCommunicator();

        return tcpIpCommunicator;
    }

    public void Send(IInverterCommand command, int id)
    {
        var serializer = new Serializer();
        MemoryStream stream = serializer.SerializeMultipleObjects(command);
        var _bytes = stream.GetBuffer();

        var networkStream = clientSockets[id].GetStream();
        networkStream.Write(_bytes, 0, _bytes.Length);
        networkStream.Flush();
    }

    public IInverterCommand Read(int id)
    {
        var memoryStream = new MemoryStream();

        byte[] buffer;
        var networkStream = clientSockets[id].GetStream();
        do
        {
            buffer = new byte[clientSockets[id].ReceiveBufferSize];
            int sizeRead = networkStream.Read(buffer, 0, buffer.Length);
            memoryStream.Write(buffer, 0, sizeRead);
        } while (networkStream.DataAvailable);

        networkStream.Flush();

        memoryStream.Position = 0;
        var serializer = new Serializer();
        return serializer.DeSerializeMultipleObject(memoryStream);

    }

    public void ReadAsync(object id)
    {
        ReadAsyncCommand = Read((int)id);
    }

    public void Dispose()
    {
        foreach (var tcpClient in clientSockets.Values)
        {
            tcpClient.Close();
        }
    }

    public int Connect(string ip, int port)
    {
        var tcpClient = new TcpClient();
        tcpClient.ReceiveTimeout = int.MaxValue;
        tcpClient.SendTimeout = int.MaxValue;
        tcpClient.Connect(ip, port);
        int key = findKey();
        clientSockets.Add(key, tcpClient);

        return key;
    }

    public void DestroyConnection(int id)
    {
        clientSockets[id].Close();
        clientSockets.Remove(id);
    }

    private int findKey()
    {
        int key = 0;
        while(clientSockets.ContainsKey(key))
        {
            key++;
        }

        return key;
    }
}

そして、私のサーバー側のコードはここにあります:

public class TCPListener : IDisposable
{
    private readonly TcpListener _serverSocket;
    private NetworkStream _networkStream;
    private readonly TcpClient _clientSocket = default(TcpClient);

    public TCPListener(int port)
    {
        _serverSocket = new TcpListener(port);
        _serverSocket.Server.ReceiveTimeout = int.MaxValue;
        _serverSocket.Server.SendTimeout = int.MaxValue;
        _serverSocket.Start();
        _clientSocket = _serverSocket.AcceptTcpClient();
    }

    public void Send(IInverterCommand message)
    {
        _networkStream = _clientSocket.GetStream();
        var serialize = new Serializer();
        var stream = serialize.SerializeMultipleObjects(message);
        var _bytes = stream.GetBuffer();

        if (_bytes.Length > _clientSocket.ReceiveBufferSize)
        {
            byte[] bytes = new byte[_clientSocket.ReceiveBufferSize];
            for (int i = 0; i < _bytes.Length; i += _clientSocket.ReceiveBufferSize)
            {
                for (int j = 0; j < _clientSocket.ReceiveBufferSize && i + j != _bytes.Length; ++j)
                {
                    bytes[j] = _bytes[i + j];
                }

                _networkStream.Write(bytes, 0, _clientSocket.ReceiveBufferSize);
            }
        }
        else
        {
            _networkStream.Write(_bytes, 0, _bytes.Length);
        }

        Thread.Sleep(50);

        _networkStream.Flush();
    }

    public IInverterCommand ReadCommand()
    {
        _networkStream = _clientSocket.GetStream();
        var memoryStream = new MemoryStream();

        do
        {
            var buffer = new byte[_clientSocket.ReceiveBufferSize];
            int sizeRead = _networkStream.Read(buffer, 0, buffer.Length);
            memoryStream.Write(buffer, 0, sizeRead);
            Thread.Sleep(50);
        } while (_networkStream.DataAvailable);

        _networkStream.Flush();
        memoryStream.Position = 0;
        var serializer = new Serializer();
        return serializer.DeSerializeMultipleObject(memoryStream);
    }

    public void Dispose()
    {
        _clientSocket.Close();
        _serverSocket.Stop();
    }
}

これは通常、クライアント側でコードを呼び出しています。

IInverterCommand command = new SoftwareUpdateInverterCommand();
        tcpClient.Send(command, tcpId);

        var thread = new Thread(tcpClient.ReadAsync);
        thread.Start(tcpId);

        if (!thread.Join(timeout))
        {
            thread.Abort();
            tcpClient.DestroyConnection(tcpId);
            return;
        }

サーバー側の呼び出しコード:

//Recieving CMD on software update
            TcpListener = new TCPListener((int)port);
            var command = TcpListener.ReadCommand();

            //Sending OK back to server
            command.Message = "OK";
            TcpListener.Send(command);
4

1 に答える 1

0

スレッド同期の主題と、それが何を意味し、どのように使用するかについて読むことをお勧めします。また、スレッドセーフではないスレッド間で物事を共有するとどうなりますか。

コードにスレッド同期がまったくありませんが、同期なしで少なくとも 2 つの異なるスレッドからディクショナリ (安全ではありません) を読み書きします。

それだけで、追跡が非常に困難なバグや、対処したくないさまざまな現象が発生する可能性があります。

スレッド化を適切に行う方法については、実際に読む必要があります。

于 2012-10-04T10:50:03.363 に答える