socket 文件传输优化问题 高手进来帮忙啊!周公,SP1234等等各位高手帮忙看看!(500分)

cqsfd 2009-11-12 09:14:21
原帖链接http://topic.csdn.net/u/20091112/11/54935b2f-b2b8-4d58-af30-40379860e581.html
回帖中还有几个小问题,一并帮着看看吧


本人新手,只有几个月的实习经验,也不知道问的问题是不是比较白痴...跪求各位高手指点。两个问题200分,不需要修改我的程序,给我点明程序思想即可,另开一贴给100分

下面是我写的一个socket文件传输程序片段,总体思路是:
1:一个线程循环接收服务器发出的指令
2:当接收到传输文件的指令时,就把需传输的文件信息插入队列
3:同时生成一个新的传输文件类,该类work()方法启动一个新线程,从队列中提取文件信息进行相应文件的传输

具体代码如下
else if (sCommand == "GET_FILE")
{
.....

queue.Enqueue(Uuid + '|' + sPath); //queue队列是自己定义的线程同步的队列,同步问题不需要考虑
Send_filesClass cc = new Send_filesClass();
cc.work();

}

public class Send_filesClass
{
private TcpClient tcpc;
public Send_filesClass()
{
this.tcpc = new TcpClient(xxx, 9001);
}
public void work()
{
Thread t = new Thread(new ThreadStart(run));
t.IsBackground = true;
t.Start();
}
private void run()
{
NetworkStream nstream = tcpc.GetStream();

... //while循环:fFileStream文件流读文件,每次102字节,再用nstream发送出去

fFileStream.Close();
Thread.Sleep(500);
nstream.Close();
tcpc.Close();

}
}

现在的问题是:

1:每次同时传输2个文件,我打开本机cmd 输入netstat -a 查看9001端口,2个文件有时会有8个在打开的端口,其中的大部分状态是Time_Wait,为什么呢?是不是我程序中间的设计有问题?有人和我说过,Send_filesClass这个类中,需要用到的socket在构造时传入就好,用一个nstream获取其中的流;但是这个socket不是任何时候都需要,不是长连接的,用完就关掉,不好判断它什么时候被实例化好,所以我把socket放到类中。是不是这里有问题?还有就是这个类传输完文件后,何时被系统回收?我在程序中写的是每次传输完,线程sleep半秒后就关闭流,可是查看本机端口信息,基本都得等个10-20秒端口才释放出来,那这个类岂不是在内存中留得更久?如何在程序中手动释放资源?

2:传输效率非常低。
我用自己机器测试,把文件发送到服务器的同时,将其下载到本机。
刚开始下载时,和网上其他下载资源差不多,都是一上来速度达到疯狂的几百K,1-2秒后,基本都维持在20k左右,这是为什么?如何提高?
我有个想法,对Send_filesClass类中传输文件的方法进行修改:将文件流一段段的先读出来,插入一个队列,再从队列中取流发送出去;如果只用一个线程取流发送,是不是可以省去文件流读取的时间?相当与先将文件流读入内存再发送?
如果我将每段文件流编号,用N多的线程从队列取流出来发送,服务器端接收到再组装起来,传输效率能提高很多吗?


问题比较琐碎,希望高手们踊跃发言,很多和我一样的新手,都将感恩戴德
...全文
506 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
周公 2009-12-03
  • 打赏
  • 举报
回复
同意增加文件块的长度,就像copy1024个1k的文件和copy1个1M的文件明显是前者占用的时间长。
用多线程传输和接收处理好了可以增加速度,但是如果处理不好,或许速度不升反降。
qldsrx 2009-11-13
  • 打赏
  • 举报
回复
仔细看了下,貌似你好像把我的一次性分配内存的意思搞错了。我没有让你把文件一次性读入内存后发送,我是说当你要分块传输文件时(假设每2M数据一个文件块,这已经是很大的文件块了),那2M读入内存后,然后发送出去,接着继续利用那2M的内存区域读取下面的2M文件块数据,这就是预先分配内存重复利用。如果不这么做,默认情况下程序会重新向系统申请2M新的内存空间来读取后面的文件块,已经使用过的废弃,等待GC来垃圾回收。
qldsrx 2009-11-13
  • 打赏
  • 举报
回复
TcpClient的Client属性就是一个Socket对象,所以就算你用TcpClient,也可以用Socket来处理数据的。Socket中如果使用了TCP协议,那就和TcpClient传输本质上一样了,只不过使用的方法不同,Socket提供更多高级的控制,而TcpClient获取的NetworkStream流却只能很傻瓜化的用用,只适合简单的场合。

你提到的文件队列是没有必要的,实际传输时可以用多线程传输来弥补文件读取消耗的时间,所以你没有必要在那上面多花心思。

至于2个连接8个端口,我要看到了完整的代码测试才能分析原因,就听你说说是猜不出来的。
cqsfd 2009-11-13
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 qldsrx 的回复:]
TcpClient的Client属性就是一个Socket对象,所以就算你用TcpClient,也可以用Socket来处理数据的。Socket中如果使用了TCP协议,那就和TcpClient传输本质上一样了,只不过使用的方法不同,Socket提供更多高级的控制,而TcpClient获取的NetworkStream流却只能很傻瓜化的用用,只适合简单的场合。

你提到的文件队列是没有必要的,实际传输时可以用多线程传输来弥补文件读取消耗的时间,所以你没有必要在那上面多花心思。

至于2个连接8个端口,我要看到了完整的代码测试才能分析原因,就听你说说是猜不出来的。
[/Quote]
非常感谢青龙白虎兄!!
刚才把NetworkStream.write改成socket.send了,速度提高了1倍多近两倍!各位还在用NetworkStream.write传输数据的可以考虑修改一下!
我提到的队列,是想做出多文件,多线程同时传输,想BT,迅雷那样,当然功能没他们那么强大。现在听大家的意见,貌似这个方法不太好,我研究下内存映射再修改吧。
然后我的代码都贴出来了,省略掉的地方大家也一看就懂,为什么出现8个端口的奇怪现象不懂,难道tcp本身就可以这样?
风骑士之怒 2009-11-13
  • 打赏
  • 举报
回复
up
SuperTyro 2009-11-13
  • 打赏
  • 举报
回复
关于文件传递通过tcp的时候
个人经验,文件不要多个同时传,要以一个文件为整体的一个一个传递,
在传递一个文件的时候,可以通过算法将文件切分开,然后再用相对数量的线程同时发送,
接收方将收到的东西组装好,这样可以大大的提高传输的效率问题。

关于第一个问题,还没遇到过,关注
qldsrx 2009-11-13
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 wangjun8868 的回复:]
首先考虑用UDP传送(类似QQ) 在UDP适应不了的环境中在使用TCP
[/Quote]
除非是自己写服务端和客户端,如果是和别人的程序通讯,肯定只能使用TCP协议传输,UDP不适合。另外UDP在同步方面实现十分困难,一个数据包发过去如果不反馈一个确认信息,丢包现象太严重了,目前网上没有非常可行的方案,腾讯也不可能公布自己实现的技术细节。
编程有钱人了 2009-11-13
  • 打赏
  • 举报
回复
首先考虑用UDP传送(类似QQ) 在UDP适应不了的环境中在使用TCP
Taiyangchen 2009-11-13
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 qldsrx 的回复:]
仔细看了下,貌似你好像把我的一次性分配内存的意思搞错了。我没有让你把文件一次性读入内存后发送,我是说当你要分块传输文件时(假设每2M数据一个文件块,这已经是很大的文件块了),那2M读入内存后,然后发送出去,接着继续利用那2M的内存区域读取下面的2M文件块数据,这就是预先分配内存重复利用。如果不这么做,默认情况下程序会重新向系统申请2M新的内存空间来读取后面的文件块,已经使用过的废弃,等待GC来垃圾?-
[/Quote]

我没有看明白,但是帮你顶!
深夜回帖要加分 !
cqsfd 2009-11-12
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 qldsrx 的回复:]
话说你这个帖子的问题还是没有变啊。
如果你用NetworkStream,就是打算不考虑效率了,因为NetworkStream没有效率可言,它只是为了让网络传输看起来像是个流才存在的,不建议考虑效率的人使用,应该使用Socket。

至于端口占用,客户端每打开一个Socket连接,就会占用本地一个端口,何时占用端口就要看协议类型了,要关闭端口只要调用Socket的Close方法,即可释放所有资源,但是资源的最终回收是靠GC来回收的。

对于文件上传下载,如果能够重用内存资源,做到可以一次性分配内存,不重新获取内存,速度才是最佳的。当我们序列化一个类到字节数组,或者从文件流读取到字节数组,程序就会向操作系统申请内存空间来存放这些内容。如果只申请不释放,那么内存占用就会越来越多,这不是我们想看到的结果。但是如果频繁地释放-申请内存,不但造成内存碎片,还占用了CPU时间,大大影响了效率。XXXAsync就是可以重用内存的高效传输方式,但使用起来难度也是相当高的,如果使用者内存预分配不当,也会造成不好的影响。
[/Quote]

青龙白虎兄又来帮忙顶贴了,非常感谢!
我贴出的这段代码用的是tcp,我在其他地方用的是socket...在我的程序中,这两个是混用的,因为之前我一直不知道二者有什么区别...这就是代码工人的悲哀啊!要不再给我讲讲这二者的区别吧!

我提到的将文件块插入队列,同时开新线程从队列取文件块发送应该比一次性分配内存,再从内存中发送更好,因为我只读入一块的时候就开始发送了,读入其他块是在socket发送等待对方接收确认这段时间完成的。就拿前一个帖子的例子,如果要传输的文件分为10个块,每个块读入内存需要时间a,传输出去需要时间b,如果一次都读入内存再发送,需要时间还是10a+10b,而插入队列貌似可以是a+10b。我理解的对吧?不过最好的方法应该是上个帖子有高人提到的用内存映射的方法,刚才查了下,相当于省了文件读入内存的时间,以后有空得好好研究下!

我很奇怪的是,为什么我程序里,要发送2个文件时,明明是产生2个类,启用2个链接,在我的印象里,应该在本机只有2个端口与服务器链接啊,为什么我打开端口查看的时候,发现有8个端口链接呢?是我这边程序问题还是服务器那边设定的问题?
而且我想做到手动释放资源,等GC来释放太慢了,以后也想在其他方面也尽量手动释放资源,这点希望周公前辈来解答。
qldsrx 2009-11-12
  • 打赏
  • 举报
回复
话说你这个帖子的问题还是没有变啊。
如果你用NetworkStream,就是打算不考虑效率了,因为NetworkStream没有效率可言,它只是为了让网络传输看起来像是个流才存在的,不建议考虑效率的人使用,应该使用Socket。

至于端口占用,客户端每打开一个Socket连接,就会占用本地一个端口,何时占用端口就要看协议类型了,要关闭端口只要调用Socket的Close方法,即可释放所有资源,但是资源的最终回收是靠GC来回收的。

对于文件上传下载,如果能够重用内存资源,做到可以一次性分配内存,不重新获取内存,速度才是最佳的。当我们序列化一个类到字节数组,或者从文件流读取到字节数组,程序就会向操作系统申请内存空间来存放这些内容。如果只申请不释放,那么内存占用就会越来越多,这不是我们想看到的结果。但是如果频繁地释放-申请内存,不但造成内存碎片,还占用了CPU时间,大大影响了效率。XXXAsync就是可以重用内存的高效传输方式,但使用起来难度也是相当高的,如果使用者内存预分配不当,也会造成不好的影响。
rizher 2009-11-12
  • 打赏
  • 举报
回复
帮顶 !最近也在学习socket
huwei12345 2009-11-12
  • 打赏
  • 举报
回复
帮顶!

110,536

社区成员

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

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

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