2009-08-10 10 views
6

Mam aplikację, którą napisałem dla mojej aplikacji rozpowszechnianą w całej firmie, aby wysłać dane do mnie za pośrednictwem naszego serwera Windows 2003 (z uruchomionymi usługami IIS 6.0). Małe wiadomości tekstowe przechodzą, ale większe wiadomości zawierające więcej danych (około 20 KB) nie są przesyłane.Połączenie klienta TCP

Ustawiam bufor bajtowy na rozmiar bufora klienta TCP. Zauważyłem, że moje dane były odbierane na serwerze; jednakże tylko pętle przechodzą przez procedurę odbierania jeden raz, a moje duże pliki zawsze były dokładnie rozmiaru bufora lub 8 KB na naszym serwerze. Innymi słowy, mój kod wykonuje tylko jedną pętlę, zanim serwer zamknie połączenie z gniazdem.

Myśląc, że może być problem z wypełnieniem całego bufora, próbowałem ograniczyć zapis/zapis do zaledwie 1 KB, ale to tylko spowodowało, że nasz serwer zamykał gniazdo po otrzymaniu 1 KB przed zamknięciem połączenia.

Wysyłam komunikat o błędzie serwera z powrotem do klienta, dzięki czemu mogę go wyświetlić. Konkretny komunikat o błędzie, że otrzymuję od klienta jest:

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

zaktualizowałem moją aplikację serwera tak, że ogólny Socket TCP użyłby „keep alives” z tej linii:

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

Teraz, ilekroć I próba wysyłania wiadomości, klient otrzymuje błąd:

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

Administrator Nasza sieć powiedział mi, że nie posiada żadnych portów zapory lub zablokowane na naszym serwerze wewnętrznym.

Podczas wyszukiwania błędów znalazłem wpisy sugerujące, że ludzie próbują telnetować się na serwerze. Użyłem ich wskazówek do telnetu na serwerze, ale nie jestem pewien, co zrobić z odpowiedzią:

C:> telnet Welcome to Microsoft Telnet Client

Escape Character is ‘CTRL+]’

Microsoft Telnet> open cpapp 500 Connecting To cpapp…

To wszystko, co otrzymuję. Nigdy nie dostaję błędu, a ekran Telnet Microsoftu ostatecznie zmieni się na "Naciśnij dowolny klawisz, aby kontynuować ..." - Myślę, że to się skończyło, ale mój kod jest jakoś w stanie się połączyć.

Próbowałem innych portów w kodzie i przez Telnet, w tym 25, 80 i 8080. Telnet uruchamia port 25, ale moja aplikacja wydaje się czytać pierwszą pętlę bez względu na port, który mam mu uruchomić.

Oto mój kod, który działa na klientach:

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; 
    } 
} 

Oto mój kod, który działa na serwerze:

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(); 
    } 
} 

Dlaczego nie moja aplikacja Czytaj dalej od Klienta TCP ? Czy zaniedbałem ustawienie czegoś, co mówi, że Gniazdo pozostaje otwarte, dopóki nie skończę? Kod serwera nigdy nie widzi wyjątku, ponieważ klient TCP nigdy się nie zatrzymuje, więc wiem, że nie ma błędu.

Nasz administrator sieci nie otrzymał jeszcze tytułu Associates Degree, więc jeśli okaże się, że jest to problem z Serwerem, prosimy o szczegółowe wyjaśnienie, jak rozwiązać ten problem, ponieważ możemy nie rozumieć, co mówisz .

Przepraszam za to, że jest tak długo, ale chcę się upewnić, że ludzie na zewnątrz wiedzą, co robię - a może nawet pobudzić trochę informacji z mojej techniki!

Dzięki za pomoc! ~ Joe

Odpowiedz

6

Treść przesłaną należy poprzedzić długością tej treści. Twoja pętla zakłada, że ​​wszystkie dane są wysyłane przed wykonaniem pętli, podczas gdy w rzeczywistości twoja pętla jest wykonywana podczas wysyłania danych.Będą chwile, kiedy nie ma danych oczekujących na przewód, więc pętla się kończy; w międzyczasie treść jest nadal wysyłana przez kabel. Dlatego twoja pętla działa tylko raz.

+0

Bez żartów? Myślę, że ma to teraz sens. Cały czas myślałem, że coś trzeba skonfigurować na serwerze. To pomaga * dużo *! Dziękuję Ci! – jp2code

+0

jak to zrobić? "poprzedza wysłaną zawartość długością tej treści" nie mam długości wysłanej treści? –

+0

Jeśli nie znasz długości, to oczywiście nie wysyłasz długości na przewodzie. – Amy

1

Jeśli czytam kod poprawnie, masz w zasadzie dostał (przepraszam za c-style - Nie jestem dobra z C#:

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

Jeśli się nie mylę, to . oznacza to, że potrzebujemy trochę więcej ... tam Jeśli ma to być szeregowane, czytając każdy przesłać w kolejności, będziemy chcieli drugą pętlę:

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

Jeśli chcesz przeczytać kilka przesłane simultaniously, potrzebujesz czegoś więcej niż:

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

Twój przykład telnet jest nieco sprzeczne z zachowaniem kodu, który opisujesz - jeśli kiedykolwiek mógł dostać coś na serwerze, „telnet <hostname> <numerportu>” powinien dostać się do pustego ekran dość szybko (na komputerze Windows w wierszu CMD). To jest pierwsza dziwna rzecz - najlepiej debugowana z wireshark, though.

Kod mądry, myślę, że może to być problem z tym wewnętrznej linii na serwerze:

... while ((0 < len) & & (ns.DataAvailable == true));

Mówisz, że chcesz zapętlić, podczas gdy ty byłeś w stanie coś przeczytać i gdy jest trochę danych dostępnych.

Możliwe jednak, że drugi segment nie dotarł jeszcze do serwera, więc nie ma jeszcze dostępnych danych, więc wypadasz z tej pętli.

Powinieneś zapętlać odbieranie danych podczas czytania czegoś i gdy nie było żadnego błędu odczytu - gwarantuje to, że nawet na wolnych linkach będziesz niezawodnie odbierać dane.

Na marginesie:

Zauważyłem swój protokół jest typu żądanie-odpowiedź-request-response. Działa dobrze w sieci LAN, jednak jeśli będziesz potrzebować w przyszłości, aby działała przez łącza o wysokiej przepustowości, stanie się to wąskim gardłem wydajności (protokół MS SMB używany do filetransferów lub TFTP działa w ten sposób) .

(Zastrzeżenie: Nie koduję w języku C# wiele, więc mogłem pomylić się z interpretacją metody "DataAvailable()", weź ten FWIW).

Edycja: pewnie moja powyższa odpowiedź musiałaby zostać poprawiona zgodnie z protokołem - tzn. Najpierw musisz przeczytać długość pliku, a następnie przeczytać plik - ponieważ jeśli weźmiesz go dosłownie, to złamie sposób zaprojektowałeś go całkowicie.

To powiedziawszy, przy TCP nie należy nigdy zakładać, że liczba operacji write() po stronie nadawcy jest taka sama jak liczba operacji read() po stronie odbiorcy - w niektórych przypadkach może tak być (brak pakietu strata, brak Walkera) - ale w ogólnym przypadku nie byłaby to prawda.

Powiązane problemy