C# TCP 客户端连接

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1256822/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 14:18:31  来源:igfitidea点击:

TCP Client Connection

c#tcpstream

提问by jp2code

I have an application that I have written for my application distributed throughout the company to send data to me through our Windows 2003 server (running IIS 6.0). Small text messages get through, but larger messages containing more data (about 20 KB) are not getting through.

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

I set the byte buffer to the TCP Client's buffer size. I noticed that my data was being received on the server; however, it only looped through the receive routine once, and my large files were always exactly the size of the buffer size, or 8 KB on our server. In other words, my code only makes it through one loop before the server closes the socket connection.

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

Thinking there might be an issue with filling the whole buffer, I tried limiting my read/writes to just 1 KB but this only resulted in our server closing the socket after receiving 1 KB before closing the connection.

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

I send the server's error message back to the client so I can view it. The specific error message that I receive from the client is:

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

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

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

I updated my server application so that the underlying TCP Socket would use “keep alives” with this line:

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

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

Now, whenever I attempt sending a message, the client receives the error:

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

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

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

Our Network Administrator has told me that he does not have a firewall or any ports blocked on our internal server.

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

Googling the errors, I found posts suggesting people try to telnet into the server. I used their directions to telnet into the server, but I am not sure what to make of the response:

谷歌搜索错误,我发现帖子建议人们尝试 telnet 进入服务器。我使用他们的指示 telnet 进入服务器,但我不确定如何处理响应:

C:> telnet Welcome to Microsoft Telnet Client

Escape Character is ‘CTRL+]'

Microsoft Telnet> open cpapp 500 Connecting To cpapp…

C:> telnet 欢迎使用 Microsoft Telnet 客户端

转义字符是 'CTRL+]'

Microsoft Telnet> open cpapp 500 Connecting To cpapp...

This is all I get. I never get an error, and Microsoft's Telnet screen will eventually change to “Press any key to continue…” – I guess it times out, but my code is somehow able to connect.

这就是我得到的全部。我从来没有收到错误消息,Microsoft 的 Telnet 屏幕最终将更改为“按任意键继续...”——我猜它超时了,但我的代码以某种方式能够连接。

I have tried other ports in code and through Telnet including 25, 80, and 8080. Telnet kicks out port 25, but my application seems to read the first loop no matter what port I tell it to run.

我已经在代码中和通过 Telnet 尝试了其他端口,包括 25、80 和 8080。Telnet 将端口 25 踢出,但无论我告诉它运行哪个端口,我的应用程序似乎都在读取第一个循环。

Here is my code that runs on the clients:

这是我在客户端上运行的代码:

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

Here is my code that runs on the server:

这是我在服务器上运行的代码:

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

Why doesn't my application continue reading from the TCP Client? Have I neglected to set something that tells the Socket to stay open until I have finished? The server code never sees an exception because the TCP Client is never stopped, so I know there is no error.

为什么我的应用程序不继续从 TCP 客户端读取?我是否忽略了设置告诉 Socket 在我完成之前保持打开状态的东西?服务器代码永远不会看到异常,因为 TCP 客户端永远不会停止,所以我知道没有错误。

Our Network Administrator has not received his Associates Degree yet, so if it turns out to be an issue with the Server, kindly be detailed in your description of how to resolve this, because we might not understand what you're saying.

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

I apologize for this being so long, but I want to make sure you folks out there know what I'm doing - and maybe even gleam some information from my technique!

我为这么长时间道歉,但我想确保你们那里的人知道我在做什么 - 甚至可能从我的技术中获得一些信息!

Thanks for helping! ~Joe

感谢您的帮助!~乔

采纳答案by Amy

You should precede the sent content with the length of that content. Your loop assumes that all of the data is sent before the loop executes, when in reality your loop is executing as the data is sent. There will be times when there is no data waiting on the wire, so the loop terminates; meanwhile, content is still being sent across the wire. That's why your loop is only running once.

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

回答by atk

If I read your code correctly, you've basically got (sorry for the c-style - I'm not good with c#:

如果我正确阅读了你的代码,你基本上已经得到了(对不起,c 风格 - 我不擅长 c#:

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

If I'm correct, then it means you need... a little more in there. If you want it to be serialized, reading each upload in sequence, you'll want a second loop:

如果我是对的,那么这意味着你需要……多一点。如果您希望它被序列化,按顺序读取每个上传,您将需要第二个循环:

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

If you want to read multiple uploads simultaniously, you'll need something more like:

如果你想同时阅读多个上传,你需要更多的东西:

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

回答by Andrew Y

Your telnet example is somewhat contradictory to the behaviour of the code that you describe - if you are ever able to get anything on the server, the "telnet <hostname> <portnumber>" should get you to a blank screen pretty quickly (on windows machine in the CMD prompt that is). So, this is the first strange thing - best debugged with wireshark, though.

您的 telnet 示例与您描述的代码的行为有些矛盾-如果您能够在服务器上获得任何信息,则“telnet <hostname> <portnumber>”应该会让您很快进入空白屏幕(在 Windows 上)机器在 CMD 提示符下即是)。所以,这是第一个奇怪的事情——最好用wireshark调试,不过

Code-wise, I think it may be problem with this inner line on the server:

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

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

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

You say that you want to loop while you were able to read something and while there is some data available.

您说您想在能够读取某些内容并且有一些数据可用时进行循环。

However, it may be that the second segment has not made it to the server yet, so there is no data available yet, so you are falling out from this loop.

但是,可能是第二段还没有到达服务器,所以还没有可用的数据,所以你会退出这个循环。

You should loop receiving data while you are reading something and while there has not been any read error - this guarantees that even on the slow links you will reliably receive the data.

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

A side note:

旁注:

I notice your protocol is request-response-request-response type. It works well on the LAN, however if you need in the future to make it work over the high-round-trip-time links, this will become a big performance bottleneck (MS SMB protocol used for filetransfers or TFTP works in this fashion).

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

(Disclaimer: I didn't code in C# much, so might have been wrong in the interpretation of "DataAvailable()" method, take this FWIW).

(免责声明:我没有用 C# 编写太多代码,所以在“DataAvailable()”方法的解释中可能是错误的,以这个 FWIW 为例)。

Edit: probably my answer above would need to be corrected according to your protocol - i.e. you'd need to first read the length of the file, and then read the file - since if you take it verbatim it will break the way you designed it completely.

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

That said, with TCP one should never assume the number of write() operations on the sender side is the same as number of read() operations on the receiver side - in some cases it may be the case (no packet loss, no Nagle) - but in the general case it would not be true.

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