Help us understand the problem. What is going on with this article?

C#でサンプルなWEBサーバー:IEは曲者!?

More than 3 years have passed since last update.

ajax対応のサンプルWEBサーバーの記事を開発ブログにまとめました。
(2013/03/18 追記)


作成中のアプリにWEBサーバー機能を追加しようとしてハマったのでシェア。

手始めにテストプログラムを作ってみました。
それが以下のソースです。

※不自然な点に気付くかもしれませんが、説明に都合の良いように整理したので。

Program.cs
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace SampleWebServer
{
    class Program
    {
        // メイン
        static void Main(string[] args)
        {
            // サーバーソケット初期化
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEndPoint = new IPEndPoint(ip, 8080);

            server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
            server.Bind(ipEndPoint);
            server.Listen(5);

            // 要求待ち(無限ループ)
            while (true)
            {
                Socket client = server.Accept();

                Response response = new Response(client);
                response.Start();
            }
        }
    }

    // 応答クラス
    class Response
    {
        private Socket  mClient;

        // コンストラクタ
        public Response(Socket client)
        {
            mClient = client;
        }

        // 応答開始
        public void Start()
        {
            Run();
        }

        // 応答実行
        public void Run()
        {
            try
            {
                // 要求受信
                byte[] buffer = new byte[4096];
                int recvLen = mClient.Receive(buffer);

                if (recvLen <= 0)
                    return;

                String message = Encoding.ASCII.GetString(buffer, 0, recvLen);
                Console.Write(message);

                // 要求URL確認 & 応答内容生成
                int pos = message.IndexOf("GET / HTTP/");

                if (0 <= pos)
                {
                    message =
                        "<title>SAMPLE WEB SERVER</title>" +
                        "<h1>サンプルWEBサーバー</h1>" +
                        System.DateTime.Now;
                }
                else
                {
                    message =
                        "<title>404 Not Found</title>" +
                        "<h1>Not Found</h1>";
                }

                buffer = Encoding.UTF8.GetBytes(message);
                int contentLen = -1;

                // HTTPヘッダー生成
                String httpHeader = String.Format(
                    "HTTP/1.1 200 OK\n" +
                    "Content-type: text/html; charset=UTF-8\n" +
                    "Content-length: {0}\n" +
                    "\n",
                    contentLen);

                byte[] httpHeaderBuffer = new byte[4096];
                httpHeaderBuffer = Encoding.UTF8.GetBytes(httpHeader);

                // 応答内容送信
                mClient.Send(httpHeaderBuffer);
                mClient.Send(buffer);
            }
            catch (System.Net.Sockets.SocketException e)
            {
                Console.Write(e.Message);
            }
            finally
            {
                mClient.Close();
            }
        }
    }
}

IEで応答なし…


試しにChromeから127.0.0.1:8080にアクセス。
思い通りに表示してくれました。

次にIEでもアクセス。
…いつまで待っても反応がありません(汗)。

調べてみるとReceive()で止まっていました。
Firefoxでも確認してみましたがこちらは問題なし。なぜだ…

mongooseに救われる


Accept()からのReceive()というごく単純なこの流れ。単純すぎてどこが悪いのか分からない…やっぱIEはあかん子やったんや…などと思ったり。

でも、HttpListenerで実装してみるとこれがうまくいってしまうのでした。
他に何か参考になるソースが欲しい…

ありました、その名も軽量WEBサーバーmongoose!
https://code.google.com/p/mongoose/

目ぼしいところにログを入れてデバッグすると原因がわかりました。
それは何か?

ブラウザからは通常のリクエスト(Req.1とする)の他にfaviconのリクエスト(Req.2とする)も流れてきていたのですが、その送られ方が違っていたのです。

IE以外:
 Req.1 接続 → 送信 → サーバーからの応答を受信
 Req.2 接続 → 送信 → サーバーからの応答を受信

IE:
 Req.1 接続 → Req.2 接続 →
 Req.1 送信 → サーバーからの応答を受信
 Req.2 送信 → サーバーからの応答を受信

つまり上記のソースでは
Accept → Receive → Send →
Accept → …

となっているため、Req.1のAcceptに対してReceiveをしても、IEにしてみればまだReq.2が接続されていないためReq.1を送信していない状態だったのです。

そこで次のように修正しました。

スレッドにしたよ!


Response.Start()修正
        public void Start()
        {
            Thread thread = new Thread(Run);
            thread.Start();
        }

これでうまくいくはず。
…でした。

表示されない…


Receiveし、Sendもできるようになりました。それなのになぜだろう?
何気なく"Content-length"を50とかにしてみる。

サンプルWE

なんか出ましたけど!
そうかー、これかー、というわけでこれを修正。

contentLen修正
                int contentLen = buffer.GetLength(0);

一時はどうなることかと思いましたがやっと表示することができました。
この瞬間がたまりませんねー。

結果

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした