TCP-клиентское соединение - PullRequest
       12

TCP-клиентское соединение

7 голосов
/ 10 августа 2009

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

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

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

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

«Невозможно записать данные в транспорт соединение: установленное соединение был прерван программным обеспечением в вашем хост-машина. ”

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

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

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

«Невозможно записать данные в транспорт соединение: существующее соединение было принудительно закрывается удаленным хостом. ”

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

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

C:> telnet Добро пожаловать в Microsoft Клиент Telnet

Escape-символ ‘CTRL +]’

Microsoft Telnet> open cpapp 500 Подключение к 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-клиента? Я забыл установить что-то, что говорит Сокету, чтобы оставаться открытым, пока я не закончил? Код сервера никогда не видит исключения, поскольку клиент TCP никогда не останавливается, поэтому я знаю, что ошибки нет.

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

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

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

Ответы [ 3 ]

6 голосов
/ 11 августа 2009

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

1 голос
/ 10 августа 2009

Ваш пример telnet несколько противоречит поведению кода, который вы описываете - если вы когда-нибудь сможете получить что-либо на сервере, "telnet " должен вывести вас на пустой экран довольно быстро (на машине windows в приглашении CMD то есть). Итак, это первая странная вещь - лучше всего отлажена с wireshark, хотя .

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

... while ((0

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

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

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

Примечание:

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

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

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

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

1 голос
/ 10 августа 2009

Если я правильно прочитал ваш код, вы в основном получили (извините за стиль 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);

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

do
{
  socket = accept();
  if( !fork() )
  {
    do { read(socket, buffer); not_done_reading=...; } while (not_done_reading);
  }
}while(not_done);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...