日期:2014-05-18  浏览次数:20991 次

c# socket 传输文件
大家好,我是一名大四的学生。最近在做毕设,遇到了在c#中socket传输大文件的问题。
我的大体思路有两个(参考网上的资料)
----------------------
(1) 个人认为效率比较低  
  连接..  
  发送第一个字节块...  
  等待确认...(接收确认信息)  
  确认  
  发送第二个字节块...  
  等待确认...  
  确认  
  发送第三个.....  
  .... 
(2)个人认为效率比较高
 
发送端一直写入网络流
接收端一直读取网络流
----------------------
我在每个字节块的前面加入了一个int型的变量,它表示每次发送文件数据的长度
我根据思路(2)写了代码
如下
----------------------
接收端

----------------------
发送端

----------------------
我发现有时能够成功,有时却陷入死循环,比如我传输2k的txt,结果很长时间都没有停止,我中断后发现生成了200M的txt
实在搞不懂是为什么,请大家帮我分析分析.


-------------------------------
关于思路(1)

我不会控制socket和相对应的networkstream
比如说发送端发送数据后,要等待接收端的确认信息
怎样让发送端停止发送数据来等待确认信息
接收到后又怎样重新发送数据
说简单一点就是发送端和接收端有序的操作网络流的问题
请大家多多指点
-----------------------------------
最后的问题:您觉得在socket上传输大文件时,如何能提高传输的效率?
networkstream的同步异步操作是怎么回事?能不能跟我说说他们的区别,最好来一个实例(beginread中的AsyncCallback asyncCallback到底是什么东西)

问了好多问题,可是我没有多少分给大家觉得很惭愧(等我有份了一定补给大家),但是我会衷心的谢谢大家
我的mail:6233843@gmail.com 如果有code的话您可以mail我
再一次谢谢大家

------解决方案--------------------
你为什么不把这两种方案结合在一起呢?
首先把文件的总长度和每次发送的大小先发送出去,等接收端接受并分析,然后开始。
比如每次发送4K(这是操作系统文件管理中使用到的最小文件大小,你可以看看你系统中的任何一个文件,占用空间都是4K的整数倍),
最后一次可能会少与4K,但是接受方是可以计算出来的。
必要时,你可以使用多线程,分段发送,接收端收集后分段组合,这还要多使用一个段号码。
socket是最底层的类,传输效率最高!
对于你说的异步操作,一句话说不清楚,基本上可以用“非阻塞模型”来概括,就是调用后立马返回,不是等到操作完成后才返回!
打个比方:阻塞模型
while(isok)
{
readdata(data);//从文件读数据
send(data); //一直等到data发送完毕后才返回,其实这期间本来可以进行下一次读操作
//影响了效率。
if(读完)
isok=false;
else
isok=true;
}
非阻塞模型,可以在发送过程中进行读取操作,提高了效率。
当然,在第二次发送前,必须等待第一次发送操作完成才行,需要检测和控制!
------解决方案--------------------
while (sendCount < fs.Length && _ns.CanWrite) 

int count = fs.Read(_sendBuf, 0, _sendBuf.Length); //读出要发送的数据 
countbuffer = BitConverter.GetBytes(count); 
countbuffer.CopyTo(clientbuffer,0); 
_sendBuf.CopyTo(clientbuffer, 4);
this._ns.Write(clientbuffer, 0, 4 + count); //写入网络流 
sendCount += count; 


有点乱:你每次读取1000还是1004??不是前四个字节是长度吗?为什么从文件里读取1004个字节啊?
------解决方案--------------------
BeginReceiveFrom 方法启动从远程主机异步读取无连接数据报的操作。调用 BeginReceiveFrom 方法将使您能够在单独的执行线程中接收数据。

您可以创建一个实现 AsyncCallback 委托的回调方法并将它的名称传递给 BeginReceiveFrom 方法。为此,您的 state 参数至少必须包含用于通信的已连接或默认 Socket。如果您的回调需要更多信息,则可以创建一个小型类来保存 Socket 和其他必需的信息。通过 state 参数将此类的一个实例传递给 BeginReceiveFrom 方法。

回调方法应调用 EndReceiveFrom 方法。当应用程序调用 BeginReceiveFrom 时,系统将会使用单独的线程来执行指定的回调方法,并将在 EndReceiveFrom 上一直阻止到 Socket 读取数据或引发异常为止。如果想要在调用 BeginReceiveFrom 方法后使原始线程阻止,请使用 WaitHandle.WaitOne。当需要原始线程继续执行时,请在回调方法中调用 T:System.Threading.ManualResetEvent 的 Set 方法。有关如何编写 callback 方法的其他信息,请参见 Callback 示例。

注意 
在调用 BeginReceiveFrom 之前,必须使用 Bind 方法显式地将 Socket 绑定到本地终结点,否则 BeginReceiveFrom 将会引发 SocketException。
 

该方法将数据读入 buffer 参数中,并捕获从其发送数据的远程主机终结点。有关如何检索此终结点的信息,请参考 EndReceiveFrom。如果打算从未知主机或多个主机异步接收无连接的数据报,则最适合使用此方法。在这些情况下,BeginReceiveFrom 将会读取本地网络缓冲区接收到的第一个排队数据报。如果您接收到的数据报大于 buffer 的大小,则 BeginReceiveFrom 方法将在 buffer 中尽可能多地填充消息内容,并引发 SocketException。如果您使用的是不可靠协议,多余的数据将会丢失。而如果当前使用的是可靠协议,则服务提供程序将保留多余的数据,而且通过使用一个足够大的缓冲区调用 BeginReceiveFrom 方法来检索这些数据。

虽然 BeginReceiveFrom 是用于无连接协议的,但您同样可以使用面向连接的协议。如果选择这样做,则必须通过调用 Connect / BeginConnect 方法来建立远程主机连接,或者调用 Accept 或 BeginAccept 方法来接受传入的连接请求。如果在建立连接或接受连接之前就调用了 BeginReceiveFrom 方法,则您将得到 SocketException。您也可以在调用 BeginReceiveFrom 方法之前,为无连接协议建立默认远程主机。在上述任何一种