2012-10-23 148 views
3

这是我在这个论坛上发布的第一个问题,我是c#世界的初学者,所以这对我来说很令人兴奋,但是我面临着一些与发送有关的问题通过套接字大量的数据,所以这是我的问题的更多细节:通过TCP套接字发送大量数据

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

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

在论坛上,他们说要将数据分成块,这是一个解决方案,如果是的话,我该如何做到这一点,我试过了,但没有奏效!

+1

看看这个答案在这里,应该帮助你:http://stackoverflow.com/a/5934816/559144 :) –

回答

0

可能您还没有阅读关于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,...); 
+1

这只适用于'发送(缓冲区,标志)'过载。 '发送(缓冲区)'不会调用这个限制。 – Skizz

+0

确实,发送(byte []缓冲区,int偏移量,int大小,SocketFlags socketFlags) –

+0

非常感谢您的快速响应,我没有想到会尽快回答:)我已经尝试了几乎方法,但我仍然有同样的问题一些数据仍然丢失,我认为这将与我的文件类型有关系:它是一个二进制文件,所以我试着发送file.dat,它工作正常。我的问题是:为什么一个二进制文件不能发送而不丢失一些数据,我如何处理来解决这个问题?谢谢;) – user1767701

2

有很多不同的解决方案,但分块的通常是一个很好的解决方案,你可以做到这一点盲目的,你保持填补临时缓冲区,然后把它变成某些状态缓冲,直到你遇到一些任意令牌或缓冲区未完全满,或者您可以遵循每个tcp 消息的某种合约(消息是要接收的总体数据)。

如果你想看看做某种合约,然后做一些事情,比如一个消息的前N个字节是描述符,你可以根据需要制作大小,但临时缓冲区只能读取这个尺寸在流的前面。

一个典型的头可能是这样的:

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

所以,你会读到,在那么如果它足够小,分配全邮件大小的临时缓冲区,并在阅读这一切,或者,如果你认为它将它分成几部分并继续读取,直到您收到的总字节数与您的标题结构的MessageSize部分相匹配。

+2

您的一般解释是正确的,但是我必须指出,您表明上述结构是“5个字节”。如果您将[StructLayoutAttribute](http://msdn.microsoft.com/zh-cn/library/system.runtime.interopservices.structlayoutattribute.aspx)应用于[LayoutKind.Sequential](http:// msdn.microsoft.com/en-us/library/system.runtime.interopservices.layoutkind.aspx)参数与[Pack](http://msdn.microsoft.com/zh-cn/library/system.runtime)相结合。 interopservices.structlayoutattribute.pack.aspx)属性设置为1. –

+0

是的,我在前面加上了“类似的东西”,因为我正在打字,因为我正在打字,尽管你提高了点。 – Grofit

0

我怀疑你的问题之一是你不打电话EndConnect。从MSDN文档:

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

另外,等待: -

布尔successNewSocket = _NewSocket.AsyncWaitHandle.WaitOne(2000,TRUE);

可能总是错误的,因为没有设置事件到信号状态。通常,您可以为BeginConnect函数指定回调函数,并在回调函数中调用EndConnect并设置要发送的事件的状态。请参阅示例代码on this MSDN page

UPDATE

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

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

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

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

+0

或者只是使用同步[连接](http://msdn.microsoft.com/zh-cn/library/d7ew360f.aspx)方法,因为这是他正在做的事情... –

+0

@FrancoisNel:尽管底层提供程序可能在某个时间点超时,但同步版本没有可以指定的超时。 – Skizz

+0

非常感谢您的快速反应,我没有想到会尽快回答:)我已经尝试了几乎所有的方法,但我仍然有同样的问题一些数据仍然丢失,我认为它将与我的文件类型有关系:它是一个二进制文件,所以我尝试发送file.dat,它工作正常。我的问题是: 为什么一个二进制文件不能发送而不丢失一些数据,我该如何处理来解决这个问题? 谢谢;) – user1767701

-1

只是一个说明:我不知道莫是什么;但如果它超过64 KB,那么你做错了。 TCP协议本身对数据包大小有限制。最大值是64 KB,但你无法达到!这是因为不同层的数据包的最大大小不同。 “The MTU (Maximum Transmission Unit) for Ethernet, for instance, is 1500 bytes.

+1

当然你可以实现它,你可以用一次send()调用写入2GB,TCP堆栈负责分段和打包数据为你,没有你需要关心的东西-1 – EJP

+0

TCP是数据流协议,但并不意味着你的网络实际上可以处理你没有问题的信息。“由于网络拥塞,流量负载平衡或其他原因不可预知的网络行为,IP数据包可能会丢失,重复或按乱序发送“;该部分主要是关于”Internet层“上的IP与”传输层“上的协议TCP的比较,如果你使用2 GB那么你将有IP碎片和数据丢失;所以实际上,你将永远不会得到你的2 GB数据没有下载管理器(即)流媒体数据协议(@Application Layer :) –

0

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

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

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

另外你真的应该测试Send()的返回值。虽然理论上它应该阻塞,直到它发送所有数据,但我想实际验证并且至少在出现不匹配时打印错误消息。