实现一个简单的多线程Web服务器
在这个案例中,我们将探讨如何使用Java语言实现一个简单的多线程Web服务器。该服务器使用了Java的Socket和多线程技术,可以监听指定端口,接受客户端的HTTP请求,并处理这些请求。我们将分为两个主要部分:WebServer 类和 HttpRequest 类。
WebServer 类
import java.io.*;
import java.net.*;
public class WebServer {
public static void main(String args[]) {
int port = 6789;
ServerSocket server = null;
Socket socket = null;
try {
// 创建一个ServerSocket来监听指定端口
server = new ServerSocket(port);
System.out.println("正在监听端口:" + server.getLocalPort());
while (true) {
// 当有新的连接请求时,创建一个新的socket,并为其启动一个新线程处理请求
socket = server.accept();
new HttpRequest(socket).run();
}
} catch (Exception e) {
System.out.println(e);
}
}
}
在这里,我们创建了一个 ServerSocket 实例,用于监听指定的端口(在这里是6789)。在一个无限循环中,我们等待新的连接请求,并为每个请求创建一个新的 Socket,然后启动一个新的线程来处理这个请求。
HttpRequest 类
import java.io.*;
import java.net.*;
import java.util.*;
public class HttpRequest implements Runnable {
// 回车换行,根据HTTP规范,回车换行作为Response消息头部行的结束
static String CRLF = "\r\n";
Socket socket;
public HttpRequest(Socket socket) throws Exception {
this.socket = socket;
}
@Override
public void run() {
try {
processRequest();
} catch (Exception e) {
System.out.println(e);
}
}
public void processRequest() throws Exception {
InputStream is = socket.getInputStream();
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 读取请求行
String requestLine = br.readLine();
System.out.println("------------请求报文-------------\n\n" + requestLine);
String headerLine = null;
// 读取所有头部行
while ((headerLine = br.readLine()).length() != 0) {
System.out.println(headerLine);
}
StringTokenizer tokens = new StringTokenizer(requestLine);
// 解析请求行,获取请求方法和文件名
String method = tokens.nextToken();
String fileName = tokens.nextToken();
fileName = "." + fileName;
FileInputStream fis = null;
boolean fileExists = true;
try {
fis = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
fileExists = false;
}
// 构建响应消息的状态行、头部行和实体行
String statusLine;
String contentTypeLine;
String entityBody = null;
if (fileExists) {
statusLine = "HTTP/1.1 200 OK";
contentTypeLine = "Content-type: " + contentType(fileName) + CRLF;
} else {
statusLine = "HTTP/1.1 404 Not Found";
contentTypeLine = "Content-type: " + contentType(fileName) + CRLF;
entityBody = "<HTML><HEAD><TITLE>Not Found</TITLE></HEAD><BODY>404 Not Found</BODY></HTML>";
}
// 向客户端发送响应消息的状态行和头部行
os.writeBytes(statusLine);
os.writeBytes(contentTypeLine);
os.writeBytes(CRLF);
System.out.println("------------部分响应报文-------------\n\n" + statusLine + "\n" + contentTypeLine);
// 如果文件存在,将文件内容通过socket输出流发送到客户端
if (fileExists) {
sendBytes(fis, os);
fis.close();
}
// 如果文件不存在,向客户端发送404错误页面
else {
os.writeBytes(entityBody);
}
// 关闭流和socket
os.close();
br.close();
socket.close();
}
// 获取MIME类型
public static String contentType(String fileName) {
// 省略了一些代码...
}
// 发送字节
public static void sendBytes(FileInputStream fis, OutputStream os) throws Exception {
byte[] buffer = new byte[1024];
int bytes;
while ((bytes = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytes);
}
}
}
在 HttpRequest 类中,我们处理了HTTP请求的各个阶段。我们读取了请求行和头部行,解析请求行以获取请求方法和文件名。然后,我们构建了响应消息的状态行、头部行和实体行,将它们通过 OutputStream 发送给客户端。
完整代码如下:
import java.util.*;
import java.io.*;
import java.net.*;
public class WebServer {
public static void main(String args[]) {
int port = 6789;
ServerSocket server = null;
Socket socket = null;
try {
// 创建一个ServerSocket来监听指定端口
server = new ServerSocket(port);
System.out.println("正在监听端口:" + server.getLocalPort());
while (true) {
// 当有新的连接请求时,创建一个新的socket,并为其启动一个新线程处理请求
socket = server.accept();
new HttpRequest(socket).run();
}
} catch (Exception e) {
System.out.println(e);
}
}
}
class HttpRequest implements Runnable {
Socket socket;
static String CRLF = "\r\n";
public HttpRequest(Socket socket) throws Exception {
this.socket = socket;
}
@Override
public void run() {
try {
processRequest();
} catch (Exception e) {
System.out.println(e);
}
}
public void processRequest() throws Exception {
InputStream is = socket.getInputStream();
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 读取请求行
String requestLine = br.readLine();
System.out.println("------------请求报文-------------\n\n" + requestLine);
String headerLine = null;
// 读取所有头部行
while ((headerLine = br.readLine()).length() != 0) {
System.out.println(headerLine);
}
StringTokenizer tokens = new StringTokenizer(requestLine);
// 解析请求行,获取请求方法和文件名
String method = tokens.nextToken();
String fileName = tokens.nextToken();
fileName = "." + fileName;
FileInputStream fis = null;
boolean fileExists = true;
try {
fis = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
fileExists = false;
}
String statusLine = null;
String contentTypeLine = null;
String entityBody = null;
// 构建响应消息的状态行、头部行和实体行
if (fileExists) {
statusLine = "HTTP/1.1 200 OK";
contentTypeLine = "Content-type: " + contentType(fileName) + CRLF;
} else {
statusLine = "HTTP/1.1 404 Not Found";
contentTypeLine = "Content-type: " + contentType(fileName) + CRLF;
entityBody = "<HTML><HEAD><TITLE>Not Found</TITLE></HEAD><BODY>404 Not Found</BODY></HTML>";
}
// 向客户端发送响应消息的状态行和头部行
os.writeBytes(statusLine);
os.writeBytes(contentTypeLine);
os.writeBytes(CRLF);
System.out.println("------------部分响应报文-------------\n\n" + statusLine + "\n" + contentTypeLine);
// 如果文件存在,将文件内容通过socket输出流发送到客户端
if (fileExists) {
sendBytes(fis, os);
fis.close();
}
// 如果文件不存在,向客户端发送404错误页面
else {
os.writeBytes(entityBody);
}
// 关闭流和socket
os.close();
br.close();
socket.close();
}
// 根据文件名获取对应的MIME类型
public static String contentType(String fileName) {
if (fileName.endsWith(".htm") || fileName.endsWith(".html")) {
return "text/html";
} else if (fileName.endsWith(".txt")) {
return "text/plain";
} else if (fileName.endsWith(".jpg")) {
return "image/jpg";
} else if (fileName.endsWith(".php")) {
return "text/x-php";
} else if (fileName.endsWith(".js")) {
return "text/javascript";
} else if (fileName.endsWith(".rff") || fileName.endsWith(".rtfd")) {
return "text/plain";
} else if (fileName.endsWith(".py")) {
return "text/x-python";
} else if (fileName.endsWith(".rb")) {
return "text/x-ruby";
} else if (fileName.endsWith(".sh")) {
return "text/x-shellscript";
} else if (fileName.endsWith(".pl")) {
return "text/x-perl";
} else if (fileName.endsWith(".sql")) {
return "text/x-sql";
}
return "application/octet-stream";
}
// 将文件内容通过输出流发送到客户端
public static void sendBytes(FileInputStream fis, OutputStream os) throws Exception {
byte[] buffer = new byte[1024];
int bytes = 0;
while ((bytes = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytes);
}
}
}