TCP客户端连接

时间:2009-08-10 19:44:23

标签: c# tcp stream

我有一个应用程序,我为我的应用程序分发了整个公司,通过我们的Windows 2003服务器(运行IIS 6.0)向我发送数据。小文本消息通过,但包含更多数据(大约20 KB)的更大消息无法通过。

我将字节缓冲区设置为TCP客户端的缓冲区大小。我注意到我的数据正在服务器上收到;但是,它只通过接收例程循环一次,我的大文件总是与缓冲区大小完全相同,或者是我们服务器上的8 KB。换句话说,我的代码只在服务器关闭套接字连接之前通过一个循环。

认为填充整个缓冲区可能存在问题,我尝试将读取/写入限制为仅1 KB,但这只会导致我们的服务器在关闭连接之前收到1 KB后关闭套接字。

我将服务器的错误消息发送回客户端,以便我可以查看它。我从客户端收到的具体错误消息是:

  

“无法将数据写入传输   连接:已建立的连接   被你的软件中止了   主机。“

我更新了我的服务器应用程序,以便底层的TCP Socket将使用“keep alives”这一行:

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

现在,每当我尝试发送消息时,客户端都会收到错误:

  

“无法将数据写入传输   连接:现有连接是   被远程主机强行关闭。“

我们的网络管理员告诉我他的内部服务器上没有防火墙或任何端口。

搜索错误,我发现帖子暗示人们试图远程登录到服务器。我用他们的指示telnet到服务器,但我不知道该怎么回应:

  

C:> telnet欢迎来到Microsoft   Telnet客户端

     

逃脱角色是'CTRL +]'

     

Microsoft Telnet>打开cpapp 500   连接到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客户端读取?在完成之前,我是否忽略了设置让Socket保持打开的东西?服务器代码永远不会出现异常,因为TCP Client永远不会停止,因此我知道没有错误。

我们的网络管理员尚未获得他的会员学位,所以如果结果是服务器的问题,请详细说明如何解决这个问题,因为我们可能不明白你在说什么。

我为这么久的事情道歉,但我想确保你们那里的人知道我在做什么 - 甚至可能从我的技术中汲取一些信息!

感谢您的帮助! 〜乔

3 个答案:

答案 0 :(得分:6)

您应该在发送的内容之前加上该内容的长度。你的循环假设所有数据都是在循环执行之前发送的,而实际上你的循环是在数据发送时执行的。有时候线路上没有数据等待,所以循环终止;同时,内容仍在通过网络发送。这就是你的循环只运行一次的原因。

答案 1 :(得分: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);

答案 2 :(得分:1)

您的telnet示例与您描述的代码的行为有些矛盾 - 如果您能够在服务器上获得任何内容,“telnet&lt; hostname&gt;&lt; portnumber&gt;”应该让你很快到达一个空白的屏幕(在CMD提示符的Windows机器上)。所以,这是第一个奇怪的事情 - 最好使用wireshark, though调试。

代码方面,我认为服务器上的这条内线可能有问题:

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

你说当你能够阅读某些内容并且有一些数据可用时你想要循环。

然而,可能是第二段尚未进入服务器,因此目前还没有可用的数据,所以你正在从这个循环中退出。

您在读取内容时应该循环接收数据并且没有任何读取错误 - 这可以保证即使在慢速链接上也可以可靠地接收数据。

旁注:

我注意到你的协议是请求 - 响应 - 请求 - 响应类型。它在局域网上运行良好,但如果您将来需要使其在高往返时间链路上工作,这将成为一个巨大的性能瓶颈(用于文件传输的MS SMB协议或TFTP以这种方式工作)

(免责声明:我没有在C#中编码太多,所以在解释“DataAvailable()”方法时可能出错了,请参考此FWIW。)

编辑:可能我的答案需要根据您的协议进行纠正 - 即您需要先读取文件的长度,然后再读取文件 - 因为如果您逐字逐句地将它打破了方式你完全设计了它。

也就是说,使用TCP,不应该假设发送方的write()操作数与接收方的read()操作数相同 - 在某些情况下可能是这种情况(没有丢包) ,没有Nagle) - 但在一般情况下,它不会是真的。