Клиенты Xamarin / WinForms не могут подключиться к сокету при отправке / получении в двухпоточной архитектуре - PullRequest
0 голосов
/ 04 марта 2019

Цель

Отправка и получение сообщений из сокета TCP на двух разных потоках в программе с пользовательским интерфейсом.

Проблема

Не удается подключитьсяв сокет и отправлять / получать данные ТОЛЬКО когда Я в Xamarin.Forms и WinForms.Так что это какая-то проблема с многопоточностью.

Когда я пытаюсь подключиться, он просто отключается здесь , поэтому нет ошибок или ничего.

То, что я пытался

Я много работал с Решением Мэтта Дэвиса здесь, на StackOverflow .

Я создал тестовый код ( TcpClientSendReceive ) на GitHub со всем необходимым кодом.

Мой код

Все это на GitHub в этом хранилище .Я не могу вместить все это здесь.Но вот код подключения , который, кажется, не работает, например, с Xamarin.Forms , см. Этот код здесь .

Если я попытаюсь подключить к моему консольному серверу из пользовательского интерфейса, который я не могу подключить (ведь это означает, что я не могу ни отправлять / получать)

базовый код работает?

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

В чем может быть проблема?

Определенно, как настроена многопоточность и как она взаимодействует с пользовательским интерфейсом.Мне просто не хватает опыта работы с потоками, чтобы понять это.Я надеюсь, что некоторые из вас делают!: -)

РЕДАКТИРОВАТЬ:

ИНФОРМАЦИЯ О КУРОРТЕ

Я создал более подробную информацию о наградах на GitHub , поэтомубудьте совершенно ясны, что мне нужно.

Я добавил ЛОТОВ изображений и ссылок на код.Должно быть очень легко получить код и просто попробовать его. Пожалуйста, попробуйте .Я действительно не думаю, что это большая проблема для кого-то более низкого уровня, чем я.

Ответы [ 3 ]

0 голосов
/ 10 марта 2019

Проблема в том, что клиенты и сервер взаимодействуют друг с другом.Сервер использует BinaryReader и BinaryWriter, а клиенты записывают / читают байты в «сырой» поток как есть.Разница в том, что BinaryWriter записывает не только string байта, но также его байты длины перед самим string и BinaryReader не читает все доступные в настоящее время байты из сети и преобразует их в string, но читаетсначала длина, а затем - чтение точного количества байтов, необходимого для получения полного string.

Так что самым простым будет использование BinaryWriter и BinaryReader везде, поскольку они могут читать сложные данные (string) байты, кодирование и декодирование их для вас.

Хотя реализация собственного протокола связи и непосредственная работа с NetworkStream методами чтения / записи байтов дает вам большую гибкость, для его правильной работы требуются дополнительные усилия.,Например, существует один метод для чтения нескольких байтов из сети

int Read(byte[] buffer, int offset, int size)

Когда сервер отправляет string, клиент не знает, сколько байтов он должен прочитать.Это может создать большой буфер, который может содержать некоторое разумное количество байтов.Но если сервер отправляет две строки одну за другой, клиент может прочитать их обе в буфер одновременно, и данные могут быть уже повреждены.На этом этапе вам необходимо реализовать свой протокол связи для отправки полных сообщений по сети.Еще одна оговорка для метода Read заключается в том, что он может считывать не все доступные на данный момент данные из сети одновременно, а значение size не гарантирует количество прочитанных байтов.Например, вы знаете, что сервер отправляет клиенту n байтов, а клиент читает их

stream.Read(buffer, 0, n)

Возможно, что клиент будет читать только некоторую часть этих байтов за один Read вызов, а оставшаяся часть будет по-прежнемубыть в сети, доступной для чтения.Вы должны учесть это и справиться с этой ситуацией, читая байты в цикле, пока нужно прочитать необходимое количество.

0 голосов
/ 13 марта 2019

Я не проверял, но я полагаю, что вижу тупик в этом методе: https://github.com/sturlath/TcpClientSendReceive/blob/a15c936889412e5a4ac249df2b291db817d40307/TcpClientLib/Client.cs#L37

Этого может быть достаточно, чтобы это исправить:

await Task.Run(() => _client.GetStream()).ConfigureAwait(false);

Похож на клиента& сервер пытается подключиться в контексте синхронизации пользовательского интерфейса.Это один поток, поэтому он не будет работать.Если он все еще заблокирован, возможно, у вас есть и другие места, которые также необходимо исправить.

0 голосов
/ 09 марта 2019

как насчет этого?

public class HandleClient
    {
        private TcpClient clientSocket;
        private static int bufferLength = 1024;
        private byte[] readBuffer = new byte[bufferLength];

        public void StartClient(TcpClient inClientSocket)
        {
            this.clientSocket = inClientSocket;

            var ctThread = new Thread(Chat);
            ctThread.Start();
        }

        private void Chat()
        {
            var reader = clientSocket.GetStream();

            try
            {
                while (true)
                {
                    var bytesRead = reader.Read(readBuffer, 0, bufferLength);

                    using (var memoryStream = new MemoryStream())
                    {
                        memoryStream.Write(readBuffer, 0, bytesRead);
                        var message = System.Text.Encoding.ASCII.GetString(memoryStream.ToArray());

                        Log.Debug("Server got this message from client: {message}", message);

                        foreach (TcpClient client in Program.GetClients())
                        {
                            var writer = new BinaryWriter(client.GetStream());
                            writer.Write($"Server got your message '{message}'");
                        }
                    }
                }
            }
            catch (EndOfStreamException)
            {
                Log.Error($"client disconnecting: {clientSocket.Client.RemoteEndPoint}");
                clientSocket.Client.Shutdown(SocketShutdown.Both);
            }
            catch (IOException e)
            {
                Log.Error($"IOException reading from {clientSocket.Client.RemoteEndPoint}: {e.Message}");
                Console.WriteLine();
            }
            catch (ObjectDisposedException e)
            {
                Log.Error(e, "Error (ObjectDisposedException) receiving from server");
            }
            catch (Exception e)
            {
                Log.Error(e, "Error receiving from server");
            }

            clientSocket.Close();
            Program.RemoveClient(clientSocket);
            Log.Debug("{count} clients connected", Program.GetClientCount());
        }
    }

Winform

android

communication

, если ваша winform не может получить сообщение.вы должны изменить метод OnClient_MainDataReceived на

this.Invoke((MethodInvoker)delegate ()
            {
                txbResponseFromServer.AppendText(Environment.NewLine);
                txbResponseFromServer.AppendText(e.Data);
            });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...