zoukankan      html  css  js  c++  java
  • C#线程系列讲座(3):线程池和文件下载服务器

    本文为原创,如需转载,请注明作者和出处,谢谢!

    上一篇C#线程系列讲座(2):Thread类的应用

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

        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();
    }

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



        要注意的是,使用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,因为,文件名要为英文名(不能有空格等特殊字符)。

    下一篇:C#线程系列讲座(4):同步与死锁

  • 相关阅读:
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行 已解决!
    iis 无法在Web服务器上启动调试。打开的URL的IIS辅助进程当前没有运行
    aspx页面,Page_Load 无人进入,解决
    Ajax后台传数组参数,接收不到报错!
    FusionCharts和highcharts 饼图区别!
    redis
    Hibernate不同数据库的连接及SQL方言
    Kafka
    Zookeeper
    BaseDao+万能方法 , HibernateDaoSupport
  • 原文地址:https://www.cnblogs.com/nokiaguy/p/1246299.html
Copyright © 2011-2022 走看看