Как отправить файлы через tcp с помощью TcpListener / Client? Проблема с SocketException - PullRequest
4 голосов
/ 18 июня 2009

Я разрабатываю простое приложение для отправки файлов по TCP с использованием классов TCPListener и TCPClient. Вот код, который отправляет файл.

Stop - это логическое значение volatile, которое помогает остановить процесс в любое время, а WRITE_BUFFER_SIZE может быть изменено во время выполнения (другое значение volatile)

while (remaining > 0 && !stop)
{
  DateTime current = DateTime.Now;
  int bufferSize = WRITTE_BUFFER_SIZE;
  buffer = new byte[bufferSize];
  int readed = fileStream.Read(buffer, 0, bufferSize);
  stream.Write(buffer, 0, readed);
  stream.Flush();
  remaining -= readed;
  // Wait in order to guarantee send speed
  TimeSpan difference = DateTime.Now.Subtract(current);
  double seconds = (bufferSize / Speed);
  int wait = (int)Math.Floor(seconds * 1000);
  wait -= difference.Milliseconds;
  if (wait > 10)
    Thread.Sleep(wait);
}                
stream.Close();

и это код, который обрабатывает сторону получателя:

do
{
  readed = stream.Read(buffer, 0, READ_BUFFER_SIZE);
  // write to .part file and flush to disk
  outputStream.Write(buffer, 0, readed);
  outputStream.Flush();
  offset += readed;
} while (!stop && readed > 0);

Теперь, когда скорость низкая (около 5 Кбит / с), все работает нормально, но, по мере увеличения скорости, размер приемника становится более склонным к возникновению исключения SocketException при чтении из потока. Я предполагаю, что это связано с закрытием удаленного сокета до того, как все данные будут прочитаны, но как правильно это сделать? Когда мне следует закрыть отправляющего клиента?

Я не нашел хороших примеров передачи файлов в Google, и те, которые я нашел, имеют аналогичную реализацию того, что я делаю, поэтому я предполагаю, что что-то упустил.

Редактировать: Я получаю эту ошибку "Невозможно прочитать данные из транспортного соединения". Это IOException, внутренним исключением которого является SocketException.

Я добавил это в функцию отправителя, но все равно получаю ту же ошибку, код никогда не достигает stream.close () и, конечно, tcpclient никогда не закрывается ... так что я полностью потерян.

buffer = new byte[1];
client.Client.Receive(buffer);
stream.Close();

1 Ответ

1 голос
/ 18 июня 2009

Обычно вы хотите установить опцию LINGER на сокете. В C ++ это будет SO_LINGER, но в Windows это на самом деле не работает так, как ожидалось. Вы действительно хотите сделать это:

  • Завершить отправку данных.
  • Вызовите shutdown () с параметром how, установленным в 1.
  • Цикл на recv (), пока не вернется 0.
  • Вызов closesocket ().

Взято из: http://tangentsoft.net/wskfaq/newbie.html#howclose

C # sharp, возможно, исправил это в своих библиотеках, но я сомневаюсь в этом, поскольку они построены на основе winsock API.

Edit:

Рассмотрим ваш код более подробно. Я вижу, что вы вообще не отправляете заголовок, поэтому на принимающей стороне вы понятия не имеете, сколько байтов вы фактически должны прочитать. Знание количества байтов, которые нужно прочитать из сокета, значительно облегчает отладку. Имейте в виду, что выключение сокета может по-прежнему отрывать последний бит данных, если вы не закроете его должным образом.

Кроме того, размер вашего буфера изменчив, не безопасен для потоков и действительно ничего вам не покупает. Использование стопа как летучего является безопасным, но не ожидайте, что он будет мгновенным. Другими словами, цикл может выполняться еще несколько раз, прежде чем он получит обновленное значение stop. Это особенно верно для многопроцессорных машин.

Edit_02:

Для TCPClientClass вы хотите сделать следующее (насколько я могу судить, не имея доступа к C # на данный момент).

// write all the bytes
// Then do the following

client.client.Shutdown(Shutdown.Send) // This assumes you have access to this protected member
while (stream.read(buffer, 0, READ_BUFFER_SIZE) != 0);
client.close()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...