У меня есть оператор switch, который использует строку, преобразованную из char[], которая была отправлена через поток данных с сервера на клиент. Код находится на стороне клиента.
Случай по умолчанию всегда срабатывает, даже если сервер и код ошибки по умолчанию всегда точно соответствуют случаям.
SERVER SIDE
private void SendToClient(string message, TcpAccount account)
{
try
{
byte[] buffMessage = Encoding.ASCII.GetBytes(message);
account.TcpClient.GetStream().WriteAsync(buffMessage, 0, buffMessage.Length);
log.AppendText(string.Format("Message {0} sent to account {1}", message, account.ID));
log.AppendText(Environment.NewLine);
}
catch
{
log.AppendText(string.Format("Message {0} sent to account {1}", message, account.ID));
log.AppendText(Environment.NewLine);
}
}
CLIENT SIDE
public async void ReadDataAsync(TcpClient client)
{
try
{
StreamReader clientStreamReader = new StreamReader(client.GetStream());
char[] buff = new char[64];
int readByteCount = 0;
while (true)
{
readByteCount = await clientStreamReader.ReadAsync(buff, 0, buff.Length);
if (readByteCount <= 0)
{
Console.WriteLine("Disconnected from server.");
client.Close();
break;
}
string code = new string(buff);
ProcessServerCode(code);
Array.Clear(buff, 0, buff.Length);
}
}
catch
{
}
}
public void ProcessServerCode(string code)
{
switch (code)
{
case "regcom":
MessageBox.Show("Registration Complete");
break;
case "logcom":
MessageBox.Show("Login Complete");
break;
case "badpass":
MessageBox.Show("Invalid Password");
break;
case "badaccount":
MessageBox.Show("Invalid Account");
break;
default:
MessageBox.Show("Unknown Code: " + code);
break;
}
}
Я не могу получить его из кода по умолчанию.
Кроме того, поскольку я новичок в программировании клиент-сервер и сокет, я только что понял, что сервер отправляет byte[], но клиент получает char[]. Нет ли там конфликта? Есть ли какая-то конкретная причина для этого (поскольку я использую эти конкретные фрагменты кода из онлайн-тренировки)?
Можно попробовать обрезать полученную строку, обновить эту строку string code = new string(buff).Trim();. Я думаю, это может быть причиной, потому что вы ждете, чтобы получить 64 байта, но полученная строка меньше в ваших примерах
примечание: никогда никогда использует async void. Даже если вас не волнует результат, вам следует всегда использовать async Task. Основная причина здесь заключается в том, что async заботится о контексте синхронизации, и существует множество контекстов синхронизации, которые активно блокироватьasync void — в частности, но не ограничиваясь этим, контекст синхронизации ASP.NET. Это означает, что вы можете обнаружить, что код отлично работает в одной системе и не работает вообще при смене версии/платформы/и т. д. из-за различий в контексте синхронизации (один разрешает async void, другой не разрешает). Итак: никогда никогда используйте async void
@MarcGravell Спасибо! Я внес изменения и обязательно избегу асинхронной недействительности в будущем.
еще одно примечание: на самом деле вы не используете await для WriteAsync - вы действительно должен ждете этого





Первая проблема, которую я вижу, заключается в том, что вы игнорируете readByteCount (который на самом деле является подсчетом символов, а не подсчетом байтов); вы должен используете:
string code = new string(buff, 0, readByteCount);
Однако следующая проблема, которую я вижу, заключается в том, что у вас нет никакого протокола кадрирования. Это означает:
В любом случае:
как и в любом случае, никогда нельзя будет правильно восстановить позицию. Отсюда: фреймы, где каждый фрейм представляет собой законченное сообщение.
Обычный подход к текстовым протоколам заключается в использовании новой строки в качестве фрейма (я позволю вам решить, означает ли это CR, LF, CRLF и т. д.); тогда вы можете просто использовать ReadLine/ReadLineAsync для чтения текста, и это будет сделать все это автоматически, поэтому:
string code = await clientStreamReader.ReadLineAsync();
if (code is null)
{
Console.WriteLine("Disconnected from server.");
client.Close();
}
else
{
ProcessServerCode(code);
}
(«и все это будет делаться автоматически» > до тех пор, пока вы не забудете экранировать концы строк в полезной нагрузке...)
@PatrickHofman действительно :) на самом деле, если бы я был педантичен, я бы сказал: «избегайте использования TextReader на сервере сокетов - обрабатывайте кадры вручную, декодируйте только тогда, когда у вас есть целые кадры, и в идеале используйте что-то вроде «конвейеров». двигатель", но это потребует много больше объяснений, чем я могу успеть в понедельник...
@MarcGravell Итак, сосредоточившись на первой проблеме, изменение кода на вашу первую строку дало мне тот же результат. По умолчанию показано, что я получаю «regcom» в качестве кода, но опять же технически это не соответствует случаю «regcom». На глаз они совпадают, но почему-то в коде не совпадают. Что касается других ваших заметок, все это кажется мне неподходящим (я действительно новичок в любом виде сетевого общения), но, используя метод ReadLineAsync(), клиент больше не получает НИКАКОЕ сообщение от сервера успешно. Есть ли какие-то другие изменения, которые мне нужны на стороне сервера?
@Arcaster да, кадры должны совпадать с обоих концов; самым простым решением будет byte[] buffMessage = Encoding.ASCII.GetBytes(message + "\r\n"); (но возможны и более эффективные подходы)
@Arcaster, что касается проблемы «изменение кода на первую строку»; к сожалению, для дальнейшей диагностики мне действительно нужно смотреть побайтно, плюс этот подход обречен на провал так или иначе из-за проблемы с кадрированием, поэтому я не уверен, что стоит тратить время на попытки продолжить это.
@MarcGravell В конечном итоге переключение на ReadLineAsync() и добавление разрыва строки в сообщение решили мою проблему. Что касается проблем с основой, то это личный учебный проект, а не код производственного уровня, я буду решать любые сложности в будущем. Спасибо за наводку и решение.
Вы пытались отлаживать то, что получает клиентский код? Если вы всегда нажимаете значение по умолчанию, то ваш ввод не такой, как вы ожидаете.