Файл с использованием .net сокетов, проблема переноса - PullRequest
1 голос
/ 19 апреля 2010

У меня есть клиент и сервер, клиент отправляет файл на сервер. Когда я передаю файлы на моем компьютере (локально), все в порядке (попробуйте отправить файл размером более 700 МБ).

Когда я пытаюсь отправить файл через Интернет моему другу, в конце отправки появляется ошибка на сервере «Входная строка не в правильном формате». Эта ошибка появляется в этом выражении fSize = Convert::ToUInt64(tokenes[0]); - и я не против, что это появляется. Файл должен быть передан и дождаться другой передачи

PS: извините за слишком много кода, но я хочу найти решение

   private: void CreateServer()
   {

    try{
     IPAddress ^ipAddres = IPAddress::Parse(ipAdress);
     listener = gcnew System::Net::Sockets::TcpListener(ipAddres, port);
     listener->Start();
     clientsocket =listener->AcceptSocket();
     bool keepalive = true;
     array<wchar_t,1> ^split = gcnew array<wchar_t>(1){ '\0' };
     array<wchar_t,1> ^split2 = gcnew array<wchar_t>(1){ '|' };

     statusBar1->Text = "Connected" ;
     //
     while (keepalive)
     {
      array<Byte>^ size1 = gcnew array<Byte>(1024); 
      clientsocket->Receive(size1);
      System::String ^notSplited = System::Text::Encoding::GetEncoding(1251)->GetString(size1);

      array<String^> ^ tokenes = notSplited->Split(split2);
      System::String ^fileName = tokenes[1]->ToString();
      statusBar1->Text = "Receiving file" ; 
      unsigned long  fSize = 0;


                             //IN THIS EXPRESSIN APPEARS ERROR 
      fSize = Convert::ToUInt64(tokenes[0]);

      if (!Directory::Exists("Received"))
       Directory::CreateDirectory("Received");

      System::String ^path = "Received\\"+ fileName;
                      while (File::Exists(path))
      {
       int dotPos = path->LastIndexOf('.');
       if (dotPos == -1)
       {
        path += "[1]";
       }
       else
       {
        path = path->Insert(dotPos, "[1]");
       }
      }

      FileStream ^fs = gcnew FileStream(path, FileMode::CreateNew, FileAccess::Write);
      BinaryWriter ^f = gcnew BinaryWriter(fs);
       //bytes received
      unsigned long processed = 0;

      pBarFilesTr->Visible = true;
      pBarFilesTr->Minimum = 0;
      pBarFilesTr->Maximum = (int)fSize;
      // Set the initial value of the ProgressBar.
      pBarFilesTr->Value = 0; 
      pBarFilesTr->Step = 1024;

      //loop for receive file
      array<Byte>^ buffer = gcnew array<Byte>(1024); 
      while (processed < fSize) 
      {
       if ((fSize - processed) < 1024)
       {
        int bytes ;
        array<Byte>^ buf = gcnew array<Byte>(1024); 
        bytes = clientsocket->Receive(buf);
        if (bytes != 0)
        {
         f->Write(buf, 0, bytes);
         processed = processed + (unsigned long)bytes;
         pBarFilesTr->PerformStep();
        }
        break;                            
       }
       else
       {
        int bytes = clientsocket->Receive(buffer);
        if (bytes != 0)
        {
         f->Write(buffer, 0, 1024);
         processed = processed + 1024;
         pBarFilesTr->PerformStep();
        }
        else break;
       }                        
      }

      statusBar1->Text = "File was received" ;
      array<Byte>^ buf = gcnew array<Byte>(1); 
      clientsocket->Send(buf,buf->Length,SocketFlags::None);
      f->Close();
      fs->Close();
      SystemSounds::Beep->Play();
     }
    }catch(System::Net::Sockets::SocketException ^es)
    {
     MessageBox::Show(es->ToString());
    }
    catch(System::Exception ^es)
    {
     MessageBox::Show(es->ToString());
    }
   }

private: void CreateClient()
{

                       clientsock = gcnew System::Net::Sockets::TcpClient(ipAdress, port);
                       ns = clientsock->GetStream();
  sr = gcnew StreamReader(ns);
  statusBar1->Text = "Connected" ;

}

private:void Send()
   {
    try{
     OpenFileDialog ^openFileDialog1 = gcnew OpenFileDialog();
     System::String ^filePath = "";
     System::String ^fileName = "";

     //file choose dialog
     if (openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK)
     {
      filePath = openFileDialog1->FileName; 
      fileName = openFileDialog1->SafeFileName;
     }
     else
     {
      MessageBox::Show("You must select a file", "Error",
       MessageBoxButtons::OK, MessageBoxIcon::Exclamation);
      return;
     }
     statusBar1->Text = "Sending file" ; 

     NetworkStream ^writerStream = clientsock->GetStream();
     System::Runtime::Serialization::Formatters::Binary::BinaryFormatter ^format = 
      gcnew System::Runtime::Serialization::Formatters::Binary::BinaryFormatter();

     array<Byte>^ buffer = gcnew array<Byte>(1024);
     FileStream ^fs = gcnew FileStream(filePath, FileMode::Open);
     BinaryReader ^br = gcnew BinaryReader(fs);
                 //file size
     unsigned long fSize = (unsigned long)fs->Length;
     //transfer file size + name
     bFSize = Encoding::GetEncoding(1251)->GetBytes(Convert::ToString(fs->Length+"|"+fileName+"|"));
     writerStream->Write(bFSize, 0, bFSize->Length);

     //status bar
     pBarFilesTr->Visible = true;
     pBarFilesTr->Minimum = 0;
     pBarFilesTr->Maximum = (int)fSize;
     pBarFilesTr->Value = 0; // Set the initial value of the ProgressBar.
     pBarFilesTr->Step = 1024;

                 //bytes transfered
     unsigned long processed = 0; 
     int bytes = 1024;
     //loop for transfer
     while (processed < fSize) 
     {
      if ((fSize - processed) < 1024)
      {
       bytes = (int)(fSize - processed);
       array<Byte>^ buf = gcnew array<Byte>(bytes);
       br->Read(buf, 0, bytes);

       writerStream->Write(buf, 0, buf->Length); 
       pBarFilesTr->PerformStep();
       processed = processed + (unsigned long)bytes;
       break;
      }
      else
      {
       br->Read(buffer, 0, 1024);
       writerStream->Write(buffer, 0, buffer->Length);
       pBarFilesTr->PerformStep();
       processed = processed + 1024;
      }               
     }
     array<Byte>^ bufsss = gcnew array<Byte>(100);
     writerStream->Read(bufsss,0,bufsss->Length);

 statusBar1->Text = "File was sent" ;
 btnSend->Enabled = true;
 fs->Close();
 br->Close();
 SystemSounds::Beep->Play();
 newThread->Abort();
 }
 catch(System::Net::Sockets::SocketException ^es)
 {
  MessageBox::Show(es->ToString());
 }
  }

ОБНОВЛЕНИЕ: 2Ben Voigt - хорошо, я могу добавить проверку, если clientsocket->Receive(size1); равно нулю, но почему он начинает получать данные снова, в конце получения.

ОБНОВЛЕНИЕ: После добавления этой проверки проблема остается. И WIN RAR SAY ОТКРЫТИЯ АРХИВА - неожиданный конец файла!

ОБНОВЛЕНИЕ: 2Kevin - http://img153.imageshack.us/img153/3760/erorr.gif Я думаю, что он продолжает получать некоторые байты от клиента (который остается в потоке), но почему? - существует цикл while (processed < fSize)

ОБНОВЛЕНИЕ: 2Ben Voigt -i сделал это исправление processed += bytes; и файл успешно передан. Спасибо! Я не очень хорошо разбираюсь в английском, и я не понимаю, что вы имеете в виду, когда говорите: «Подумайте, что произойдет, если ваше начальное чтение зацепит часть данных файла ...» Что означает зацепки? А какие исходные данные вы имеете в виду?

1 Ответ

3 голосов
/ 19 апреля 2010

Не игнорируйте возвращаемое значение из clientsocket->Receive(size1).

DOC: "Socket.Receive Метод (Byte[]) "

РЕДАКТИРОВАТЬ: подумайте, что произойдет, если ваше первоначальное чтение зацепит часть данных файла. Также обратите внимание на то, что произойдет, если ваше последнее чтение (которое по какой-то причине по-прежнему составляет 1024 байта вместо количества оставшихся байтов) зацепляет часть заголовка для следующего запроса.

РЕДАКТИРОВАТЬ: Вы также еще не сделали ничего полезного с возвращаемым значением из Receive. Ваша строка кода:

processed = processed + 1024;

должно быть

processed += bytes;

РЕДАКТИРОВАТЬ: «коряги» означает «захват» или «захват»

У вас есть:

clientsocket->Receive(size1);

и чуть позже:

clientsocket->Receive(buf);

и вы предполагаете, что все данные заголовка в size1, а все данные файла в buf. Это не безопасное предположение о потоковом сокете. Сокеты датаграмм сохраняют границы сообщений, сокеты потоков, такие как TCP, - нет. Фактически алгоритм Nagle может даже привести к тому, что первая часть данных файла будет помещена в тот же сетевой пакет, что и заголовок, но даже если этого не произойдет, стек TCP получателя отбрасывает границы пакета и просто помещает объекты в один большой приемный буфер.

...