2009-08-10 319 views
6

我有一個應用程序爲我的應用程序編寫,分佈在整個公司,通過我們的Windows 2003服務器(運行IIS 6.0)向我發送數據。小文本消息可以通過,但包含更多數據(大約20 KB)的大消息無法通過。TCP客戶端連接

我將字節緩衝區設置爲TCP客戶端的緩衝區大小。我注意到我的數據正在服務器上收到;但是,它只通過接收例程循環一次,而我的大文件總是正好是緩衝區大小的大小,或者我們服務器上的大小爲8 KB。換句話說,我的代碼只在服務器關閉套接字連接之前通過一個循環。

考慮到填充整個緩衝區可能存在問題,我嘗試將讀/寫限制爲1 KB,但這僅導致我們的服務器在關閉連接之前收到1 KB後關閉套接字。

我將服務器的錯誤信息發送回客戶端,以便我可以查看它。我從客戶端收到特定的錯誤信息是:

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

我更新了我的服務器應用程序,使底層TCP套接字將使用的「保持活動」這一行:

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.」

我們的網絡管理員告訴我,他沒有防火牆或阻止我們內部的服務器上的任意端口。

使用谷歌搜索錯誤,我發現帖子暗示人們嘗試遠程登錄到服務器。我用自己的方向遠程登錄到服務器上,但我不知道該做出響應的是什麼:

C:> telnet Welcome to Microsoft Telnet Client

Escape Character is ‘CTRL+]’

Microsoft Telnet> open cpapp 500 Connecting To cpapp…

這是我得到的。我從來沒有得到一個錯誤,微軟的Telnet屏幕最終會改變爲「按任意鍵繼續......」 - 我猜測它超時了,但我的代碼以某種方式能夠連接。

我已經嘗試了代碼中的其他端口,並通過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客戶端從不停止,所以我知道沒有錯誤。

我們的網絡管理員還沒有收到他的同事學位,所以如果事實證明是服務器的問題,請在你的描述中詳細說明如何解決這個問題,因爲我們可能不明白你在說什麼。

我很抱歉這麼長時間,但我想確保你們在那裏知道我在做什麼 - 甚至可以從我的技術中獲得一些信息!

感謝您的幫助! 〜喬

回答

6

您應該在發送的內容之前加上該內容的長度。你的循環假定所有的數據都是在循環執行之前發送的,而實際上你的循環是在數據發送時執行的。有些時候在線路上沒有數據等待,所以循環終止;同時,內容仍然通過電話線傳送。這就是爲什麼你的循環只運行一次。

+0

沒有開玩笑嗎?我想現在是有道理的。我一直在想,需要在服務器上設置一些東西。這有助於很多*!謝謝! – jp2code 2009-08-12 13:06:29

+0

我該怎麼做? 「在發送內容之前的內容長度」我沒有發送內容的長度? – 2017-11-28 08:26:48

+0

如果你不知道長度,那麼顯然你不會發送線上的長度。 – Amy 2017-11-28 22:09:03

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

如果你想同時放讀取多個上傳,你需要更多的東西:

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

你的telnet的例子是有些矛盾的代碼,你描述的行爲 - 如果你是以往任何時候都能夠在服務器上,「遠程登錄<主機名> <端口號>」上得到任何東西應該讓你到空白屏幕相當快(在CMD提示符下的Windows機器上)。所以,這是第一個奇怪的事情 - 最好用wireshark, though進行調試。

代碼的角度來看,我認爲這可能是問題與服務器上的此內線:

...而((0 < LEN)& &(ns.DataAvailable ==真));

你說你想循環,而你能夠閱讀的東西,而有一些數據可用。

但是,它可能是第二個段還沒有到達服務器,所以沒有可用的數據,所以你正在從這個循環中退出。

你應該循環接收數據,而你正在閱讀的東西,而沒有任何讀取錯誤 - 這保證即使在慢速鏈接,你會可靠地接收數據。

甲側面說明:

我注意到你的協議是請求 - 響應 - 請求 - 響應類型。它在局域網上運行良好,但是如果您將來需要在高往返時間鏈路上工作,這將成爲一個巨大的性能瓶頸(用於文件傳輸的MS SMB協議或TFTP以此方式工作) 。 (免責聲明:我沒有用C#編寫很多代碼,因此在解釋「DataAvailable()」方法時可能會出錯,請參閱此FWIW)。

編輯:上面的答案可能需要根據您的協議進行更正 - 即您需要首先閱讀文件的長度,然後閱讀該文件 - 因爲如果您逐字記錄,它會打破你完全設計它的方式。

這就是說,對於TCP,不應該假定發送方的write()操作數與接收方的read()操作數相同 - 在某些情況下可能是這種情況(沒有數據包損失,沒有納格爾) - 但在一般情況下,這是不對的。