C#線程系列講座(3):線程池和文件下載服務器


如果設計一個服務器程序,每當處理用戶請求時,都開始一個線程,將會在一定程序上消耗服務器的資源。爲此,一個最好的解決方法就是在服務器啓動之前,事先創建一些線程對象,然後,當處理客戶端請求時,就從這些建好的線程中獲得線程對象,並處理請求。保存這些線程對象的結構就叫做線程池。

    在C#中可以通過System.Threading.ThreadPool類來實現,在默認情況下,ThreadPool最大可建立500個工作線程和1000個I/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();
}
複製代碼

 

    下圖爲上面代碼的運行結果。



    要注意的是,使用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

下圖爲下載對話框:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章