Клиент StreamSocket C# может писать только один раз на сервер

Я пытался настроить клиент-серверное приложение, используя StreamSockets в C#. Я в состоянии первоначально подключиться к серверу (ConnectAsync) и следующий, чтобы написать и прочитать поток. Если клиент отправляет другой поток на сервер с помощью метода WriteToServer, событие на стороне сервера не инициируется (SocketListener_ConnectionReceived). Я отправляю xmlSerialized объект "Сообщение" на сервер.

При отладке я не получаю никаких ошибок. На стороне клиента, хотя после использования "WriteToServer" и перехода к "ReadFromServer" код явно застревает в строке ниже, так как сервер не отвечает

            int bufferLength = inStream.ReadByte();

Я надеюсь, что кто-то вызовет помощь. Честно говоря, я не уверен, в чем проблема, потому что метод "Запись" используется одинаково, когда клиент пытается выполнить запись на сервер.

Клиент - это компьютер с Windows 10, а Сервер Raspberry Pi работает под управлением Windows 10 IoT.

Класс внутри клиентского приложения, которое обрабатывает соединение, выглядит следующим образом.

StreamSocket socket;
    HostName remoteHostName, localHostName;
    string serviceAddress = "1337";
    EndpointPair endPointPair;
    Boolean isConnected;
    Socket test;


    public Connection()
    {
        remoteHostName = new HostName("172.16.20.202"); // might have to change based on pi's ip address
        //remoteHostName = new HostName("172.16.20.4");
        localHostName = new HostName(getLocalIpAddress());

        endPointPair = new EndpointPair(localHostName, serviceAddress, remoteHostName, serviceAddress);

        socket = new StreamSocket();
        socket.Control.NoDelay = true;
        socket.Control.QualityOfService = SocketQualityOfService.LowLatency;


    }

    private string getLocalIpAddress()
    {
        var icp = NetworkInformation.GetInternetConnectionProfile();

        if (icp?.NetworkAdapter == null) return null;
        var hostname =
            NetworkInformation.GetHostNames()
                .SingleOrDefault(
                    hn =>
                        hn.IPInformation?.NetworkAdapter != null && hn.IPInformation.NetworkAdapter.NetworkAdapterId
                        == icp.NetworkAdapter.NetworkAdapterId);

        // the ip address
        return hostname?.CanonicalName;
    }

    public async Task StartConnection()
    {
        try
        {
            if (isConnected)
            {
                await socket.CancelIOAsync();
                socket.Dispose();
                socket = null;
                isConnected = false;
            }
            await socket.ConnectAsync(endPointPair);
            isConnected = true;
        }
        catch (Exception exc)
        {
            if (Windows.Networking.Sockets.SocketError.GetStatus(exc.HResult) == SocketErrorStatus.Unknown)
            {
                throw;
            }
            Debug.WriteLine("Connect failed with error: " + exc.Message);

            socket.Dispose();
            isConnected = false;
            socket = null;
            //return null;
        }
    }

    public async Task WriteToServer(Message msg)
    {
        try
        {
            using (DataWriter writer = new DataWriter(socket.OutputStream))
            {
                writer.WriteBytes(serialize(msg));
                await writer.StoreAsync();
                writer.DetachStream();
                writer.Dispose();
            }
        }
        catch (Exception exc)
        {
            Debug.WriteLine("Write failed with error: " + exc.Message);
        }

    }
    public async Task<Library.Message> ReadFromServer()
    {
        try
        {
            Stream inStream = socket.InputStream.AsStreamForRead();
            int bufferLength = inStream.ReadByte();
            byte[] serializedMessage = new byte[bufferLength];

            await inStream.ReadAsync(serializedMessage, 0, bufferLength);
            await inStream.FlushAsync();

            Library.Message incomingMessage;
            using (var stream = new MemoryStream(serializedMessage))
            {
                var serializer = new XmlSerializer(typeof(Library.Message));
                incomingMessage = (Library.Message)serializer.Deserialize(stream);
            }
            return incomingMessage;
        }
        catch (Exception exc)
        {
            Debug.WriteLine("Read failed with error: " + exc.Message);
            return null;
        }
    }

    private byte[] serialize(Message msg)
    {
        byte[] serializedMessage, returnArray;
        var serializer = new XmlSerializer(typeof(Library.Message));

        using (var stream = new MemoryStream())
        {
            serializer.Serialize(stream, msg);
            serializedMessage = stream.ToArray();
            stream.Dispose();
        }

        int bufferLength = serializedMessage.Length;

        returnArray = new byte[serializedMessage.Length + 1];
        serializedMessage.CopyTo(returnArray, 1);
        returnArray[0] = (byte)bufferLength;

        Debug.WriteLine("ClientReturnArrayLength: " + returnArray.Length);
        Debug.WriteLine("ClientFirstByte: " + returnArray[0]);
        return returnArray;
    }

Серверная сторона выглядит так

    public Task DispatcherPriority { get; private set; }

    public MainPage()
    {
        this.InitializeComponent();
        dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
        hostName = new HostName(getLocalIpAddress());
        clients = new List<Client>();
    }

    /// <summary>
    /// Gets the ip address of the host
    /// </summary>
    /// <returns></returns>
    private string getLocalIpAddress()
    {
        var icp = NetworkInformation.GetInternetConnectionProfile();

        if (icp?.NetworkAdapter == null) return null;
        var hostname =
            NetworkInformation.GetHostNames()
                .SingleOrDefault(
                    hn =>
                        hn.IPInformation?.NetworkAdapter != null && hn.IPInformation.NetworkAdapter.NetworkAdapterId
                        == icp.NetworkAdapter.NetworkAdapterId);

        // the ip address
        return hostname?.CanonicalName;
    }


    async void setupSocketListener()
    {

        if (socketListener != null)
        {
            await socketListener.CancelIOAsync();
            socketListener.Dispose();
            socketListener = null;
        }
        socketListener = new StreamSocketListener();
        socketListener.Control.QualityOfService = SocketQualityOfService.LowLatency;
        socketListener.ConnectionReceived += SocketListener_ConnectionReceived;
        await socketListener.BindServiceNameAsync("1337");
        listBox.Items.Add("server started.");

        clients.Clear();
    }

    private async void SocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
    {
        HostName ip = args.Socket.Information.RemoteAddress;
        string port = args.Socket.Information.RemotePort;

        try
        {
            Stream inStream = args.Socket.InputStream.AsStreamForRead();
            int bufferLength = inStream.ReadByte();
            byte[] serializedMessage = new byte[bufferLength];

            await inStream.ReadAsync(serializedMessage, 0, bufferLength);
            await inStream.FlushAsync();

            Message incomingMessage;
            using (var stream = new MemoryStream(serializedMessage))
            {
                var serializer = new XmlSerializer(typeof(Message));
                incomingMessage = (Message)serializer.Deserialize(stream);
            }

            /// <summary>
            /// 1 = Connected
            /// 2 = SentNote
            /// 3 = Login
            /// </summary>
            switch (incomingMessage.Mode)
            {
                case 1:
                    onClientConnect(ip, port, incomingMessage.Username, args.Socket);
                    break;
                case 2:
                    onNoteReceived(incomingMessage);
                    break;
                case 3:
                    //handle login
                    break;
            }
        }
        catch (Exception msg)
        {
            Debug.WriteLine(msg);
        }

    }

    private async void onNoteReceived(Message msg)
    {
        foreach (var client in clients)
        {
            //if (client.Username != msg.Username)
            //{
            using (DataWriter writer = new DataWriter(client.Socket.OutputStream))
            {
                writer.WriteBytes(serialize(msg));
                await writer.StoreAsync();
                writer.DetachStream();
                writer.Dispose();
            }
            //}
        }
    }

    private void buttonStartServer_Click(object sender, RoutedEventArgs e)
    {
        setupSocketListener();
    }


    private async void notifyClients(string username)
    {
        Message msg = new Message();
        msg.Username = username;

        foreach (var client in clients)
        {
            //if (client.Username != msg.Username)
            //{
            using (DataWriter writer = new DataWriter(client.Socket.OutputStream))
            {
                writer.WriteBytes(serialize(msg));
                await writer.StoreAsync();
                writer.DetachStream();
                writer.Dispose();
            }
            //}
        }
    }

    private async void onClientConnect(HostName ip, string port, string username, StreamSocket socket)
    {
        clients.Add(new Client(ip, port, username, socket));

        await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            listBox.Items.Add("User: " + username + " on IP " + ip + " is connected.");
        });

        notifyClients(username);
    }


    private byte[] serialize(Message msg)
    {
        byte[] serializedMessage, returnArray;
        var serializer = new XmlSerializer(typeof(Message));

        using (var stream = new MemoryStream())
        {
            serializer.Serialize(stream, msg);
            serializedMessage = stream.ToArray();
            stream.Dispose();
        }

        int bufferLength = serializedMessage.Length;

        returnArray = new byte[serializedMessage.Length + 1];
        serializedMessage.CopyTo(returnArray, 1);
        returnArray[0] = (byte)bufferLength;

        Debug.WriteLine("ClientReturnArrayLength: " + returnArray.Length);
        Debug.WriteLine("ClientFirstByte: " + returnArray[0]);
        return returnArray;
    }


}

Это класс сообщений, который я использую для отправки по сети.

    public string Username { get; set; }

    /// <summary>
    /// 1 = startConnection
    /// 2 = SendNote
    /// 3 = Login
    /// </summary>
    public int Mode { get; set; }
    public Piano PianoNote { get; set; }
    public string Instrument { get; set; }

    public Message()
    {

    }

}

public enum Piano { a1, a1s, b1, c1 };

**Редактировать: **

Обрамление сообщения:

byte[] prefix = BitConverter.GetBytes(serializedMessage.Length);
returnArray = new byte[serializedMessage.Length + prefix.Length];
prefix.CopyTo(returnArray, 0);
serializedMessage.CopyTo(returnArray, prefix.Length);

Читая сообщение:

byte[] prefix = new byte[4];
await inStream.ReadAsync(prefix, 0, 4);
int bufferLength = BitConverter.ToInt32(prefix, 0);

Полуоткрытый: вместо синхронного чтения я переключился на асинхронное чтение первых 4 байтов, как показано выше.

1 ответ

Решение

Я в состоянии первоначально подключиться к серверу (ConnectAsync) и следующий, чтобы написать и прочитать поток. Если клиент отправляет другой поток на сервер с помощью метода WriteToServer, событие на стороне сервера не инициируется (SocketListener_ConnectionReceived).

Внимательно посмотрите на эти имена. Ты звонишь ConnectAsync один раз и потом WriteToServer дважды, и только видя SocketListener_ConnectionReceived один раз. Есть только одна связь, так что да, ConnectionReceived будет срабатывать только один раз.

Это просто царапает поверхность, хотя. Есть несколько других очень тонких проблем с этим кодом.

То, что торчит, - это отсутствие надлежащей структуры сообщений, как я описываю в своем блоге. Вы используете префикс длиной в один байт, поэтому в сети все в порядке (хотя ограничено 256 байтами, что далеко не идет с XML). Но чтение сообщений неверно; особенно, Stream.ReadAsync может читать между 1 а также bufferLength байтов, или он может вернуться 0,

Еще одна проблема заключается в том, что это проблема полуоткрытого типа, как я описываю в своем блоге. Особенно, int bufferLength = inStream.ReadByte(); будет блокироваться на неопределенный срок в полуоткрытой ситуации. Вы должны использовать только асинхронные методы для всех сетевых потоков и периодически выполнять запись в ожидании поступления данных.

Таким образом, я настоятельно рекомендую вам использовать собственный хост SignalR вместо необработанных сокетов. Программирование сокетов в необработанном виде чрезвычайно сложно сделать правильно, особенно потому, что неправильный код часто работает правильно в локальной среде.

Другие вопросы по тегам