2009-08-10 5 views
6

У меня есть приложение, которое я написал для своего приложения, распространяемого по всей компании, для отправки данных мне через наш сервер Windows 2003 (с запуском IIS 6.0). Небольшие текстовые сообщения проходят, но большие сообщения, содержащие больше данных (около 20 КБ), не проходят.TCP-соединение с клиентом

Я установил байтовый буфер в размер буфера TCP-клиента. Я заметил, что мои данные поступают на сервер; однако он только зациклился на процедуру приема один раз, и мои большие файлы всегда были точно размером с размером буфера, или 8 КБ на нашем сервере. Другими словами, мой код пропускает только один цикл до того, как сервер закроет соединение сокета.

Думаю, что может возникнуть проблема с заполнением всего буфера, я попытался ограничить чтение/запись всего на 1 КБ, но это привело только к тому, что наш сервер закрыл сокет после получения 1 КБ до закрытия соединения.

Я отправляю сообщение об ошибке сервера клиенту, чтобы я мог его просмотреть. Конкретное сообщение об ошибке, что я получаю от клиента:

“Unable to write data to the transport connection: An established connection was aborted by the software in your host machine.”

Я обновил свое серверное приложение так, что лежащий в основе сокет TCP будет использовать «держать alives» с этой линией:

client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, true); 

Теперь, когда Я попытка отправки сообщения, клиент получает сообщение об ошибке:

“Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.”

наш администратор сети сказал мне, что он не имеет брандмауэр или какие-либо порты заблокированы на нашем внутреннем сервере.

Исправлены ошибки, я нашел сообщения, предлагающие людям пытаться подключиться к серверу telnet. Я использовал их направления для телнета на сервер, но я не уверен, что сделать из ответа:

C:> telnet Welcome to Microsoft Telnet Client

Escape Character is ‘CTRL+]’

Microsoft Telnet> open cpapp 500 Connecting To cpapp…

Это все я. Я никогда не получаю сообщение об ошибке, и экран Telnet от Microsoft в конечном итоге изменится на «Нажмите любую клавишу, чтобы продолжить ...» - я думаю, это время, но мой код каким-то образом может подключиться.

Я пробовал другие порты в коде и через Telnet, включая 25, 80 и 8080. Telnet выдает порт 25, но мое приложение, похоже, читает первый цикл независимо от того, какой порт я ему скажу.

Вот мой код, который работает на клиентах:

int sendUsingTcp(string location) { 
    string result = string.Empty; 
    try { 
    using (FileStream fs = new FileStream(location, FileMode.Open, FileAccess.Read)) { 
     using (TcpClient client = new TcpClient(GetHostIP, CpAppDatabase.ServerPortNumber)) { 
     byte[] riteBuf = new byte[client.SendBufferSize]; 
     byte[] readBuf = new byte[client.ReceiveBufferSize]; 
     using (NetworkStream ns = client.GetStream()) { 
      if ((ns.CanRead == true) && (ns.CanWrite == true)) { 
      int len; 
      string AOK = string.Empty; 
      do { 
       len = fs.Read(riteBuf, 0, riteBuf.Length); 
       ns.Write(riteBuf, 0, len); 
       int nsRsvp = ns.Read(readBuf, 0, readBuf.Length); 
       AOK = Encoding.ASCII.GetString(readBuf, 0, nsRsvp); 
      } while ((len == riteBuf.Length) && (-1 < AOK.IndexOf("AOK"))); 
      result = AOK; 
      return 1; 
      } 
      return 0; 
     } 
     } 
    } 
    } catch (Exception err) { 
    Logger.LogError("Send()", err); 
    MessageBox.Show(err.Message, "Message Failed", MessageBoxButtons.OK, MessageBoxIcon.Hand, 0); 
    return -1; 
    } 
} 

Вот мой код, который работает на сервере:

SvrForm.Server = new TcpListener(IPAddress.Any, CpAppDatabase.ServerPortNumber); 

void Worker_Engine(object sender, DoWorkEventArgs e) { 
    BackgroundWorker worker = sender as BackgroundWorker; 
    string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Application.CompanyName); 
    if (Directory.Exists(path) == false) Directory.CreateDirectory(path); 
    Thread.Sleep(0); 
    string eMsg = string.Empty; 
    try { 
    SvrForm.Server.Start(); 
    do { 
     using (TcpClient client = SvrForm.Server.AcceptTcpClient()) { // waits until data is avaiable 
     if (worker.CancellationPending == true) return; 
     client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, true); 
     string location = Path.Combine(path, string.Format("Acp{0:yyyyMMddHHmmssff}.bin", DateTime.Now)); 
     byte[] buf = new byte[client.ReceiveBufferSize]; 
     try { 
      using (NetworkStream ns = client.GetStream()) { 
      if ((ns.CanRead == true) && (ns.CanWrite == true)) { 
       try { 
       int len; 
       byte[] AOK = Encoding.ASCII.GetBytes("AOK"); 
       using (FileStream fs = new FileStream(location, FileMode.Create, FileAccess.Write)) { 
        do { 
        len = ns.Read(buf, 0, client.ReceiveBufferSize); 
        fs.Write(buf, 0, len); 
        ns.Write(AOK, 0, AOK.Length); 
        } while ((0 < len) && (ns.DataAvailable == true)); 
       } 
       byte[] okBuf = Encoding.ASCII.GetBytes("Message Received on Server"); 
       ns.Write(okBuf, 0, okBuf.Length); 
       } catch (Exception err) { 
       Global.LogError("ServerForm.cs - Worker_Engine(DoWorkEvent)", err); 
       byte[] errBuf = Encoding.ASCII.GetBytes(err.Message); 
       ns.Write(errBuf, 0, errBuf.Length); 
       } 
      } 
      } 
     } 
     worker.ReportProgress(1, location); 
     } 
    } while (worker.CancellationPending == false); 
    } catch (SocketException) { 
    // See MSDN: Windows Sockets V2 API Error Code Documentation for detailed description of error code 
    e.Cancel = true; 
    } catch (Exception err) { 
    eMsg = "Worker General Error:\r\n" + err.Message; 
    e.Cancel = true; 
    e.Result = err; 
    } finally { 
    SvrForm.Server.Stop(); 
    } 
} 

Почему мое приложение не продолжать чтение от клиента TCP ? Не пренебрегал ли я установкой того, что говорит о том, что Socket остается открытым до тех пор, пока я не закончу? Код сервера никогда не видит исключения, потому что TCP-клиент никогда не останавливается, поэтому я знаю, что ошибки нет.

Наш сетевой администратор еще не получил степень Associates, поэтому, если это окажется проблемой на сервере, пожалуйста, подробно расскажите о том, как это разрешить, потому что мы можем не понимать, что вы говорите ,

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

Спасибо за помощь! ~ Джо

ответ

6

Вы должны предшествовать отправленному контенту с длиной этого содержимого. Ваш цикл предполагает, что все данные отправляются до того, как цикл будет выполняться, когда на самом деле ваш цикл выполняется при отправке данных.Будут моменты, когда нет ожиданий данных на проводе, поэтому цикл завершается; Между тем, контент по-прежнему отправляется через провод. Вот почему ваш цикл работает только один раз.

+0

Не шутите? Думаю, теперь это имеет смысл. Я все думал о том, что нужно настроить на сервере. Это помогает * много *! Спасибо! – jp2code

+0

как я могу это сделать? «предшествует отправленному контенту с длиной этого содержимого», у меня нет длины отправленного контента? –

+0

Если вы не знаете длину, то, очевидно, вы не отправляете длину на проводе. – Amy

1

Если я прочитал ваш код правильно, вы в основном получили (извините за C-стиля - я не очень хорошо с C#:

 
do 
{ 
    socket = accept(); 
    read(socket, buffer); 
}while(not_done); 

Если я правильно, то . означает, что нужно ... немного больше там Если вы хотите сериализовать, чтение каждой загрузки в последовательности, вы хотите, второй цикл:

 
do 
{ 
    socket = accept(); 
    do { read(socket, buffer); not_done_reading=...; } while (not_done_reading); 
}while(not_done); 

Если вы хотите читать загрузки нескольких Simultaniously, вам понадобится нечто большее:

 
do 
{ 
    socket = accept(); 
    if(!fork()) 
    { 
    do { read(socket, buffer); not_done_reading=...; } while (not_done_reading); 
    } 
}while(not_done); 
1

Ваш пример телнет несколько противоречит поведению кода, который вы описали - если вы когда-либо в состоянии получить что-либо на сервере, то «телнет < имя хоста > < номер_порта >» должен заставить вас пустой довольно быстро (на машине Windows в приглашении CMD). Итак, это первая странная вещь - лучшая отлаженная с wireshark, though.

Code-мудрый, я думаю, что это может быть проблема с этой внутренней линии на сервере:

... в то время как ((0 < LEN) & & (ns.DataAvailable == верно));

Вы говорите, что хотите зациклиться, пока сможете прочитать что-то, и пока есть некоторые данные.

Однако, возможно, второй сегмент еще не дошел до сервера, поэтому пока нет данных, поэтому вы выходите из этого цикла.

Вы должны получать данные, когда вы что-то читаете, и пока не было никакой ошибки чтения - это гарантирует, что даже на медленных ссылках вы будете надежно получать данные.

Примечание стороны:

Я заметил ваш протокол является типом запрос-ответ-запрос-ответ. Он хорошо работает в локальной сети, однако, если вам нужно в будущем, чтобы он работал над высокоскоростными каналами, это станет большим узким местом производительности (MS SMB-протокол, используемый для файловых передач или TFTP, работает таким образом) ,

(Отказ от ответственности: я не кодировал на C# много, так что, возможно, было ошибкой в ​​интерпретации метода DataAvailable(), возьмите этот FWIW).

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

При этом при использовании TCP не следует полагать, что количество операций write() на стороне отправителя совпадает с количеством операций read() на стороне приемника - в некоторых случаях это может быть так (без пакета потеря, ни Нагла), - но в общем случае это было бы неверно.

Смежные вопросы