Webアプリケーションを使用したコードについて質問です。
クライアントからHTTPリクエストをPOSTで送信し、
サーバー側の処理で送信したHTTPリクエスト、及びパラメータの値を取り出し、
コンソール出力するプログラムを作成しています。
サーバー側のコードを以下に示します。
C#
1class Program 2{ 3 static void Main(string[] args) 4 { 5 bool flag = false; 6 7 while(true) 8 { 9 // 1回目に関数を呼び出してからは、DoServerのawaitでプログラムを終了しないようにする 10 if (!flag) 11 { 12 DoServer(); 13 flag = true; 14 } 15 } 16 } 17 18 /// <summary> 19 /// サーバーの動作をする。 20 /// </summary> 21 static async private void DoServer() 22 { 23 // TCP接続を待ち受けるためのソケット用意 24 TcpListener tcp = new TcpListener(IPAddress.Loopback, 1234); 25 // クライアントからの接続を待機する。 26 tcp.Start(); 27 28 // 適当に待つ 29 Console.WriteLine("クライアント接続待ち中…"); 30 31 while (true) 32 { 33 using (TcpClient client = await tcp.AcceptTcpClientAsync()) // クライアントからのインスタンスを受け取る 34 using (NetworkStream stream = client.GetStream()) // インスタンスからストリームを取り出す 35 using (StreamReader reader = new StreamReader(stream)) // ストリームから読み取り用ストリーム取得 36 using (StreamWriter writer = new StreamWriter(stream)) // ストリームから書き込み用ストリーム取得 37 { 38 // 接続先のクライアントを出力 39 Console.WriteLine(client.Client.RemoteEndPoint); 40 41 // 表示用の文字 42 string headerStr = string.Empty; 43 // 解釈用に、取得した文字列を全て取得する 44 List<string> readList = new List<string>(); 45 46 do 47 { 48 // 読み込みストリームから、文字を1行読み込む 49 headerStr = await reader.ReadLineAsync(); 50 51 if (headerStr != null) 52 { 53 // 読んだ行を出力 54 Console.WriteLine(headerStr); 55 56 // 解釈用のリストに入れる 57 readList.Add(headerStr); 58 } 59 60 } while (headerStr != null); // ReadLineAsyncは、最後まで行くとnullを返す 61 62 if (readList == null || readList.Count == 0) 63 { 64 // エラーのステータスが返っていることをレスポンスに帰す 65 await writer.WriteLineAsync("HTTP/1.0 400 Bad Request"); 66 await writer.WriteLineAsync("Content-Type: text/plain; charset=UTF-8"); 67 await writer.WriteLineAsync(); 68 await writer.WriteLineAsync("Bad Request"); 69 return; 70 } 71 72 // 1行目を読み取る(GET/POST部。パラメータも含んでいる。) 73 string readLine = readList[0]; 74 75 // 1行目はGET/HTTPを示しているので、半角スペース区切りで取得 76 string[] readLineAddr = readLine.Split(' '); 77 78 // 3つない場合はエラーである 79 if (readLineAddr == null || readLineAddr.Count() != 3) 80 { 81 // エラーのステータスが返っていることをレスポンスに帰す 82 await writer.WriteLineAsync("HTTP/1.0 400 Bad Request"); 83 await writer.WriteLineAsync("Content-Type: text/plain; charset=UTF-8"); 84 await writer.WriteLineAsync(); 85 await writer.WriteLineAsync("Bad Request"); 86 return; 87 } 88 89 // パラメータの文字列取得 90 Dictionary<string, string> paramDict = new Dictionary<string, string>(); 91 92 if (readLineAddr[0] == "GET") 93 { 94 // GETメソッド取得時のパラメータの文字列取得 95 paramDict = GetParameterStr(readLineAddr[1]); 96 } 97 else if (readLineAddr[0] == "POST") 98 { 99 // POSTメソッド取得時のパラメータの文字列取得 100 paramDict = PostParameterStr(readList); 101 } 102 else 103 { 104 // エラーのステータスが返っていることをレスポンスに帰す 105 await writer.WriteLineAsync("HTTP/1.0 400 Bad Request"); 106 await writer.WriteLineAsync("Content-Type: text/plain; charset=UTF-8"); 107 await writer.WriteLineAsync(); 108 await writer.WriteLineAsync("Bad Request"); 109 return; 110 } 111 112 if (paramDict != null && paramDict.Count > 0) 113 { 114 foreach (KeyValuePair<string, string> keyPair in paramDict) 115 { 116 // リクエストのKey値、Value値を出力する 117 Console.WriteLine(string.Format("Key:{0}, Value:{1}", keyPair.Key, keyPair.Value)); 118 } 119 120 Console.WriteLine(""); 121 } 122 123 // レスポンスを返す 124 // ヘッダー部 125 await writer.WriteLineAsync("HTTP/1.0 200 OK"); 126 await writer.WriteLineAsync("Content-Type: text/plain; charset=UTF-8"); 127 await writer.WriteLineAsync(); // 終わり 128 129 // ボディ 130 await writer.WriteLineAsync("Hello!World!"); 131 } 132 } 133 } 134 135 /// <summary> 136 /// 取得したGETリクエストからパラメータ部の文字列を取り出す。 137 /// </summary> 138 /// <param name="readLine"></param> 139 /// <returns></returns> 140 static private Dictionary<string, string> GetParameterStr(string paramStr) 141 { 142 // HTMLとパラメータは「?」区切り 143 string[] dataList = paramStr.Split('?'); 144 145 if (dataList == null || dataList.Count() < 2) 146 { 147 return new Dictionary<string, string>(); 148 } 149 150 // パラメータは「&」区切りなので各々を返す。これは、ない場合もある 151 string[] paramList = dataList[1].Split('&'); 152 153 if (paramList == null || paramList.Count() < 2) 154 { 155 // ない場合は空の値を返す 156 return new Dictionary<string, string>(); 157 } 158 159 // パラメータのDictionary 160 Dictionary<string, string> paramDict = new Dictionary<string, string>(); 161 162 foreach (string paramData in paramList) 163 { 164 // パラメータは「=」で結ばれている 165 string[] paramPair = paramData.Split('='); 166 167 if (paramPair.Count() == 2) 168 { 169 // パラメータのKey名とValue名を取得 170 paramDict.Add(paramPair[0], paramPair[1]); 171 } 172 } 173 174 return paramDict; 175 } 176 177 /// <summary> 178 /// 取得したPOSTリクエストからパラメータ部の文字列を取り出す 179 /// </summary> 180 /// <param name="readList"></param> 181 /// <returns></returns> 182 private static Dictionary<string, string> PostParameterStr(List<string> readList) 183 { 184 Dictionary<string, string> retDict = new Dictionary<string,string>(); 185 bool flag = false; 186 187 foreach (string readline in readList) 188 { 189 if (string.IsNullOrEmpty(readline)) 190 { 191 // 空文字があれば、その次がPOSTメソッドのパラメータ部である。 192 flag = true; 193 } 194 195 if(flag && !string.IsNullOrEmpty(readline)) 196 { 197 // POST部のパラメータのフラグONで、空文字でない場合、パラメータ取得 198 string[] paramList = readline.Split('&'); 199 200 foreach (string param in paramList) 201 { 202 // パラメータを取得 203 string[] data = param.Split('='); 204 205 if (data.Count() == 2) 206 { 207 // key = valueの形式のものだけ格納する 208 retDict.Add(data[0], data[1]); 209 } 210 } 211 } 212 } 213 214 return retDict; 215 } 216}
クライアント側のコードを以下に示します。
C#
1public partial class Form1 : Form 2{ 3 public Form1() 4 { 5 InitializeComponent(); 6 } 7 8 /// <summary> 9 /// 送信ボタン押下 10 /// </summary> 11 /// <param name="sender"></param> 12 /// <param name="e"></param> 13 private void btnSend_Click(object sender, EventArgs e) 14 { 15 const string url = "http://localhost:1234/index.html"; 16 // Webクライアントを設定 17 WebClient webclient = new WebClient(); 18 // 19 NameValueCollection nameValue = new NameValueCollection(); 20 21 nameValue.Add("word", "Internet"); 22 nameValue.Add("id", "1"); 23 24 // 指定したURLにデータを送信 25 byte[] resData = webclient.UploadValues(url, nameValue); 26 27 webclient.Dispose(); 28 29 // レスポンス表示 30 string resText = Encoding.UTF8.GetString(resData); 31 this.txtRedData.Text = resText; 32 } 33}
最初にサーバー側の処理を実行し、次にクライアント側の処理を実行すると、
コンソール出力には以下のように表示されます。
POST /index.html HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost:1234
Content-Length: 18
Expect: 100-continue
Connection: Keep-Alive
本来であれば、
POST /index.html HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost:1234
Content-Length: 18
Expect: 100-continue
Connection: Keep-Alive
<空行>
word=Internet&id=1
のように表示され、「Connection」の2行下にパラメータ部である
「word=Internet&id=1」
を表示したいのですが、サーバー側のDoServerメソッドで、空行が来た次のループで
「ReadLineAsync」を実行するとDoServerメソッドを抜けてしまっているようです。
改善点、修正点が分かる方がおられましたらお教え下さい。

回答2件
あなたの回答
tips
プレビュー
下記のような回答は推奨されていません。
このような回答には修正を依頼しましょう。
また依頼した内容が修正された場合は、修正依頼を取り消すようにしましょう。