読者です 読者をやめる 読者になる 読者になる

clock-up-blog

go-mi-tech

C# による簡易 HTTP サーバの実装

C# AdventCalendar

C# Advent Calendar 2016 1日目の記事です。

HttpListener というクラスを使うと意外と簡単にできる。
要は何らかのポート (80番または別番号) で待ちうけて、
GET /hoge/fuga.html みたいなリクエストが来たらそれに対する応答を返せばいいだけ。

今回のサンプル

以下にソースコード置いてあります。Visual Studio 2015 あたりで起動できるはず。

HELLO を返すだけの HTTP サーバ

※今回は便宜上 1000 番ポートを用いている。

using System;
using System.IO;
using System.Net;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            HttpListener listener = new HttpListener();
            listener.Prefixes.Add("http://localhost:1000/");
            listener.Start();
            while (true)
            {
                HttpListenerContext context = listener.GetContext();
                HttpListenerResponse res = context.Response;
                res.StatusCode = 200;
                byte[] content = Encoding.UTF8.GetBytes("HELLO");
                res.OutputStream.Write(content, 0, content.Length);
                res.Close();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }
    }
}

どんなURLパス指定されても「HELLO」を返し続ける愚直なサーバができあがります。
f:id:kobake:20161130130734p:plain
f:id:kobake:20161130130737p:plain
f:id:kobake:20161130130739p:plain

ちゃんとファイル内容を返す HTTP サーバ

リクエスト内容に応じて wwwroot フォルダの中に格納されているファイル内容を返す HTTP サーバ。

using System;
using System.IO;
using System.Net;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            FileServer();
        }
        catch(Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }
    }
    static void FileServer()
    {
        // ドキュメントルート (プロジェクトディレクトリ内 wwwroot)
        // 例: "C:\Projects\CsharpHttpServerSample\wwwroot"
        string wwwroot = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory + @"\..\..\wwwroot");

        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://localhost:1000/");
        listener.Start();

        while (true)
        {
            HttpListenerContext context = listener.GetContext();
            HttpListenerRequest req = context.Request;
            HttpListenerResponse res = context.Response;

            // URL (ここには "/" とか "/index.html" 等が入ってくる)
            string urlPath = req.RawUrl;
            Console.WriteLine(urlPath);
            if (urlPath == "/") urlPath = "/index.html";

            // 実際のローカルファイルパス
            string path = wwwroot + urlPath.Replace("/", "\\");

            // ファイル内容を出力
            try
            {
                res.StatusCode = 200;
                byte[] content = File.ReadAllBytes(path);
                res.OutputStream.Write(content, 0, content.Length);
            }
            catch (Exception ex)
            {
                res.StatusCode = 500; // 404 でも良いのだがここは雑に 500 にまとめておく
                byte[] content = Encoding.UTF8.GetBytes(ex.Message);
                res.OutputStream.Write(content, 0, content.Length);
            }
            res.Close();
        }
    }
}

ちゃんとコンテンツが表示される HTTP サーバができました。
f:id:kobake:20161130132339p:plain
f:id:kobake:20161130132343p:plain
f:id:kobake:20161130132346p:plain

おしまい

本当は Content-Type の指定等等もしたほうがいいのですが、あくまでも簡易サンプル、ということで。

レスポンスいじりたければ Visual Studio 上で res. って打ち込めばインテリセンスにより目的に叶うキーワードが見つかると思うのでリファレンスなんか見なくてもちょちょいと機能追加できますよ。


ちなみに HTTP サーバはこんな感じですごく楽ですが HTTPS サーバは証明書周りのちょっとめんどくさい手順があるのでまた別の機会にでも書きます。

});