c# socket 文件 传输 进度

wangzhe1945 2008-12-15 03:44:37
c# 用 socket 传输文件时,如何得到当前传输进度?

filestream=new FileStream(filename,FileMode.Open,FileAccess.Read);

byte[] buffer = new byte[1024];

filestream.Seek(0,SeekOrigin.Begin);
while((number=filestream.Read(buffer,0,1024))!=0)
{
stream.Write(buffer,0,number);
stream.Flush();
Console.WriteLine(filestream.Position);
}
filestream.Close();

如果网络繁忙的情况下,以上代码会不会出现问题,写入太快,而对方却无法接收?
...全文
470 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
wangzhe1945 2008-12-17
  • 打赏
  • 举报
回复
帖子沉了,自己总结下

System.Net.Sockets.NetworkStream
NetworkStream 类提供在阻塞模式下通过 Stream 套接字发送和接收数据的方法。有关阻塞与非阻塞 Socket 的更多信息,请参见使用异步客户端套接字。您可以使用 NetworkStream 类进行同步和异步数据传输。有关异步通信与同步通信的更多信息,请参见套接字。要创建 NetworkStream,必须提供连接的 Socket。也可指定 NetworkStream 对所提供的 Socket 具有哪些 FileAccess 权限。默认情况下,关闭 NetworkStream 并不会关闭所提供的 Socket。如果要使 NetworkStream 拥有关闭所提供的 Socket 的权限,则必须将 ownsSocket 构造函数参数的值指定为 true。

将 Write 和 Read 方法用于简单的单线程同步阻塞 I/O。若要使用不同的线程来处理 I/O,则请考虑使用 BeginWrite/ EndWrite 和 BeginRead/ EndRead 方法进行通信。

NetworkStream 不支持对网络数据流的随机访问。CanSeek 属性用于指示流是否支持查找,它的值始终为 false。读取 Position 属性、读取 Length 属性或者调用 Seek 方法都会引发 NotSupportedException。

公共方法
BeginRead
受 .NET Framework 精简版的支持。
已重写。从 NetworkStream 开始异步读取。
BeginWrite
受 .NET Framework 精简版的支持。
已重写。开始向流异步写入。
Close
受 .NET Framework 精简版的支持。
已重写。关闭 NetworkStream。
CreateObjRef(从 MarshalByRefObject 继承) 创建一个对象,该对象包含生成用于与远程对象进行通讯的代理所需的全部相关信息。
EndRead
受 .NET Framework 精简版的支持。
已重写。处理异步读取的结束。
EndWrite
受 .NET Framework 精简版的支持。
已重写。处理异步写入的结束。
Equals(从 Object 继承)
受 .NET Framework 精简版的支持。
已重载。确定两个 Object 实例是否相等。
Flush
受 .NET Framework 精简版的支持。
已重写。刷新流中的数据。保留此方法供将来使用。
GetHashCode(从 Object 继承)
受 .NET Framework 精简版的支持。
用作特定类型的哈希函数,适合在哈希算法和数据结构(如哈希表)中使用。
GetLifetimeService(从 MarshalByRefObject 继承) 检索控制此实例的生存期策略的当前生存期服务对象。
GetType(从 Object 继承)
受 .NET Framework 精简版的支持。
获取当前实例的 Type。
InitializeLifetimeService(从 MarshalByRefObject 继承) 获取控制此实例的生存期策略的生存期服务对象。
Read
受 .NET Framework 精简版的支持。
已重写。从 NetworkStream 读取数据。
ReadByte(从 Stream 继承)
受 .NET Framework 精简版的支持。
从流中读取一个字节,并将流内的位置向前推进一个字节,或者如果已到达流的末尾,则返回 -1。
Seek
受 .NET Framework 精简版的支持。
已重写。将流的当前位置设置为给定值。此方法始终引发 NotSupportedException。
SetLength
受 .NET Framework 精简版的支持。
已重写。设置流的长度。此方法始终引发 NotSupportedException。
ToString(从 Object 继承)
受 .NET Framework 精简版的支持。
返回表示当前 Object 的 String。
Write
受 .NET Framework 精简版的支持。
已重写。将数据写入 NetworkStream。
WriteByte(从 Stream 继承)
受 .NET Framework 精简版的支持。
将一个字节写入流内的当前位置,并将流内的位置向前推进一个字节。

wangzhe1945 2008-12-16
  • 打赏
  • 举报
回复
还有一个问题,如何知道当前传输的进度呢?
hangang7403 2008-12-15
  • 打赏
  • 举报
回复
up
claymore1114 2008-12-15
  • 打赏
  • 举报
回复
UP
liningln0 2008-12-15
  • 打赏
  • 举报
回复
阻塞是在网络拥挤的时候必然现象,如果要从根本上解决的话很难,如果你现在缓解的话,可以采取多线程和stream.Flush()都有一定的用处.
xu_2007 2008-12-15
  • 打赏
  • 举报
回复
顶一下!
zcandyly20211 2008-12-15
  • 打赏
  • 举报
回复
友情up~
wangzhe1945 2008-12-15
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 yhy0611 的回复:]
如果网络繁忙的情况下,以上代码会不会出现问题,写入太快,而对方却无法接收?
=============================================================


绝对会的,我现在正在经历这个问题,像1楼说用缓冲区可以解决,但我的问题太严重了,一个2K的包,可能要延迟十分钟,已经超出了程序控制的范围,正在建议用户把ADSL换成光纤
[/Quote]

是用UDP协议传输的吗?
sikezx 2008-12-15
  • 打赏
  • 举报
回复
while(uiTotal < uiLength)

{

int iEnd=pDlg->m_bSendEnd;

//传送发送端状态(是否结束)

iNumByte = sockSend.Send(&iEnd, sizeof(int));

//发送错误

if(iNumByte == SOCKET_ERROR)

{

AfxMessageBox("发送错误!");

goto ExitLable1;

}else if(iEnd==1)//发送端终止

{

AfxMessageBox("发送端终止");

goto ExitLable1;

}

//读取文件内容

if((int)(uiLength - uiTotal) < iBufSize)

iSize = uiLength - uiTotal;//当小于缓冲区iTEST时的处理

iSize=file.Read(pBuf , iSize);//得到读取的字节数

int iCount=0;

//发送定长文件数据

while(iCount<iSize)

{

iNumByte = sockSend.Send(pBuf, iSize-iCount);//注意iNumByte为实际的发送字节数,不要以iSize为准

if(iNumByte == SOCKET_ERROR)

{

AfxMessageBox("发送错误!");

goto ExitLable1;

}

iCount+=iNumByte;

if(iCount<iSize)

{

file.Seek(iSize-iCount,CFile::current);

}

}

uiTotal += iCount;

//设置发送数据进度条

pDlg->m_CtrlProgressSend.SetPos(int(((double)uiTotal/uiLength)*100));

str.Format("发送进度:%d%%",int(((double)uiTotal/uiLength)*100));

//表明发送数据百分比

pDlg->GetDlgItem(IDC_STATIC_SEND)->GetWindowText(str1);

if(str1!=str)

pDlg->GetDlgItem(IDC_STATIC_SEND)->SetWindowText(str);

}

//发送文件成功

AfxMessageBox("发送文件成功!");

ExitLable1:

delete[] pBuf;

file.Close();

sockSend.Close();

pDlg->m_CtrlProgressSend.SetPos(0);//恢复进度

pDlg->GetDlgItem(IDC_BUTTON_SEND_END)->EnableWindow(FALSE);//设置发送结束按钮禁止

pDlg->GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(TRUE);//设置发送按钮正常

pDlg->GetDlgItem(IDC_STATIC_SEND)->SetWindowText("发送进度:"); //恢复提示进度

return 0;

}

UINT ReceiveDataThread(LPVOID lpParam)

{

CTzg004Dlg *pDlg=(CTzg004Dlg *)lpParam;

//保存文件对话框

CFileDialog dlg(FALSE,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,

"所有文件 (*.*)|*.*||");

while(dlg.DoModal()!=IDOK)

{

AfxMessageBox("选择文件出错,请重新选择!");

}

CString str,str1,str2;

CSocket sockRecv;

sockRecv.Create();

pDlg->m_CtrlIPSend.GetWindowText(str);//得到发送端IP地址

pDlg->GetDlgItem(IDC_BUTTON_RECEIVE)->EnableWindow(FALSE);//禁止接收按钮

while(sockRecv.Connect(str,pDlg->m_iDataPort2)==0)//连接发送方地址,若上网,可改为实际IP地址,端口要跟Server端相同。

{

Sleep(50);

}

pDlg->GetDlgItem(IDC_BUTTON_REC_END)->EnableWindow(TRUE);//打开终止接收按钮

str2=dlg.GetPathName();//得到文件名

CFile file;

file.Open(str2, CFile::modeCreate | CFile::modeWrite);

BOOL bFileFail=FALSE;

DWORD dwTemp = 0;

sockRecv.AsyncSelect(0);

sockRecv.IOCtl( FIONBIO, &dwTemp);//变为阻塞方式



UINT uiLength;

sockRecv.Receive(&uiLength, 4);//接收发方(Server端)的文件大小

int iBufSize = 1024 * 5;

int iSize = iBufSize;

LPBYTE pBuf = new BYTE[iBufSize];

int iNumByte;

UINT uiTotal = 0;

while(uiTotal < uiLength)

{

int iEnd=0;

//接收端终止

if(pDlg->m_bRecEnd)

{

AfxMessageBox("接收端终止!");

goto ExitLable2;

}

//接收发送端状态数据

iNumByte=sockRecv.Receive(&iEnd, sizeof(int));

if(iNumByte == SOCKET_ERROR)

{

AfxMessageBox("接收信号错误!");

goto ExitLable2;

}

//发送端终止

if(iEnd==1)

{

AfxMessageBox("发送端终止!");

goto ExitLable2;

}



if((int)(uiLength - uiTotal) < iBufSize)

iSize = uiLength - uiTotal;

int iCount=0;

//读取定长数据

while(iCount<iSize)

{

iNumByte = sockRecv.Receive(pBuf, iSize-iCount);

if(iNumByte == SOCKET_ERROR)

{

AfxMessageBox("接收错误!");

goto ExitLable2;

}

iCount+=iNumByte;

file.Write(pBuf, iNumByte);

}

uiTotal += iCount;//以实际接收字节为准

//设置接收进度

pDlg->m_CtrlProgressRec.SetPos(int(((double)uiTotal/uiLength)*100));

str.Format("接收进度:%d%%",int(((double)uiTotal/uiLength)*100));

//显示接收进度百分比

pDlg->GetDlgItem(IDC_STATIC_REC)->GetWindowText(str1);

if(str1!=str)

pDlg->GetDlgItem(IDC_STATIC_REC)->SetWindowText(str);

}

//接收文件成功

AfxMessageBox("接收文件成功!");

bFileFail=TRUE;

ExitLable2:

delete[] pBuf;

file.Close();

//文件接收失败,则删除接收文件

if(!bFileFail)

{

CFile::Remove( str2 );

}

sockRecv.Close();

pDlg->m_CtrlProgressRec.SetPos(0);//恢复接收进度

//禁止终止接收按钮

pDlg->GetDlgItem(IDC_BUTTON_REC_END)->EnableWindow(FALSE);

//打开接收按钮

pDlg->GetDlgItem(IDC_BUTTON_RECEIVE)->EnableWindow(TRUE);

//恢复提示进度

pDlg->GetDlgItem(IDC_STATIC_REC)->SetWindowText("接收进度:");

return 0;

}

看看吧!应该有帮助
sikezx 2008-12-15
  • 打赏
  • 举报
回复
//1:显示文件进度

//2:可以随时终止传输过程

//发送数据线程

UINT SendDataThread(LPVOID lpParam);

//接收数据线程

UINT ReceiveDataThread(LPVOID lpParam);

//发送数据按钮消息响应函数

void CTzg004Dlg::OnButtonSend()

{

// TODO: Add your control notification handler code here

//初始化数据发送结束标志

m_bSendEnd=FALSE;

//初始化数据接收结束标志

m_bRecEnd=FALSE;

//更新对话框数据

UpdateData(TRUE);

//打开文件对话框

CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,

"所有文件 (*.*)|*.*||");

if(dlg.DoModal()==IDOK)

{

m_strFileName=dlg.GetPathName();

//开始发送数据线程

AfxBeginThread(SendDataThread,this,THREAD_PRIORITY_NORMAL);

}

}

//接收数据按钮消息响应函数

void CTzg004Dlg::OnButtonReceive()

{

// TODO: Add your control notification handler code here

//初始化数据发送结束标志

m_bSendEnd=FALSE;

//初始化数据接收结束标志

m_bRecEnd=FALSE;

UpdateData(TRUE);

//开始接收数据线程

AfxBeginThread(ReceiveDataThread,this,THREAD_PRIORITY_NORMAL);

}

//终止发送按钮消息响应

void CTzg004Dlg::OnButtonSendEnd()

{

// TODO: Add your control notification handler code here

//设置发送数据结束标志

m_bSendEnd=TRUE;

}

//终止接收按钮消息响应

void CTzg004Dlg::OnButtonRecEnd()

{

// TODO: Add your control notification handler code here

//设置接收数据结束标志

m_bRecEnd=TRUE;

}

UINT SendDataThread(LPVOID lpParam)

{

CTzg004Dlg *pDlg=(CTzg004Dlg *)lpParam;

CFile file;

if( !file.Open(pDlg->m_strFileName, CFile::modeRead) )

{

AfxMessageBox("打开文件出错!");

return 0;

}

CSocket sockTemp;

CString str,str1;

sockTemp.Create(pDlg->m_iDataPort1); //得到端口号

sockTemp.Listen(1);//只接受一个连接

CSocket sockSend;

//设置发送按钮禁止

pDlg->GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(FALSE);

sockTemp.Accept(sockSend);//注意,sockTemp已交了自己的指针地址到sockSend,故不用Close

//打开发送终止按钮

pDlg->GetDlgItem(IDC_BUTTON_SEND_END)->EnableWindow(TRUE);





int iBufSize = 1024 * 5;

int iSize = iBufSize;

LPBYTE pBuf = new BYTE[iBufSize];



DWORD dwTemp = 0;

BOOL bTest = sockSend.AsyncSelect(0);//由于CSocket实际是异步,将它变为同步(阻塞)方式。

sockSend.IOCtl( FIONBIO, &dwTemp);//用IOCtl要将AsyncSelect的第一个参数为0,参看MSDN



UINT uiLength = file.GetLength();

sockSend.Send(&uiLength, 4);//传送文件大小到接收方(Client端)



int iNumByte;

UINT uiTotal = 0;

yhy0611 2008-12-15
  • 打赏
  • 举报
回复
如果网络繁忙的情况下,以上代码会不会出现问题,写入太快,而对方却无法接收?
=============================================================


绝对会的,我现在正在经历这个问题,像1楼说用缓冲区可以解决,但我的问题太严重了,一个2K的包,可能要延迟十分钟,已经超出了程序控制的范围,正在建议用户把ADSL换成光纤
wangzhe1945 2008-12-15
  • 打赏
  • 举报
回复

while((number=filestream.Read(buffer,0,1024))!=0)
{
stream.Write(buffer,0,number);
stream.Flush();
Console.WriteLine(filestream.Position);
}

在循环中,如果网络问题,在stream.Write(buffer,0,number);
stream.Flush(); 中 ,写入会不会阻塞?
如果是,那么进度问题就能够控制了。
长沙三毛 2008-12-15
  • 打赏
  • 举报
回复
接收方应该缓冲文件数据:可扩展多线程异步Socket服务器框架EMTASS 2.0

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧