Friday, November 17, 2006

Цуурай

Таны толилуур вэб үйлчлэгч рүү хүсэлт явуулахдаа толгойн хэсгээ хэрхэн бүрдүүлж буйг үүгээр шалгаж болно.


package web.server;

/**
*
* @author java4salhi
*/
import java.io.*;
import java.net.*;

public class HttpEcho
{
public static void main (String args[])
{
if (args.length == 0)
{
System.out.println("USAGE: java HttpEcho ");
return;
}


try {
int port = Integer.parseInt(args[0]);
ServerSocket server = new ServerSocket(port);
System.out.println("Started HttpEcho on port " + port + ". Press CTRL-C to end.\n");

while (true)
{
Socket client = server.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream());

StringBuffer header = new StringBuffer("");
StringBuffer body = new StringBuffer("");
String data;
int contentLength = 0;
int pos = 0;

// get the header
while ((data = in.readLine()) != null)
{
// the header ends at the first blank line
if (data.length() == 0)
break;
header.append(data + "\n");
pos = data.toLowerCase().indexOf("content-length:");
if (pos >= 0)
contentLength = Integer.parseInt(data.substring(pos + 15).trim());
}

// get the body, if any
if (contentLength > 0)
{
try {
char[] cbuf = new char[contentLength];
in.read(cbuf);
body.append(cbuf);
} catch (Exception e) {
body.append("Error: " + e);
}
} else {
body.append("No Body information retrieved. " +
"Content-Length equals zero or was not specified.");
}

// write back to the client
out.print("HTTP/1.0 200\nContent Type: text/plain\n\n");
out.println("HEADER:");
out.println(header);
out.println("BODY:");
out.println(body);

// write to the console too
System.out.println("HEADER:");
System.out.println(header);
System.out.println("BODY: (" + contentLength + " / " + body.length() + ")");
System.out.println(body);
System.out.println("\n============ HttpEcho on Port " + port +
". Press CTRL-C to End ============\n");

// close all the client streams so we can listen again
out.close();
in.close();
client.close();
}
} catch (Exception e) {
System.out.println(e);
}
}
}

Thursday, November 16, 2006

Бичил вэб үйлчлүүлэгч, хоёрдугаар хэсэг

Энэ удаад StringBuffer обьект ашиглан үйлчлэгчээс ирэх хариуны толгойн хэсгийг уншиж байна. Уншихдаа BufferedReader обьектийг, харин хүсэлт илгээхдээ PrintWriter-ийг ашиглажээ.

Үйлчлэгчээс ирэх хариуны биеийг хэрхэн уншиж буйг бас анхаарагтун.


package web.server;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.logging.Logger;

/**
*
* @author
*/
public class SimpleClient {
static int PORT = 1010;
static String getHeader = "GET / HTTP/1.1";
static String agentHeader ="User-Agent: Simple Client";
static String hostHeader = "host: %s: %s";

static Logger logger = Logger.getLogger("SimpleClient");

public static void main(String[] args) throws Exception {

String host = "localhost";
int port = PORT;

try {
host = args[0];
} catch (Exception e) {}

try {
port = Integer.parseInt(args[1]);
} catch (Exception e) {}

Socket socket = new Socket( host, port);
logger.info("started SimpleClient on port " + socket.getLocalPort());

BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));

PrintWriter out = new PrintWriter(socket.getOutputStream());

// sending get request
out.println(getHeader);
out.println(String.format(hostHeader, host, port));
out.println(agentHeader);

out.println();
out.flush();

StringBuffer header = new StringBuffer("");
StringBuffer body = new StringBuffer("");
String data;
int contentLength = 0;
int pos = 0;

// get the header
while ( (data = in.readLine()) != null) {

// the header ends at the first blank line
if (data.length() == 0)
break;

header.append(data + "\n");
pos = data.toLowerCase().indexOf("content-length:");
if (pos >= 0)
contentLength = Integer.parseInt(data.substring(pos + 15).trim());
}

// get the body, if any
if (contentLength > 0) {
try {
char[] cbuf = new char[contentLength];
in.read(cbuf);
body.append(cbuf);
} catch (Exception e) {
body.append("Error: " + e);
}
} else {
body.append("No Body information retrieved. " +
"Content-Length equals zero or was not specified.");
}

// write to the console
System.out.println("HEADER:");
System.out.println(header.toString());
System.out.println("BODY: (" + contentLength + " / " + body.length() + ")");
System.out.println(body.toString());


// close all the client streams so we can listen again
out.close();
in.close();
socket.close();

logger.info("SimpleClient ended on port " + socket.getLocalPort());
}
}

Wednesday, November 15, 2006

Бичил вэб үйлчлэгч, хоёрдугаар хэсэг

Анхны бичил вэб үйлчлэгчийг бага зэрэг сайжруулан боловсронгуй болгосон хувилбарыг дор харуулав. Энд үйлчлүүлэгчээс ирэх хүсэлтийг уншихдаа InputStream обьектыг бус, харин мэдээллийг мөр, мөрөөр нь унших чадвартай BufferedReader обьектыг ашигласан байна. Мөн мөрийн задаргаа хийх жишээ болгон GET хүсэлт доторхи URL хэсгийг ялган авч харуулав. Ингэснээр URL-ээр өгөгдсөн файлыг үйлчлүүлэгч рүү хариу болгон илгээх зам нээгдэнэ.


package web.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.logging.Logger;


/**
*
* @author Erdenebat Byambaa, USI company
* very simple, very ugly implementation of web server
*/
public class SimpleServer {

static Logger log = Logger.getLogger("SimpleServer");

static final int PORT = 1010;
static final String NEWLINE = "\r\n";
static final String OK = "HTTP/1.1 200 OK" + NEWLINE;

static String WelcomeMessage = "<html>" +
"<head><title>Welcome</title></head>" +
"<body><h1>" +
"Welcome, Sir/Madame" +
"</h1></body>"+
"</html>";
static long length = WelcomeMessage.length();


public static void main(String[] args) throws IOException {

int port = PORT;

if (args.length > 0)
port = Integer.parseInt(args[0]);

System.out.println();
log.info("SimpleServer is listening on " + port);
System.out.println();

// creating server socket
ServerSocket server = new ServerSocket(port);

// main loop
while (true) {

// accepting a new client
Socket socket = server.accept();
int remotePort = socket.getPort();

log.info("accepting new client on " + remotePort);

InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(in,"UTF-8"));

String line = br.readLine();
String[] tokens = line.split(" ");
String rawUrl = tokens[1];

log.info("requested resource is " + rawUrl);
System.out.println();

while ( (line = br.readLine()) != null && !line.equals("")) {
for (byte data: line.getBytes()) {

String hex = Integer.toHexString(data);
hex = (hex.length()==1? "0"+hex: hex);
System.out.print(hex + " ");
}

System.out.println("0d 0a");
System.out.println(line);
System.out.println();

}

if (line.equals("")) {
System.out.println("0d 0a");
}

// so, here we have completed receiving data from client
System.out.println();
System.out.println("sending response ...");

// sending OK
out.write(OK.getBytes());

// starting http header sending
out.write("Content-Type: text/html".getBytes());
out.write(NEWLINE.getBytes());

out.write("Content-Length: ".getBytes());
out.write(Long.toString(length).getBytes());
out.write(NEWLINE.getBytes());

out.write("Date: ".getBytes());
out.write(new Date().toString().getBytes());
out.write(NEWLINE.getBytes());

out.write("Server: Simplest Web Server".getBytes());
out.write(NEWLINE.getBytes());

// end of header sending
out.write(NEWLINE.getBytes());

// sending body part
out.write(WelcomeMessage.getBytes());

in.close();
out.close();
socket.close();

System.out.println();
log.info("disconnected --- remote port: " + remotePort);
System.out.println();

} // main loop

} // main method

} // SimpleServer

хөтөлбөрийн гарц:
Nov 15, 2006 4:18:25 PM web.server.SimpleServer main
INFO: SimpleServer is listening on 1010

Nov 15, 2006 4:18:52 PM web.server.SimpleServer main
INFO: accepting new client on 1056
Nov 15, 2006 4:18:52 PM web.server.SimpleServer main
INFO: requested resource is /

48 6f 73 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 3a 31 30 31 30 0d 0a
Host: localhost:1010

55 73 65 72 2d 41 67 65 6e 74 3a 20 4d 6f 7a 69 6c 6c 61 2f 35 2e 30 20 28 57 69 6e 64 6f 77 73 3b 20 55 3b 20 57 69 6e 64 6f 77 73 20 4e 54 20 35 2e 31 3b 20 65 6e 2d 55 53 3b 20 72 76 3a 31 2e 38 2e 30 2e 38 29 20 47 65 63 6b 6f 2f 32 30 30 36 31 30 32 35 20 46 69 72 65 66 6f 78 2f 31 2e 35 2e 30 2e 38 0d 0a
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8

41 63 63 65 70 74 3a 20 74 65 78 74 2f 78 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 68 74 6d 6c 2b 78 6d 6c 2c 74 65 78 74 2f 68 74 6d 6c 3b 71 3d 30 2e 39 2c 74 65 78 74 2f 70 6c 61 69 6e 3b 71 3d 30 2e 38 2c 69 6d 61 67 65 2f 70 6e 67 2c 2a 2f 2a 3b 71 3d 30 2e 35 0d 0a
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

41 63 63 65 70 74 2d 4c 61 6e 67 75 61 67 65 3a 20 65 6e 2d 75 73 2c 65 6e 3b 71 3d 30 2e 35 0d 0a
Accept-Language: en-us,en;q=0.5

41 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e 67 3a 20 67 7a 69 70 2c 64 65 66 6c 61 74 65 0d 0a
Accept-Encoding: gzip,deflate

41 63 63 65 70 74 2d 43 68 61 72 73 65 74 3a 20 49 53 4f 2d 38 38 35 39 2d 31 2c 75 74 66 2d 38 3b 71 3d 30 2e 37 2c 2a 3b 71 3d 30 2e 37 0d 0a
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7

4b 65 65 70 2d 41 6c 69 76 65 3a 20 33 30 30 0d 0a
Keep-Alive: 300

43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a
Connection: keep-alive

0d 0a

sending response ...
Nov 15, 2006 4:18:52 PM web.server.SimpleServer main
INFO: disconnected --- remote port: 1056

Monday, November 13, 2006

Бичил вэб үйлчлүүлэгч, нэгдүгээр хэсэг


Маш хялбархан вэб үйлчлүүлэгчийн жишээ хөтөлбөрийг дор харуулав. Үүгээр ямар ч вэб үйлчлэгч рүү GET хүсэлт илгээн хариу авч болно. Гэхдээ хариугаа зөвхөн бичвэр хэлбэрээр авна. Өмнөх хичээлээр бүтээсэн маш хялбархан вэб үйлчлэгчтэй ярилцсан харилцан яриаг жишээ болгон хавсаргав.

package web.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
*
* @author Erdenebat Byambaa, USI company
*/
public class SimplestClient {

static int PORT = 1010;
static String NEWLINE = "\r\n";
static String getHeader = "GET / HTTP/1.1" + NEWLINE;
static String hostHeader = "Host: localhost:" + PORT + NEWLINE;
static String agentHeader ="User-Agent: Simple Client" + NEWLINE;

public static void main(String[] args) throws IOException {

System.out.println();
System.out.println("SimplestClient: starting...");

// connecting to server
Socket socket = new Socket("localhost", PORT);

// obtaining input & output stream from socket
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();

// sending GET request to server
out.write(getHeader.getBytes());
out.write(hostHeader.getBytes());
out.write(agentHeader.getBytes());

// end of request
out.write(NEWLINE.getBytes());


// preparing to receive response from server
int data = 0; // holds single byte;
byte[] line = new byte[256]; // used to compose/collect a line by sequentally reading single byte from input stream
// a line is terminated by two bytes: 13 10

boolean CR = false; // true, if data == 13 (or 0d)
boolean NL = false; // true, if data == 10 (or 0a) and preceding data == 13
int lineCounter = 0;

System.out.println("SimplestClient: header start");
// starting to recieve request from server
while ( (data = in.read()) > -1 ) {

line[lineCounter] = (byte)data;
lineCounter++;

String hex = Integer.toHexString(data);
hex = (hex.length()==1? "0"+hex: hex);

System.out.print(hex + " ");

// simple state machine used to detect line terminator
switch (data) {
case 13: CR = true; break;
case 10: NL = (CR ? true: false); break;
default: CR = false; NL = false; break;
}

if ( CR && NL ) {
if (lineCounter == 2) {
// empty line contains only two bytes: 13 10
// it happens at the end of request header
break;
} else {

// reached at the end of line
// let's build a string representing a line and print it
byte[] temp = new byte[lineCounter];
System.arraycopy(line, 0, temp, 0, lineCounter);

System.out.println();
System.out.print(new String(temp));
System.out.println();

lineCounter = 0;
CR = false;
NL = false;
}
}

} // end of header part
System.out.println();
System.out.println("SimplestClient: header end");

// starting to receive content part
int contentCounter = 0;
byte[] tempContent = new byte[1024];

while ( (data = in.read()) > -1 )
tempContent[contentCounter++] = (byte)data;


byte[] actualContent = new byte[contentCounter];
System.arraycopy(tempContent, 0, actualContent, 0, contentCounter);

System.out.println();
System.out.println("SimplestClient: content start");
System.out.println("SimplestClient: content length = " + contentCounter);
System.out.println(new String(actualContent));
System.out.println("SimplestClient: content end");
System.out.println();

socket.close();

System.out.println("SimplestClient: ending...");
}
}


энэ хөтөлбөрийн гарц:

SimplestClient: starting...
SimplestClient: header start
48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d 0a
HTTP/1.1 200 OK

43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 65 78 74 2f 68 74 6d 6c 0d 0a
Content-Type: text/html

43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 38 39 0d 0a
Content-Length: 89

44 61 74 65 3a 20 54 75 65 20 4e 6f 76 20 31 34 20 31 31 3a 31 30 3a 30 37 20 55 4c 41 54 20 32 30 30 36 0d 0a
Date: Tue Nov 14 11:10:07 ULAT 2006

53 65 72 76 65 72 3a 20 53 69 6d 70 6c 65 73 74 20 57 65 62 20 53 65 72 76 65 72 0d 0a
Server: Simplest Web Server

0d 0a
SimplestClient: header end

SimplestClient: content start
SimplestClient: content length = 89
<html><head><title>Welcome</title></head><body><h1>Welcome, Sir/Madame</h1></body></html>
SimplestClient: content end

SimplestClient: ending...


Sunday, November 12, 2006

Бичил вэб үйлчлэгч, нэгдүгээр хэсэг


Ямар нэгэн толилуур (Mozilla, FireFox, Internet Explorer гэх мэт) ашиглан хандсан хэрэглэгч бүр рүү Welcome, Sir/Madame гэсэн мэдээ илгээх маш хялбархан вэб үйлчлэгчийн жишээ хөтөлбөрийг дор харуулав.

package web.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.Date;

/**
*
* @author Erdenebat Byambaa, USI company
* вэб үйлчлэгчийн маш хялбар жишээ хөтөлбөр
*/
public class SimplestServer {

static final int PORT = 1010;
static final int BUFFER_SIZE = 10000;
static final String NEWLINE = "\r\n";
static final String OK = "HTTP/1.1 200 OK" + NEWLINE;
static String WelcomeMessage = "<html>" +
"<head><title>Welcome</title></head>" +
"<body><h1>" +
"Welcome, Sir/Madame" +
"</h1></body>"+
"</html>";
static long length = WelcomeMessage.length();


public static void main(String[] args) throws IOException {

// Тооцоолууруудын хооронд мэдээлэл солилцохдоо Socket буюу Сүв
//
ашигладаг. Сүв нь мэдээлэл хүлээн авах үүрэгтэй ServerSocket
//
буюу ҮйлчлэгчСүв, мэдээлэл илгээх үүрэгтэй Socket буюу Сүв
//
гэсэн үндсэн хоёр хэлбэртэй. Шинээр Сүв үүсгэхдээ портын дугаар
//
буюу боомтын дугаарыг зааж өгнө. Боомтын дугаар нь 0-ээс 65535
//
хүртэлхи дурын бүхэл тоо байж болно. Нэг боомт дээр зөвхөн нэг
//
сүв үүсгэх боломжтой. 1024 хүртэлхи бүх боомтууд эзэнтэй байдаг.

ServerSocket server = new ServerSocket(PORT);
System.out.println("listening on " + PORT);

// үндсэн эргүүлэг
// аливаа үйлчлэгч хөтөлбөр битүү тойргоор тойрон эргэлдэх
// зарчимтай.
while (true) {

// ҮйлчлэгчСүв нь өөрийн боомт дээр гаднаас мэдээлэл ирж буй эсэхийг
// байнга шалгаж байдаг. Хэрвээ гаднаас мэдээлэл ирэхгүй бол цааш
// хөдлөхгүй. Үүнийг сонсох гэнэ.

// Хэрэв гаднаас мэдээлэл ирвэл ямар нэгэн сул чөлөөтэй
// боомт дээр сүв үүсгэнэ. Сүв гэдгийг өөрөөр нүх гэж ойлгож болно.
// Шинээр үүссэн Сүв нь гаднаас холбогдсон нөгөө тооцоолуурын сүвтэй
// холбогдохоороо нэг төрлийн хоолой үүсгэнэ.

// Ямар нэгэн гэмтэл гарахыг эс тооцвол, харилцагч хоёрын аль нэг нь
// сүвээ хаахад хоолой тасарч тухайн сүв дахин сул, чөлөөтэй болно.

// Тооцоолуур дээр олон хөтөлбөр зэрэгцэн гүйдгийг санах хэрэгтэй.
// Тиймээс сүлжээний хөтөлбөр бичихдээ ямар, ямар хөтөлбөр аль, аль
// боомтыг ашигладаг талаар мэдэж байх хэрэгтэй.

// Эхлээд хөтөлбөрийн явдлаа түр зогсоон сонсоё.
// Шинээр хэрэглэгч холбогдмогц, үүссэн Сүвийг socket нэртэй
// хувьсагч руу хадгалан цааш хөдлөе.
Socket socket = server.accept();

// Хөтөлбөр энд ирнэ гэдэг нь хэрэглэгч холбогджээ гэсэн үг.

// Сүв нь ОрцУрсгуур ба ГарцУрсгуур гэсэн хоёр хэсэгтэй.
// ОрцУрсгуур нь мэдээллийг зөвхөн хүлээн авч унших үүрэгтэй.
// ГарцУрсгуур нь мэдээллийг зөвхөн бичиж илгээх үүрэгтэй.
// Энэ хоёр нийлэхээрээ хоёр чиглэлтэй хоолой болно.

// ГарцУрсгуур руу мэдээлэл бичсэн л бол тухайн мэдээллийг
// буцаан авах боломжгүйгээр цааш урсган авч одно.
// ОрцУрсгуураас мэдээлэл уншсан л бол тухайн мэдээлэл
// дахин сэргэхгүйгээр урсгуураас сорогдон үгүй болно.

// Үүссэн сүвийнхээ ОрцУрсгуурыг in хувьсагч руу хадгалъя.
// Хэрэглэгчийн хүсэлтийг үүгээр дамжуулан уншина.
InputStream in = socket.getInputStream();

// Үүссэн сүвийнхээ ГарцУрсгуурыг out руу хадгалъя.
// Хариугаа үүгээр дамжуулан хэрэглэгч рүү илгээнэ.
OutputStream out = socket.getOutputStream();

// ОрцУрсгуураас унших нэг байт мэдээлэл түр хадгалах үүр.
int data = 0;

// Энэ бүл дотор 256 хүртэл байт мэдээлэл хадгалж болно.
// Хэрэглэгчээс ирэх хүсэлт хэдэн ч мөрөөс бүрдэж болно.
// Гэхдээ нэг мөр нь 256 ширхэг байтаас хэтрэхгүй гэж үзэв.
byte[] line = new byte[256]; //

// мөрийн төгсгөлд 0d 0a гэсэн хоёр байт заавал олдох ёстой.
//
0d байтыг CR, харин 0a байтыг NL гэж тэмдэглэсэн байгаа.
// Энэ хоёр байтаар мөр хаана эхэлж, хаана дуусч буйг мэднэ.


boolean CR = false; // return код мөн эсэх
boolean NL = false; // new line код мөн эсэх

// ОрцУрсгуураас мэдээлэл уншихдаа хэрэглэх тоолуур.
int count = 0;

// Энэ бүл дотор 10000 ширхэг байт хадгалж болно.
// Тэгэхлээр нэлээд олон мөр багтаах чадалтай гэсэн үг.
// Одоогоор бид үүнийг ашиглахгүй.
byte[] buffer = new byte[BUFFER_SIZE];

// хэрвээ ОрцУрсгуураас -1 гэсэн утга ирвэл орц хаагджээ,
// цаашдаа мэдээлэл ирэхгүй гэсэн үг.
// Энэ тохиолдолд, давталтаас гарч цааш явна.

// -1 утга ирсний дараа орцоос унших үйлдэл хийж болохгүй.
// Уншина гэдэг нь хүлээнэ гэсэн үг, харин хүлээнэ гэдэг нь
// мэдээлэл иртэл байрнаасаа хөдлөхгүй зогсоно гэсэн үг.
//
// Хэрвээ энэ хөтөлбөрийн кодыг бүхэлд нь гүйлгэн харж
// явдлыг нь шинжвэл дараахь байдалтай:

// эхлэл
// 1. сонсох
// 2. хүсэлт тайлан унших
// 3. хариу илгээх
// 4. сүвээ хаах
// төгсгөл

// Нэг хүсэлт боловсруулахдаа дээрхи дөрвөн шатыг дамжиж байна.
// Сүүлийн шатанд сүвээо хаасныхаа дараа дахиад эхний шатандаа
//
эргэн ирж дараагийн хүсэлт ирэхийг хүлээнэ. Үйл явдал ийм
// байдлаар тасралтгүй үргэлжлэн эргэлдэнэ.

// Хүсэлтийг тайлан уншихдаа өөр нэг эргүүлэг үүсгэж байна.
// Явдлыг нь тоймолбол:

// эхлэл
// 1. байт унших
// 2. уншсан байтаа мөр рүү нэмэх
// 3. төлөвөө өөрчлөх
// төгсгөл

// Хүсэлтийг тайлан уншихдаа энэ гурван шатыг дамжиж байж нэг
//
байт уншина. Хэрэглэгчээс ирэх нэг хүсэлт хэдэн ч мөрнөөс
//
бүрдэж болно. Бас мөр бүхэн ондоо. Тэгэхлээр хэдэн байт
//
унших вэ, хэзээ эргүүлгээ зогсоож дараагийн шат руу шилжих
//
вэ гэдэг тодорхойгүй.

// Энэ асуудлыг шийдэхийн тулд дотоод төлвийн хувьсагчдыг
// ашигласан байна. Ямар ч байтыг уншихад мөрийн төгсгөл мөн,
//
мөрийн төгсгөл биш, хоосон мөр гэсэн гурван төлвийн аль
//
нэгэнд заавал оршино.

// Хэрвээ хоосон мөр байвал эргүүлгээс шууд гарна. Манай
//
эргүүлгийн нэг онцлог бол төгсгөлдөө хүрээд эхлэлдээ заавал
//
буцаж ирдэг, харин дараа нь эргүүлгээс гарах эсэхээ шийднэ.
//
Гэтэл эхлэл маань унших үйлдэл хийж байна.

// Хоосон мөр тааралдахад эргүүлгийн төгсгөлөөс шууд таслан
//
гарснаар унших үйлдлээс мултарч байна. Хоосон мөр гэдэг нь
//
дахиж мэдээлэл ирэхгүй гэсэн үг. Мэдээлэл ирэх явцын дундуур
// ямар нэгэн гэмтлийн улмаас хэрэглэгчтэй тогтоосон хоолой
// тасарч бас болно. Энэ тохиолдолд хэрэглэгчээс дахин
// мэдээлэл ирэхгүй.

// Товчоор хэлэхэд, ОрцУрсгуураас / жишээ нь in.read() / унших
//
үйлдэл хийхдээ тун хянуур, болгоомжтой байх ёстой. Учир нь
//
энэ үйлдэл нэг байт мэдээлэл ирэн иртэл хүлээгээд зогсдог.
//
Хэрэв нөгөө тал мэдээлэл дамжуулахаа зогсоочихсон байхад нь
//
унших гэж оролдвол тэр чигээрээ үүрд хүлээнэ гэсэн үг.

// Ийм үед хөтөлбөрийг хүчээр унтраахгүй л бол цаашаа явахгүй.
// Уул нь нэг хэрэглэгчийн хүсэлтийг хүлээн авч хариуг нь
//
явуулаад дараагийн хүсэлтээ хүлээн авах ёстой. Гэтэл нэг
//
хэрэглэгч дээр очоод зогсчих аваас дараагийн хэрэглэгчтэй
//
харьцаж чадахаа болино.

// Энэ мэт логикийг доороос харж болно.

while ((data = in.read()) > -1) {

line[count] = (byte)data; // уншсан байтаа мөр рүү хадгалъя
count++;

String hex = Integer.toHexString(data);
hex = (hex.length()==1? "0"+hex: hex);

// ямар байт ирснийг арванзургаатын кодоор нь харья
System.out.print(hex + " ");

switch (data) {
case 13: CR = true; break;
case 10: NL = (CR ? true: false); break;
default: CR = false; NL = false; break;
}

// шинэ мөр гэдэг нь дараах хоёр байт: 13 10;
if ( CR &&amp; NL ) {

// хоосон мөр гэдэг нь толгойн хэсэг дууслаа
// гэсэн үг.
// хоосон мөр хоёрхон байтаас бүрдэнэ: 13 10
if (count == 2) {
break;
} else {

// мөр бүтээе
byte[] temp = new byte[count];
System.arraycopy(line,0,temp,0,count);

// мөрөө бичвэр болгон харъя
System.out.println();
System.out.print(new String(temp));
System.out.println();

// шинэ мөр
count = 0;
CR = false;
NL = false;
}
}

}

// хэрэглэгчээс ирсэн хүсэлтийг тайлж уншиж дууслаа
// хариугаа илгээе
System.out.println();
System.out.println("sending response ...");

// эхлээд OK мэдээ илгээнэ
out.write(OK.getBytes());

// http header хэсгээ илгээе
out.write("Content-Type: text/html".getBytes());
out.write(NEWLINE.getBytes());

out.write("Content-Length: ".getBytes());
out.write(Long.toString(length).getBytes());
out.write(NEWLINE.getBytes());

out.write("Date: ".getBytes());
out.write(new Date().toString().getBytes());
out.write(NEWLINE.getBytes());

out.write("Server: Simplest Web Server".getBytes());
out.write(NEWLINE.getBytes());

// http header хэсгээ илгээж дууслаа
// тиймээс, хоосон мөр илгээе
out.write(NEWLINE.getBytes());


// http body биеэ илгээе
out.write(WelcomeMessage.getBytes());

in.close();
out.close();
socket.close();

} // main loop

} // main method

} // SimplestServer


энэ хөтөлбөрийн гарц:

listening on 1010
47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a
GET / HTTP/1.1

48 6f 73 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 3a 31 30 31 30 0d 0a
Host: localhost:1010

55 73 65 72 2d 41 67 65 6e 74 3a 20 4d 6f 7a 69 6c 6c 61 2f 35 2e 30 20 28 57 69 6e 64 6f 77 73 3b 20 55 3b 20 57 69 6e 64 6f 77 73 20 4e 54 20 35 2e 31 3b 20 65 6e 2d 55 53 3b 20 72 76 3a 31 2e 38 2e 30 2e 38 29 20 47 65 63 6b 6f 2f 32 30 30 36 31 30 32 35 20 46 69 72 65 66 6f 78 2f 31 2e 35 2e 30 2e 38 0d 0a
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.8) Gecko/20061025 Firefox/1.5.0.8

41 63 63 65 70 74 3a 20 74 65 78 74 2f 78 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 68 74 6d 6c 2b 78 6d 6c 2c 74 65 78 74 2f 68 74 6d 6c 3b 71 3d 30 2e 39 2c 74 65 78 74 2f 70 6c 61 69 6e 3b 71 3d 30 2e 38 2c 69 6d 61 67 65 2f 70 6e 67 2c 2a 2f 2a 3b 71 3d 30 2e 35 0d 0a
Accept: text/xml,application/xml,application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5

41 63 63 65 70 74 2d 4c 61 6e 67 75 61 67 65 3a 20 65 6e 2d 75 73 2c 65 6e 3b 71 3d 30 2e 35 0d 0a
Accept-Language: en-us,en;q=0.5

41 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e 67 3a 20 67 7a 69 70 2c 64 65 66 6c 61 74 65 0d 0a
Accept-Encoding: gzip,deflate

41 63 63 65 70 74 2d 43 68 61 72 73 65 74 3a 20 49 53 4f 2d 38 38 35 39 2d 31 2c 75 74 66 2d 38 3b 71 3d 30 2e 37 2c 2a 3b 71 3d 30 2e 37 0d 0a
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7

4b 65 65 70 2d 41 6c 69 76 65 3a 20 33 30 30 0d 0a
Keep-Alive: 300

43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a
Connection: keep-alive

43 61 63 68 65 2d 43 6f 6e 74 72 6f 6c 3a 20 6d 61 78 2d 61 67 65 3d 30 0d 0a
Cache-Control: max-age=0

0d 0a
sending response ...


Энд, үйлчлэгч, үйлчлүүлэгч хоёрын хооронд явагдаж буй харилцан яриаг байт болон бичвэр гэсэн хоёр хэлбэрээр зэрэг харуулсан байна. Гэхдээ зөвхөн header буюу толгойн хэсэг.

Хөтөлбөр маань сүвээр (сокет) дамжин хэрэглэгчээс (толилуур) ирж буй өгөгдлийг эхлээд байт мөр (line, дараа нь temp), дараа нь тэмдгийн хэлхээс / new String(temp) / болгон хувиргаж байна. Мөрийн төгсгөлийг 0d, 0a (ердийн 13, 10) гэсэн хоёр байтаар дүрсэлдгийг сануулъя.

Хэрвээ тухайн мөр зөвхөн 0d, 0a гэсэн хоёрхон байтаас бүрдэж байвал уг мөр нь хоосон мөр гэсэн үг. Энэ нь толгойн хэсэг дууслаа гэсэн үг.

Одоохондоо хариу мэдээ маань WelcomeMessage хувьсагч доторхи тогтмол бичвэр байна. Үүнийг хэрхэн файлаас уншдаг болгон өөрчлөх вэ?