#c# #winforms #tcp #tcpclient #tcpserver
Вопрос:
Недавно я начал изучать компьютерные сети и решил попробовать сервер и клиент TCP/IP. Они оба работают, но у меня возникли проблемы с отправкой данных mutliple на сервер. Я сделал так, чтобы это выглядело как служба чата между клиентами, но сервер принимает только одного клиента и закрывает соединение после отправки данных, а клиент по какой-то причине перестает отвечать после отправки данных на сервер (я думаю, что проблема исходит от сервера, а не от самого клиента), сообщения об ошибке нет, только на стороне сервера, когда я принудительно закрываю клиент. Вот как выглядит мой сервер…
static void Main(string[] args)
{
//User can define port
Console.WriteLine("open a port:");
string userInputPort = Console.ReadLine();
//listening for connections
TcpListener listener = new TcpListener(System.Net.IPAddress.Any, Convert.ToInt32(userInputPort));
listener.Start();
Console.WriteLine("listening...");
while (true)
{
//waiting for client to connect to server
Console.WriteLine("Waiting for connection...");
//when user connects to server, server will accept any request
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Client Accepted");
NetworkStream stream = client.GetStream();
StreamReader streamR = new StreamReader(client.GetStream());
StreamWriter streamW = new StreamWriter(client.GetStream());
while (true)
{
if(client.Connected)
{
if (stream.CanRead)
{
//buffer
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
int recv = 0;
foreach (byte b in buffer)
{
if(b != 0)
{
recv ;
}
}
string request = Encoding.UTF8.GetString(buffer, 0, recv);
Console.WriteLine("request recived: " request);
streamW.Flush();
}
}
}
}
}
}
}
и вот как выглядит клиент…
...
try
{
//try to connect
client = new TcpClient(textBoxIP.Text, Convert.ToInt32(textBoxPort.Text));
}
...
static void sendMessage(string message, TcpClient client)
{
int byteCount = Encoding.ASCII.GetByteCount(message);
byte[] sendData = new byte[byteCount];
sendData = Encoding.ASCII.GetBytes(message);
NetworkStream stream = client.GetStream();
stream.Write(sendData, 0, sendData.Length);
StreamReader streamReader = new StreamReader(stream);
string respone = streamReader.ReadLine();
stream.Close();
client.Close();
}
Как я уже сказал, я все еще изучаю компьютерные сети, и любой комментарий к этому коду поможет!
Спасибо
Комментарии:
1. Поэтому, когда вы отладили все основные, строки и состояния. Какие результаты вы получили, чем программа отличается от ваших ожиданий? Если вы не можете ответить на этот вопрос, значит, вы недостаточно сделали для отладки своего кода. Вам нужно владеть фактами при написании сложного программного обеспечения, вам нужно быть уверенным в том, как оно работает, догадки закончатся неудачей и вопросами, на которые не будут даны ответы в stackoverflow
Ответ №1:
Это поможет, если вы дадите себе некоторое представление о том, чего вы на самом деле ожидаете от кода, который вы пишете. Мне кажется, что вы делаете много автоматических предположений, на самом деле не заботясь о том, чтобы включить их в свой код.
- Ваш сервер может в лучшем случае принимать только одного клиента. Не по одному клиенту за раз, а всегда по одному. Вы никогда не выходите из цикла чтения, поэтому после отключения клиента вы оказываетесь в замечательном бесконечном цикле занятости. Вероятно, вы намеревались обслужить другого клиента, когда он отключится, но это не то, что вы делаете.
- Вы предполагаете, что сервер отправит ответ клиенту. Но на самом деле вы никогда не отправляете никакого ответа! Чтобы клиент что-то прочитал, сервер сначала должен отправить что-то, чтобы клиент прочитал.
- Вы предполагаете, что строка, отправленная клиентом, будет обнулена или что целевой буфер для
Read
будет обнулен. Если вы хотите нулевого завершения, вы должны отправить его самостоятельно от клиента-StreamWriter
конечно, это не так. Строки, как правило, не заканчиваются нулем-это всего лишь один способ представления строк в памяти в стиле Си. Вы не должны предполагать ничего о содержимом буфера, кроме того, чтоRead
вам сообщило возвращаемое значение.
По-видимому, это проблемы с вещами, которые вы забыли вставить. Теперь перейдем к неверным предположениям о том, как работает TCP. Чтобы сохранить ясность, я расскажу так, как оно есть, а не о неправильном предположении.
- Одна запись может привести к нескольким считываниям с другой стороны, а одно чтение может считывать данные из нескольких записей с другой стороны. TCP не отправляет (и не получает) сообщения, он имеет дело с потоками. Вам нужно добавить протокол обмена сообщениями в дополнение к этому, если потоки недостаточно хороши для вас.
Read
возвращает количество прочитанных байтов. Используйте это для обработки ответа, вместо того, чтобы искать ноль. КогдаRead
возвращается ноль, это означает, что соединение было закрыто, и вы также должны закрыть свою сторону. Это все, что вам нужно , вместо всегоwhile (true)
,if (Connected)
иif (CanRead)
— цикл доRead
тех пор, пока не вернется ноль. Обрабатывайте данные, которые вы получаете, по мере их поступления к вам.- С потоком TCP работать немного сложнее, чем с большинством потоков; он ведет себя достаточно по-другому, поэтому использование таких помощников
StreamReader
опасно. Вы должны выполнить эту работу самостоятельно или получить библиотеку с более высокой абстракцией для работы с сетями. TCP-это очень низкий уровень. - Вы не можете полагаться на получение ответа на a
Read
. TCP использует соединения, но он ничего не делает, чтобы поддерживать связь самостоятельно или замечать, когда она отключена — он был разработан для совсем другого Интернета, чем сегодняшний, и он может счастливо пережить перерывы в обслуживании в течение нескольких часов — до тех пор, пока вы не попытаетесь что-либо отправить. Если клиент внезапно отключится, сервер может никогда не узнать об этом.
Вы также должны убедиться, что правильно очистили все собственные ресурсы — это действительно помогает использовать using
их, когда это возможно. .NET в конце концов очистится, но для таких вещей, как ввод-вывод, это часто опасно поздно.
Ответ №2:
while (true)
{
if(client.Connected)
{
if (stream.CanRead)
{
Я не вижу никакого кода, который выходит из внешнего цикла во время цикла, если либо client.Connected
или stream.CanRead
становится ложным. Поэтому, когда клиент отключается и они становятся ложными, мне кажется, что сервер просто зацикливается навсегда.
Вы должны, по крайней мере, выполнить всю обработку ошибок (закрыть все необходимые потоки) и выйти из цикла.
Следующей проблемой является то, что в коде одновременно может быть только один клиент. Если клиент на самом деле не закрывает соединение. Я не знаю наверняка, какое правильное решение на C#, но я думаю, что оно порождает отдельный поток для каждого подключенного клиента.