簡體   English   中英

TCP客戶端連接

[英]TCP Client Connection

我有一個為整個應用程序編寫的應用程序,該應用程序分布在整個公司中,以通過Windows 2003服務器(運行IIS 6.0)向我發送數據。 小型文本消息通過,但是包含更多數據(大約20 KB)的較大消息沒有通過。

我將字節緩沖區設置為TCP客戶端的緩沖區大小。 我注意到服務器上已收到我的數據。 但是,它僅循環執行一次接收例程,而且我的大文件始終與緩沖區大小完全相同,即服務器上的8 KB。 換句話說,我的代碼只能在服務器關閉套接字連接之前經過一個循環。

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

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

“無法將數據寫入傳輸連接:已建立的連接已被主機中的軟件中止。”

我更新了服務器應用程序,以便基礎TCP套接字在此行中使用“保持活動”:

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

現在,每當我嘗試發送消息時,客戶端都會收到錯誤:

“無法將數據寫入傳輸連接:遠程主機強行關閉了現有連接。”

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

搜尋錯誤,發現帖子暗示人們嘗試通過telnet進入服務器。 我使用了他們的指令將telnet接入服務器,但是不確定響應如何:

C:> telnet歡迎使用Microsoft Telnet客戶端

轉義字符為'CTRL +]'

Microsoft Telnet>打開cpapp 500連接到cpapp…

這就是我所得到的。 我從沒有收到錯誤,Microsoft的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客戶端讀取? 我是否忽略了一些設置,以使Socket保持打開狀態直到完成? 服務器代碼永遠不會出現異常,因為TCP客戶端永遠不會停止,因此我知道沒有錯誤。

我們的網絡管理員尚未獲得他的副學士學位,因此,如果發現這與服務器有關,請在您如何解決此問題的說明中進行詳細說明,因為我們可能無法理解您的意思。

對於這么長時間,我深表歉意,但我想確保你們那里的人們知道我在做什么-甚至可以從我的技術中獲取一些信息!

感謝您的幫助! 〜喬

您應該在發送的內容之前加上該內容的長度。 您的循環假定所有數據都在循環執行之前發送,而實際上您的循環是在發送數據時執行的。 有時,線路上沒有數據等待,因此循環終止。 同時,內容仍通過網絡發送。 這就是循環僅運行一次的原因。

如果我正確地閱讀了您的代碼,那么您基本上已經獲得(對不起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);

您的telnet示例與您描述的代碼的行為有些矛盾-如果您能夠在服務器上獲取任何內容,則“ telnet <主機名> <端口號>”應該可以使您很快進入黑屏(在Windows上在CMD提示符下顯示)。 因此,這是第一個奇怪的事情-最好使用Wireshark進行調試。

在代碼方面,我認為服務器上的此內線可能有問題:

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

您說您想在能夠讀取某些內容並且有一些可用數據時循環播放。

但是,可能第二段尚未到達服務器,因此尚無可用數據,因此您將退出此循環。

當您正在讀取內容且沒有任何讀取錯誤時,應該循環接收數據-這樣可以保證即使在慢速鏈接上,您也可以可靠地接收數據。

旁注:

我注意到您的協議是請求-響應-請求-響應類型。 它在LAN上運行良好,但是,如果將來您需要使其在高往返時間鏈路上工作,這將成為很大的性能瓶頸(用於文件傳輸或TFTP的MS SMB協議以這種方式工作) 。

(免責聲明:我沒有用C#編寫太多代碼,因此在解釋“ DataAvailable()”方法時可能是錯誤的,請使用此FWIW)。

編輯:也許我上面的答案需要根據您的協議進行更正-即您需要先讀取文件的長度,然后再讀取文件-因為如果逐字閱讀,它將破壞您設計文件的方式完全。

也就是說,對於TCP,永遠不要假設發送方的write()操作數與接收方的read()操作數相同-在某些情況下,情況可能是這樣(沒有數據包丟失,沒有Nagle )-但通常情況下並非如此。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM