TCP客户端连接

我有一个应用程序,我为我的应用程序分发在整个公司,通过我们的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); 

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

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

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

谷歌搜索错误,我发现post暗示人们试图telnet到服务器。 我用他们的方向telnet到服务器,但我不知道如何做出响应:

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

逃脱角色是’CTRL +]’

Microsoft Telnet> open 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保持打开状态的东西? 服务器代码永远不会出现exception,因为TCP客户端永远不会停止,因此我知道没有错误。

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

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

谢谢你的帮助! 〜乔

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

如果我正确地读了你的代码,你基本上得到了(抱歉c风格 – 我对c#不好:

做
 {
   socket = accept();
  读(套接字,缓冲区);
 }而(not_done);

如果我是对的,那就意味着你需要……再多一点。 如果你想要序列化,按顺序读取每个上传,你需要第二个循环:

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

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

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

您的telnet示例与您描述的代码的行为有些矛盾 – 如果您能够在服务器上获得任何内容,“telnet ”会让您快速进入空白屏幕(在Windows上)机器在CMD提示符中)。 所以,这是第一个奇怪的事情 – 最好用wireshark调试。

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

… while((0

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

但是,可能是第二个段尚未进入服务器,因此还没有可用的数据,因此您将从此循环中退出。

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

附注:

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

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

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

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