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

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

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        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("最大IO线程:" + completionPortThreads);
            ThreadPool.QueueUserWorkItem(execute, "线程1");
            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 ConsoleApplication1
{
    public class FileServer
    {
        private string root;
        private Thread listenerThread;


        private void worker(object state)
        {
            TcpClient client = state as TcpClient;
            try
            {
                client.ReceiveTimeout = 2000;
                NetworkStream stream = client.GetStream();
                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 ex)
            {
                Console.WriteLine(ex.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();
                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,因为,文件名要为英文名(不能有空格等特殊字符)。

posted @ 2013-06-05 07:51  云中雀  阅读(323)  评论(0编辑  收藏  举报