【问题标题】:sending a large amount of data throught TCP socket通过 TCP 套接字发送大量数据
【发布时间】:2012-10-13 03:53:24
【问题描述】:

这是我在这个论坛上发布的第一个问题,我是 c# 世界的初学者,所以这对我来说有点令人兴奋,但是我在通过套接字发送大量数据时遇到了一些问题,所以这个是关于我的问题的更多详细信息:

我正在通过 TCP 套接字发送 5 Mo 的二进制图像,在接收部分我正在保存结果(接收到的数据)并且只得到 1.5 Mo ==> 数据已丢失(我比较了原始和生成的文件,它向我展示了丢失的部分) 这是我使用的代码:

private void senduimage_Click(object sender, EventArgs e)
{
    if (!user.clientSocket_NewSocket.Connected)
    {
        Socket clientSocket_NewSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        user.clientSocket_NewSocket = clientSocket_NewSocket;
        System.IAsyncResult _NewSocket = user.clientSocket_NewSocket.BeginConnect(ip_address, NewSocket.Transceiver_TCP_Port, null, null);
        bool successNewSocket = _NewSocket.AsyncWaitHandle.WaitOne(2000, true);
     }
     byte[] outStream = System.Text.Encoding.ASCII.GetBytes(Uimage_Data);
     user.clientSocket_NewSocket.Send(outStream);
 }

在论坛上他们说将数据分成块,这是一个解决方案,如果是我该怎么做,我试过了,但它没有用!

【问题讨论】:

标签: c# .net sockets


【解决方案1】:

可能您还没有阅读 C# 中有关套接字使用的文档。 (http://msdn.microsoft.com/en-us/library/ms145160.aspx)

内部缓冲区无法存储您提供给发送方法的所有数据。您的问题的一个可能解决方案可能是您所说的将数据分成块。

int totalBytesToSend = outstream.length;     int bytesSend = 0;
while(bytesSend < totalBytesToSend )
    bytesSend+= user.clientSocket_NewSocket.Send(outStream, bytesSend, totalBytesToSend - bytesSend,...);

【讨论】:

  • 这仅适用于Send (buffer, flags) 重载。 Send (buffer) 没有取消这个限制。
  • 确实,Send(byte[] buffer,int offset,int size, SocketFlags socketFlags)
  • 非常感谢您的快速回复,我没想到会像您一样快速回答:) 我已经尝试了几乎所有的方法,但我仍然有同样的问题一些数据仍然丢失,我认为它与我的文件类型有关:它是一个二进制文件,所以我尝试发送 file.dat 并且它工作正常。我的问题是:为什么在不丢失一些数据的情况下无法发送二进制文件,我该如何处理来解决这个问题?谢谢;)
  • 我建议发布您的客户端代码。与您的服务器代码类似,您需要等到 Receive 方法返回 0 以确保收到所有数据。
【解决方案2】:

有很多不同的解决方案,但分块通常是一个很好的解决方案,您可以盲目地执行此操作,不断填充临时缓冲区,然后将其放入某个有状态的缓冲区,直到您遇到任意令牌或缓冲区未完全完成已满,或者您可以遵守每个 tcp message 的某种约定(消息是要接收的整体数据)。

如果你打算做某种契约,那么做一些事情,比如消息的前 N ​​个字节是描述符,你可以根据需要将其设置为大或小,但你的临时缓冲区只会读取这个从流中提前调整大小。

典型的标题可能是这样的:

public struct StreamHeader // 5 bytes
{
   public byte MessageType {get;set;} // 1 byte
   public int MessageSize {get;set;}  // 4 bytes
}

因此,如果它足够小,则将完整的消息大小分配给临时缓冲区并将其全部读取,或者如果您认为它太大,将其分成多个部分并继续读取,直到您收到的总字节数匹配标题结构的 MessageSize 部分。

【讨论】:

  • 您的一般解释是正确的,但是我必须指出,您指出上述结构是“5 个字节”。仅当您应用 StructLayoutAttribute 并将 LayoutKind.Sequential 参数结合 Pack 属性设置为 1 时,这才是正确的。
  • 是的,我确实在它前面加上了“类似的东西”,因为我在打字时正在编造它,不过你提出的观点很好。
【解决方案3】:

我怀疑你的问题之一是你没有打电话给EndConnect。来自 MSDN 文档:

异步BeginConnect操作必须通过调用EndConnect方法完成。

还有,等待:-

bool successNewSocket = _NewSocket.AsyncWaitHandle.WaitOne(2000, true);

可能总是错误的,因为没有将事件设置为信号状态。通常,您会为BeginConnect 函数指定一个回调函数,并在回调中调用EndConnect 并将事件的状态设置为已发出信号。参见示例代码on this MSDN page

更新

我想我看到了另一个问题:-

byte[] outStream = System.Text.Encoding.ASCII.GetBytes(Uimage_Data);

我不知道 Uimage_Data 是什么类型,但我真的不认为你想将它转换为 ASCII。数据中的零可能表示数据字节结束(或者可能是 26 或其他 ASCII 码)。关键是,编码过程很可能会改变数据。

您能否提供Uimage_Data 对象的类型?

【讨论】:

  • 或者只使用同步的Connect方法,反正他就是这么做的……
  • @FrancoisNel:同步版本没有可以指定的超时时间,尽管底层提供程序可能在某些时候超时。
  • 非常感谢您的快速回复,我没想到会像您一样快速回答:) 我已经尝试了几乎所有的方法,但我仍然有同样的问题一些数据仍然丢失,我认为它与我的文件类型有关:它是一个二进制文件,所以我尝试发送 file.dat 并且它工作正常。我的问题是:为什么在不丢失一些数据的情况下无法发送二进制文件,我该如何处理来解决这个问题?谢谢;)
  • @user1767701:对我的答案进行了更新,代码存在另一个潜在问题。
  • @Skizz 这就是我们拥有SendTimeout 属性的原因
【解决方案4】:

问题很可能是您在所有数据传输到服务器之前关闭了客户端套接字,因此它被丢弃了。

默认情况下,当您关闭套接字时,所有未传输的数据(位于操作系统缓冲区中)都会被丢弃。有几个解决方案:

[1] 设置 SO_LINGER(参见http://developerweb.net/viewtopic.php?id=2982) [2] 让服务器向客户端发送确认,并且在收到之前不要关闭客户端套接字。 [3] 等到客户端的输出缓冲区为空后再关闭套接字(使用 getsocketopt SO_SND_BUF 进行测试 - 我不确定 c# 上的语法)。

另外,您确实应该测试 Send() 的返回值。虽然理论上它应该阻塞,直到它发送所有数据,但我想实际验证这一点,如果存在不匹配,至少打印一条错误消息。

【讨论】:

    猜你喜欢
    • 2020-12-03
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    • 2017-03-14
    • 1970-01-01
    • 2016-02-17
    • 2020-02-17
    • 2014-07-25
    相关资源
    最近更新 更多