Асинхронный сокет, получение строковых сообщений - PullRequest
2 голосов
/ 21 июля 2011

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

Мое клиентское приложение получает от сервера асинхронно. Я хочу передать кучу вещей одновременно (содержимое массива, пара сотен байтов).

Я хочу, чтобы сервер мог отправлять «команды» и иметь функцию на стороне клиента, действующую в соответствии с этими командами, например, если сообщение с сервера гласит "print_hello", он должен вызвать функцию, которая печатает привет.

Теперь я понимаю, что при асинхронном получении данных я не могу знать, какая часть данных была отправлена ​​(или было ли отправлено больше данных, чем я ожидал), поэтому мне нужно хранить все данные в буфера, и когда получен знак «конец команды» (например, '!'), он должен знать, что нужно вызвать функцию.

Пока это имеет смысл для меня, но у меня возникают проблемы с его реализацией. В моей функции обратного вызова DataReceived у меня есть этот код:

Console.WriteLine("Raw data: {0}", data));
mainBuffer += data;
Console.WriteLine(mainBuffer);

mainBuffer объявлен как volatile static string mainBuffer = ""; Первая строка печатается правильно и проходит все данные, как и ожидалось. Однако, когда я распечатываю mainBuffer, он печатает только самый первый набор данных, которые я получил, остальные не добавляются в буфер.

Что могло вызвать это? Вопросы безопасности потока? Разве я не читаю последнее значение mainBuffer? Я не могу использовать точки останова для отладки.

Пример вывода:

Raw data: ABC
ABC
Raw data: DEF
ABC
RAW data: GHI
ABC

Небольшое обновление, я также попытался использовать volatile static int, и он увеличивается и печатается правильно после каждого DataReceived(). Однако строка по-прежнему не обновляется.

1 Ответ

3 голосов
/ 23 июля 2011

Вот ваша проблема "со строками кода!" :

//of course the problem has noting to do with the string being volatile...
private volatile string mainBuffer = string.Empty;

byte[] buffer = new byte[1024];

while (networkStream.Read(buffer, 0, buffer.Length) > 0)
{
    string data = System.Text.Encoding.UTF8.GetString(buffer);
    Console.WriteLine("Raw data: {0}", data));
    mainBuffer += data;
    Console.WriteLine(mainBuffer);
}

Естественно, вывод этого кода будет таким, как вы упоминали ранее.Вот что происходит:

Класс string в C # - это массив char, начинающийся с указателя на первый char в массиве и заканчивающийся специальным "терминальным" char \0,Когда вы создаете байтовый массив индекса n, он заполнит все индексы массива значением по умолчанию byte, равным 0.но 0 просто равен символу терминала \0

byte b = (byte)`\0`;\\the value of b will be 0

Итак, когда вы вызываете Read(buffer), метод не будет обрезать буфер до соответствия прочитанные данные.поэтому, если размер буфера «здесь 1024» больше, чем считанные данные, все оставшиеся байты буфера будут равны терминалу char '\ 0', поэтому массив символов сгенерированной строки будет ABC\0\0\0\0... to the index 1024.Когда вы добавляете строку DEF к ней, она будет добавлена ​​в последний индекс массива char «после последнего \0», тогда массив char будет ABC\0\0\0\0...DEF, но из-за DEFдобавляется после char (s) терминала, поэтому Console.Write будет игнорировать все после первого \0 !.

Также обратите внимание, что во время отладки, если вы наведите указатель мыши на переменную mainBuffer, вы увидите фактические данные, которые она содержит, может быть, что-то вроде ABC\0\0\0\0..DEF\0\0\0\0..GHI

Однако, чтобы решить проблему и создать только надежную строку, получите фактическое чтение байтов и сгенерируйте строку только из нее.Итак:

int dataRead = 0;

while ((dataRead = networkStream.Read(buffer, 0, buffer.Length)) > 0)
{
    List<byte> actualBuffer = (new List<byte>(buffer)).GetRange(0, dataRead);

    string data = System.Text.Encoding.UTF8.GetString(actualBuffer.ToArray());
    Console.WriteLine("Raw data: {0}", data));
    mainBuffer += data;
    Console.WriteLine(mainBuffer);
}

Глупо упоминать здесь, что вы должны рассмотреть возможность использования StringBuilder вместо string.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...