1. まず、http のメッセージ構造を見てみましょう。
1.リクエストメッセージ
HTTP リクエスト メッセージは、リクエスト行、リクエスト ヘッダー、空白行、リクエスト データの 4 つの部分で構成されます。次の図は、リクエスト メッセージの一般的な形式を示しています。
(1)リクエストライン
リクエスト行は、スペースで区切られたリクエスト メソッド フィールド、URL フィールド、HTTP プロトコル バージョン フィールドの 3 つのフィールドで構成されます。たとえば、GET /index.html HTTP/1.1。
HTTPプロトコルのリクエストメソッドには、GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECTなどがあります。ここではよく使われるGETメソッドとPOSTメソッドを紹介します。
GET: GET メソッドは、クライアントがサーバーからドキュメントを読み取りたい場合に使用されます。 GET メソッドでは、サーバーが URL によって特定されたリソースを応答メッセージのデータ部分に入れてクライアントに送り返す必要があります。 GET メソッドを使用する場合、リクエスト パラメータと対応する値は URL に追加され、渡されるリクエスト パラメータの長さは URL の終わりと始まりを表すために使用されます。パラメーターは限られています。たとえば、/index.jsp?id=100&op=bind のようになります。
POST: POST メソッドは、クライアントがサーバーに大量の情報を提供する場合に使用できます。 POST メソッドは、HTTP リクエスト データ内のリクエスト パラメータを名前/値の形式でカプセル化し、大量のデータを送信できます。
(2)リクエストヘッダ
リクエストヘッダーはキーワードと値のペアで構成され、1 行に 1 つのペアがあり、キーワードと値は英語のコロン「:」で区切られます。リクエスト ヘッダーは、クライアントのリクエストについてサーバーに通知します。一般的なリクエスト ヘッダーは次のとおりです。
ユーザーエージェント: リクエストを生成したブラウザのタイプ。
Accept: クライアントによって認識されるコンテンツ タイプのリスト。
ホスト: 要求されたホスト名。複数のドメイン名が同じ IP アドレスにあることを許可します。つまり、仮想ホストです。
(3)空行
最後のリクエスト ヘッダーの後には空行が続き、これ以上続くリクエスト ヘッダーがないことをサーバーに通知するために、キャリッジ リターンとライン フィード文字が送信されます。
(4)リクエストデータ
リクエストデータはGETメソッドではなくPOSTメソッドで使用されます。 POST メソッドは、顧客がフォームに記入する必要がある状況に適しています。リクエスト データに関連して最も一般的に使用されるリクエスト ヘッダーは、Content-Type と Content-Length です。
2. 応答メッセージ
応答メッセージの形式は、最初の行が異なることを除いて、通常、要求メッセージと似ています。読者は、この点の概要をインターネットで見つけることができるため、ここでは詳しく説明しません。
2. プログラムの実施
プログラムを実装する手順は次のとおりです。
1. クライアントのブラウザからリクエストを受信します。
2. リクエストを処理するための新しいスレッドを作成します。
3. メッセージ データを読み取り、メッセージが正しいかどうかを判断し、メッセージの内容を分析します。
4. 応答メッセージを作成してクライアントに送信します。
5. 処理スレッドを終了し、他の顧客リクエストを処理します。
プログラムコードは次のとおりです。
プレーンコピーをクリップボードプリントに表示しますか?
java.net.* をインポートします。
java.io.* をインポートします。
java.util.* をインポートします。
java.lang.* をインポートします。
パブリック クラス WebServer {
public static void main(String [] args){
整数ポート;
ServerSocket サーバーソケット;
試す{
port=Integer.parseInt(args[0]);
}
catch (例外 e){
ポート=8080;
}
試す{
server_socket=新しいサーバーソケット(ポート);
System.out.println("ポートで実行中の Web サーバー"+server_socket.getLocalPort());
while(true){
ソケットソケット=server_socket.accept();
System.out.println("新しい接続が受け入れられました"+socket.getInetAddress()+":"+socket.getPort());
// リクエストを処理するために特定のリクエストのスレッドを作成します
試す{
httpRequestHandler request=new httpRequestHandler(ソケット);
スレッド thread=新しいスレッド(リクエスト);
thread.start();
}
catch(例外 e){
System.out.println(e);
}
}
}
catch(IOException e){
System.out.println(e);
}
}
}
//リクエストを処理するためのスレッドクラス
クラス httpRequestHandler は Runnable{ を実装します
最終的な静的文字列 CRLF="rn";
ソケットソケット。
InputStream 入力;
OutputStream 出力。
BufferedReader br;
// 要求されたファイルの種類が正しいかどうかを判断します
ブール値 fileType=true;
//初期化パラメータ
public httpRequestHandler(Socket ソケット) が例外をスローします{
this.socket=ソケット;
this.input=socket.getInputStream();
this.output=socket.getOutputStream();
this.br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
// スレッドを開始します
public void run(){
試す{
プロセスリクエスト();
}
catch(例外 e){
System.out.println(e);
}
}
//リクエストを処理するためのコア関数
private void processRequest() が例外をスローする{
while(true){
文字列 headerLine=br.readLine();
System.out.println("クライアントリクエストは"+headerLine);
if(headerLine.equals(CRLF)||headerLine.equals(""))
壊す;
StringTokenizer s=new StringTokenizer(headerLine);
文字列 temp=s.nextToken();
if(temp.equals("GET")){
文字列ファイル名=s.nextToken();
ファイル名="."+ファイル名;
FileInputStream fis=null;
ブール値のファイル存在 = true;
if(!(ファイル名.endsWith(".htm")||ファイル名.endsWith(".html")))
{
this.fileType=false;
試す{
fis=new FileInputStream("error.html");
}
catch(FileNotFoundException e){
ファイル存在=false;
}
}
それ以外{
試す{
fis=新しいFileInputStream(ファイル名);
}
catch(FileNotFoundException e){
ファイル存在=false;
}
}
String serverLine="サーバー: 単純な Java Web サーバー";
文字列 statusLine=null;
文字列 contentTypeLine=null;
文字列エンティティボディ = null;
文字列 contentLengthLine = "エラー";
if(fileExists&&this.fileType){
statusLine="HTTP/1.0 200 OK"+CRLF;
contentTypeLine="コンテンツ タイプ:"+this.contentType(ファイル名)+CRLF;
contentLengthLine="Content-Length:"+(new Integer(fis.available())).toString()+CRLF;
}
それ以外{
if(fileExists&&this.fileType==false){
statusLine="HTTP/1.0 400 BadRequest"+CRLF;
contentTypeLine="テキスト/html";
entityBody="<HTML>400 Not BadRequest</TITLE></HEAD>"+
"<BODY>400 BadRequest"+
"<br>使用法:http://yourHostName:port/"+
"ファイル名.html</BODY></HTML>";
}
else if(fileExists==false){
statusLine="HTTP/1.0 404 が見つかりません"+CRLF;
contentTypeLine="テキスト/html";
entityBody="<HTML>404 が見つかりません</TITLE></HEAD>"+
「<BODY>404 が見つかりません」+
"<br>使用法:http://yourHostName:port/"+
"ファイル名.html</BODY></HTML>";
}
}
出力.write(statusLine.getBytes());
出力.write(serverLine.getBytes());
出力.write(contentTypeLine.getBytes());
Output.write(contentLengthLine.getBytes());
出力.write(CRLF.getBytes());
if(fileExists&&this.fileType){
sendBytes(fis,output);
fis.close();
}
それ以外{
出力.write(entityBody.getBytes());
}
}
}
試す{
出力.close();
br.close();
ソケット.クローズ();
}
catch(例外 e){}
}
//クライアントからリクエストされたページを送信する
private static void sendBytes(FileInputStream fis,OutputStream os) throws Exception{
byte[] バッファ=新しいバイト[1024];
整数バイト=0;
while((bytes=fis.read(buffer))!=-1){
os.write(バッファ,0,バイト);
}
}
//contentTypeの内容を設定する
private static String contentType(String fileName){
if(fileName.endsWith(".htm")||fileName.endsWith(".html")){
「テキスト/html」を返します;
}
「ファイル名」を返します;
}