C#线程系列讲座(3):线程池和文件下载服务器

如果设计一个服务器程序,每当处理用户请求时,都开始一个线程,将会在一定程序上消耗服务器的资源。为此,一个最好的解决方法就是在服务器启动之前,事先创建一些线程对象,然后,当处理客户端请求时,就从这些建好的线程中获得线程对象,并处理请求。保存这些线程对象的结构就叫做线程池。

    C#中可以通过System.Threading.ThreadPool类来实现,在默认情况下,ThreadPool最大可建立500个工作线程和1000I/O线程(根据机器CPU个数和.net framework版本的不同,这些数据可能会有变化)。下面是一个用C#从线程池获得线程的例子:

private static void execute(object  state)
{
    Console.WriteLine(state);      
}
static void Main(string
[] args)
{
  
    
int
 workerThreads;
    
int
 completionPortThreads;
         
    ThreadPool.GetMaxThreads(
out workerThreads, out
 completionPortThreads);
    Console.WriteLine(workerThreads);
    Console.WriteLine(completionPortThreads);    
    ThreadPool.QueueUserWorkItem(execute,
"线程1");   // 从线程池中得到一个线程,并运行execute

    ThreadPool.QueueUserWorkItem(execute, "线程2" );
    ThreadPool.QueueUserWorkItem(execute, 
"线程3"
);
    Console.ReadLine();
}

    下图为上面代码的运行结果。

C#线程系列讲座(3):线程池和文件下载服务器

    要注意的是,使用ThreadPool获得的线程都是后台线程。

    下面的程序是我设计的一个下载文件服务器的例子。这个例子从ThreadPool获得线程,并处理相应的客户端请求。

using  System;
using
 System.Collections.Generic;
using
 System.Linq;
using
 System.Text;
using
 System.Threading;
using
 System.Net.Sockets;
using
 System.IO;

namespace
 MyThread
{
    
class
 FileServer
    {
        
private
 String root;
        
private
 Thread listenerThread;

        
private void worker(object
 state)
        {
             TcpClient client 
= state as
 TcpClient;
             
try

             {

                 client.ReceiveTimeout 
= 2000 ;
                 Stream stream 
=
 client.GetStream();
                 System.IO.StreamReader sr 
= new
 StreamReader(stream);
                 String line 
=
 sr.ReadLine();
                 String[] array 
= line.Split(' '
);
                 String path 
= array[1].Replace('/''\\'
);
                 String filename 
= root +
 path;
                 
if (File.Exists(filename))  // 如果下载文件存在,开始下载这个文件

                 {
                     FileStream fileStream 
= new
 FileStream(filename, FileMode.Open, FileAccess.Read,
                                                           FileShare.Read);
                     
byte[] buffer = new byte[8192]; // 每次下载8K

                     int count = 0 ;
                     String responseHeader 
= "HTTP/1.1 200 OK\r\n" +

                                             
"Content-Type:application/octet-stream\r\n" +
                                             
"Content-Disposition:attachment;filename="  +
                                                   filename.Substring(filename.LastIndexOf("\\"+ 1+ "\r\n\r\n"
;
                     
byte[] header =
 ASCIIEncoding.ASCII.GetBytes(responseHeader);
                     stream.Write(header, 
0
, header.Length);
                     
while ((count = fileStream.Read(buffer, 0, buffer.Count())) > 0
)
                     {
                         stream.Write(buffer, 
0
, count);
                     }
                     Console.WriteLine(filename 
+ "下载完成"
);
                 }
                 
else  // 文件不存在,输出提示信息

                 {
                     String response 
= "HTTP/1.1 200 OK\r\nContent-Type:text/plain;charset=utf-8\r\n\r\n文件不存在"
;
                     
byte[] buffer =
 ASCIIEncoding.UTF8.GetBytes(response);
                     stream.Write(buffer, 
0
, buffer.Length);
                 }

             }
             
catch
 (Exception e)
             {
                 Console.WriteLine(e.Message);
             }
             
finally

             {
                 
if (client != null )
                 {
                     client.Close();
                 }
             }
        }

        
private void
 listener()
        {
            TcpListener listener 
= new TcpListener(1234
);
            listener.Start();  
// 开始监听客户端请求

            TcpClient client = null ;

            
while (true
)
            {
                client 
=
 listener.AcceptTcpClient();
                client.ReceiveTimeout 
=2000
;
                ThreadPool.QueueUserWorkItem(worker, client);  
// 从线程池中获得一个线程来处理客户端请求

            }
        }
        
public
 FileServer(String root)
        {
            
this.root=
 root;        
        }
        
public void
 start()
        {
            listenerThread 
= new
 Thread(listener);
            listenerThread.Start();  
// 开始运行监听线程

        }
    }
}


    FileServer类的使用方法:

    FileServer fs = new FileServer(“d:\\download”);

fs.start(); // 端口为1234

如果d:"download目录中有一个叫aa.exe的文件,在浏览器中输入如下的地址可下载:
    http://localhost:1234/aa.exe

下图为下载对话框:

C#线程系列讲座(3):线程池和文件下载服务器

要注意的是,本程序并没有处理含有中文和其他特殊字符(如空格)的url,因为,文件名要为英文名(不能有空格等特殊字符)。

你可能感兴趣的:(文件下载)