原帖链接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多的线程从队列取流出来发送,服务器端接收到再组装起来,传输效率能提高很多吗? 
问题比较琐碎,希望高手们踊跃发言,很多和我一样的新手,都将感恩戴德

解决方案 »

  1.   

    话说你这个帖子的问题还是没有变啊。
    如果你用NetworkStream,就是打算不考虑效率了,因为NetworkStream没有效率可言,它只是为了让网络传输看起来像是个流才存在的,不建议考虑效率的人使用,应该使用Socket。至于端口占用,客户端每打开一个Socket连接,就会占用本地一个端口,何时占用端口就要看协议类型了,要关闭端口只要调用Socket的Close方法,即可释放所有资源,但是资源的最终回收是靠GC来回收的。对于文件上传下载,如果能够重用内存资源,做到可以一次性分配内存,不重新获取内存,速度才是最佳的。当我们序列化一个类到字节数组,或者从文件流读取到字节数组,程序就会向操作系统申请内存空间来存放这些内容。如果只申请不释放,那么内存占用就会越来越多,这不是我们想看到的结果。但是如果频繁地释放-申请内存,不但造成内存碎片,还占用了CPU时间,大大影响了效率。XXXAsync就是可以重用内存的高效传输方式,但使用起来难度也是相当高的,如果使用者内存预分配不当,也会造成不好的影响。
      

  2.   


    青龙白虎兄又来帮忙顶贴了,非常感谢!
    我贴出的这段代码用的是tcp,我在其他地方用的是socket...在我的程序中,这两个是混用的,因为之前我一直不知道二者有什么区别...这就是代码工人的悲哀啊!要不再给我讲讲这二者的区别吧!我提到的将文件块插入队列,同时开新线程从队列取文件块发送应该比一次性分配内存,再从内存中发送更好,因为我只读入一块的时候就开始发送了,读入其他块是在socket发送等待对方接收确认这段时间完成的。就拿前一个帖子的例子,如果要传输的文件分为10个块,每个块读入内存需要时间a,传输出去需要时间b,如果一次都读入内存再发送,需要时间还是10a+10b,而插入队列貌似可以是a+10b。我理解的对吧?不过最好的方法应该是上个帖子有高人提到的用内存映射的方法,刚才查了下,相当于省了文件读入内存的时间,以后有空得好好研究下!我很奇怪的是,为什么我程序里,要发送2个文件时,明明是产生2个类,启用2个链接,在我的印象里,应该在本机只有2个端口与服务器链接啊,为什么我打开端口查看的时候,发现有8个端口链接呢?是我这边程序问题还是服务器那边设定的问题?
    而且我想做到手动释放资源,等GC来释放太慢了,以后也想在其他方面也尽量手动释放资源,这点希望周公前辈来解答。
      

  3.   

    TcpClient的Client属性就是一个Socket对象,所以就算你用TcpClient,也可以用Socket来处理数据的。Socket中如果使用了TCP协议,那就和TcpClient传输本质上一样了,只不过使用的方法不同,Socket提供更多高级的控制,而TcpClient获取的NetworkStream流却只能很傻瓜化的用用,只适合简单的场合。你提到的文件队列是没有必要的,实际传输时可以用多线程传输来弥补文件读取消耗的时间,所以你没有必要在那上面多花心思。至于2个连接8个端口,我要看到了完整的代码测试才能分析原因,就听你说说是猜不出来的。
      

  4.   

    仔细看了下,貌似你好像把我的一次性分配内存的意思搞错了。我没有让你把文件一次性读入内存后发送,我是说当你要分块传输文件时(假设每2M数据一个文件块,这已经是很大的文件块了),那2M读入内存后,然后发送出去,接着继续利用那2M的内存区域读取下面的2M文件块数据,这就是预先分配内存重复利用。如果不这么做,默认情况下程序会重新向系统申请2M新的内存空间来读取后面的文件块,已经使用过的废弃,等待GC来垃圾回收。
      

  5.   

    首先考虑用UDP传送(类似QQ) 在UDP适应不了的环境中在使用TCP
      

  6.   

    除非是自己写服务端和客户端,如果是和别人的程序通讯,肯定只能使用TCP协议传输,UDP不适合。另外UDP在同步方面实现十分困难,一个数据包发过去如果不反馈一个确认信息,丢包现象太严重了,目前网上没有非常可行的方案,腾讯也不可能公布自己实现的技术细节。
      

  7.   

    关于文件传递通过tcp的时候
    个人经验,文件不要多个同时传,要以一个文件为整体的一个一个传递,
    在传递一个文件的时候,可以通过算法将文件切分开,然后再用相对数量的线程同时发送,
    接收方将收到的东西组装好,这样可以大大的提高传输的效率问题。关于第一个问题,还没遇到过,关注
      

  8.   

    非常感谢青龙白虎兄!!
    刚才把NetworkStream.write改成socket.send了,速度提高了1倍多近两倍!各位还在用NetworkStream.write传输数据的可以考虑修改一下!
    我提到的队列,是想做出多文件,多线程同时传输,想BT,迅雷那样,当然功能没他们那么强大。现在听大家的意见,貌似这个方法不太好,我研究下内存映射再修改吧。
    然后我的代码都贴出来了,省略掉的地方大家也一看就懂,为什么出现8个端口的奇怪现象不懂,难道tcp本身就可以这样?
      

  9.   

    同意增加文件块的长度,就像copy1024个1k的文件和copy1个1M的文件明显是前者占用的时间长。
    用多线程传输和接收处理好了可以增加速度,但是如果处理不好,或许速度不升反降。