Основная проблема в вашем коде заключается в том, что ваш сервер (файловый хост) пренебрег чтением из сокета, в который записывает файл, и поэтому не имеет возможности обнаружить, не говоря уже о ожидании, клиент закрывает соединение.
Код мог бы быть в порядке лучше, но, как минимум, вы могли бы заставить его работать, добавив что-то вроде этого непосредственно перед вашим client.Close();
утверждением:
// Indicate the end of the bytes being sent
ns.Socket.Shutdown(SocketShutdown.Send);
// arbitrarily-sized buffer...most likely nothing will ever be written to it
byte[] buffer = new byte[4096];
int byteCount;
while ((byteCount = ns.Read(buffer, 0, buffer.Length)) > 0)
{
// ignore any data read here
}
Когда конечная точка инициирует постепенное закрытие (например, путем вызова Socket.Shutdown(SocketShutdown.Send);
), это позволит сетевому уровню идентифицировать конец потока данных. Как только другая конечная точка прочитает все оставшиеся байты, которые отправила удаленная конечная точка, следующая операция чтения завершится с нулевой длиной байта. Это сигнал другой конечной точки о достижении конца потока и о том, что пора закрывать соединение.
Любая конечная точка может инициировать постепенное закрытие с причиной отключения «send». Другая конечная точка может подтвердить ее, как только она закончила отправку того, что она хочет отправить, используя причину отключения «обоих», когда обе конечные точки могут закрыть свои сокеты (или потоки или прослушиватели или любой другой более высокий уровень) абстракция, они могут использовать оболочку сокета).
Конечно, в правильно реализованном протоколе вы заранее знаете, будут ли какие-либо данные когда-либо отправляться удаленной конечной точкой. Если вы знаете, что никогда не будет, вы могли бы обойтись без буфера нулевой длины, и если вы знаете, что некоторые данные могут быть отправлены обратно от клиента, то вы на самом деле делаете что-то с этими данными ( в отличие от пустого тела l oop, приведенного выше).
В любом случае вышеприведенное является строго kludge , чтобы получить уже введенный в действие код, который вы отправили на работу. Пожалуйста, не принимайте это за нечто, предназначенное для того, чтобы его можно было увидеть в коде качества производства.
Несмотря на это, код, который вы опубликовали, далек от того, чтобы быть настолько хорошим. Вы не просто реализуете базовое TCP-соединение c, но, по-видимому, пытаетесь переопределить протокол HTTP. В этом нет никакого смысла, поскольку в NET уже встроены функции HTTP-сервера (см., Например, System. Net .HttpListener ). Если вы намереваетесь заново изобрести HTTP-сервер, вам потребуется гораздо больше кода, чем вы опубликовали. Отсутствие одной только обработки ошибок является серьезным недостатком и вызовет всевозможные головные боли.
Если вы собираетесь писать низкоуровневый сетевой код, вам следует провести лот дополнительных исследований и эксперименты. Одним из очень хороших ресурсов является FAQ программиста Winsock . Конечно, основное внимание уделяется программистам, ориентирующимся на Winsock API. Но там также имеется масса информации общего назначения, и в любом случае все различные API-интерфейсы сокетов очень похожи, поскольку все они основаны на одних и тех же низкоуровневых концепциях.
Вы также можете хочу просмотреть различные существующие вопросы и ответы по переполнению стека. Вот несколько примеров, связанных с вашей конкретной проблемой c: Как правильно использовать TPL с TcpClient? Отправить большой файл по TCP-соединению
Будьте осторожны, хотя. Там почти столько же плохих советов, сколько и хороших. Там нет недостатка в людях, которые ведут себя так, как будто они являются экспертами в сетевом программировании, когда они этого не делают, поэтому принимайте все, что вы прочитали, с небольшим количеством соли (включая мой совет выше!).