本项目以http协议的一问一答方式为基础,通过socket来处理服务器和客户端之间的请求;本项目为某机构学习所得,此文记录自己的程序员学习之路;(侵权即删)
**
* 简易服务器,通过socket进行服务器和客户端之间的交流
*/
public class WebServer {
private ServerSocket serverSocket;
private ExecutorService threadPool;
public WebServer() {
try {
System.out.println("正在启动服务器...");
serverSocket = new ServerSocket(8088);
System.out.println("服务器启动成功...");
threadPool = Executors.newFixedThreadPool(50);
} catch (Exception e) {
e.printStackTrace();
}
}
private void start() {
try {
while (true) {
System.out.println("正在连接客户端...");
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功");
// 客户端连接后通过线程进行管理
ClinetHandler handler = new ClinetHandler(socket);
threadPool.execute(handler);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WebServer webServer = new WebServer();
webServer.start();
}
}
**
* 每次连接一个客户端服务器都会在这里处理与客户端链接
*/
public class ClinetHandler implements Runnable {
private Socket socket;
public ClinetHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
//1.解析请求
HttpRequest request = new HttpRequest(socket);
//2.处理请求
HttpResponse response = new HttpResponse(socket);
String fileName = request.getRequestURI();
HttpServlet servlet = ServletContext.getServletMapping(fileName);
if (servlet != null) {
servlet.services(request, response);
} else {
String path = "./src/main/webapp" + fileName;
File entity = new File(path);
if (entity.exists() && entity.isFile()) {
response.setEntity(entity);
} else {
response.setStatusCode(404);
response.setStatusReason("NOT FOUND PAGE");
response.setEntity(new File("./src/main/webapp/root/notfound.html"));
}
}
//3.发送响应
response.flush();
System.out.println("发送响应完毕");
} catch (EmptyRequestException e1) {
System.out.println("------------->这是一个空请求");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
**
* 请求类,接受请求,处理请求
*/
public class HttpRequest {
private Socket socket;
//请求行的参数
private String requestMethod;
private String requestURL;
private String protocol;
//业务请求的参数
private String requestURI;
private Map requestParam = new HashMap<>();
//消息头参数
private Map headParam = new HashMap();
public HttpRequest(Socket socket) throws EmptyRequestException {
try {
this.socket = socket;
parseRequest();
} catch (EmptyRequestException e) {
throw e;
}
}
//解析请求
private void parseRequest() throws EmptyRequestException {
try {
parseRequestLine();
parseRequestHead();
parseRequestContext();
} catch (EmptyRequestException e) {
throw e;
}
}
//解析请求行
private void parseRequestLine() throws EmptyRequestException {
try {
System.out.println("开始解析请求行..");
String line = readLine();
if ("".equals(line)) {
throw new EmptyRequestException("空请求");
}
System.out.println("请求行:" + line);
String[] data = line.split("\\s");
requestMethod = data[0];
requestURL = data[1];
if (requestURL.contains("?")) {
parseRequestUrl(requestURL);
} else {
requestURI = requestURL;
}
protocol = data[2];
System.out.println("请求参数--method:" + requestMethod + " url: " + requestURL + " protocol:" + protocol);
System.out.println("解析请求行完成!");
} catch (EmptyRequestException e1) {
throw e1;
} catch (Exception e) {
e.printStackTrace();
}
}
private void parseRequestUrl(String URL) {
String[] data = requestURL.split("\\?");
if (data.length > 1) {
String[] params = data[1].split("&");
for (String s : params) {
String[] param = s.split("=");
if (param.length > 1) {
requestParam.put(param[0], param[1]);
} else {
requestParam.put(param[0], null);
}
}
}
requestURI = data[0];
}
//解析消息头
private void parseRequestHead() {
try {
System.out.println("开始解析消息头..");
String line = null;
while (!"".equals(line = readLine())) {
String[] data = line.split(": ");
headParam.put(data[0], data[1]);
}
Traverse.traverStringMap(headParam.entrySet());
System.out.println("解析消息头完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
//解析消息正文
private void parseRequestContext() {
try {
System.out.println("开始解析消息正文..");
System.out.println("解析消息正文完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
private String readLine() throws IOException {
InputStream in = socket.getInputStream();
StringBuilder builder = new StringBuilder();
int r1 = -1;
int r2 = 0;
while ((r1 = in.read()) != -1) {
if (r1 == 10 && r2 == 13) {
break;
}
builder.append((char) r1);
r2 = r1;
}
return builder.toString().trim();
}
public String getRequestMethod() {
return requestMethod;
}
public void setRequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}
public String getRequestURL() {
return requestURL;
}
public void setRequestURL(String requestURL) {
this.requestURL = requestURL;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getHeadParam(String paramkey) {
return headParam.get(paramkey);
}
public void setHeadParam(Map headParam) {
this.headParam = headParam;
}
public String getRequestParam(String key) {
return requestParam.get(key);
}
public String getRequestURI() {
return requestURI;
}
public void setRequestURI(String requestURI) {
this.requestURI = requestURI;
}
}
**
* 响应类,根据请求和业务发送的响应
*/
public class HttpResponse {
private Socket socket;
private OutputStream out;
//响应状态行
private String protocol = "HTTP/1.1";
private int statusCode = 200;
private String statusReason = "OK";
private File entity;
//响应头
private Map responseHeadParam = new HashMap<>();
public HttpResponse(Socket socket) {
this.socket = socket;
try {
out = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
public void flush() {
flushResponseStatus();
flushResponseHead();
flushResponseContext();
}
/**
* 响应状态行
*/
private void flushResponseStatus() {
try {
System.out.println("准备发送状态行..");
String statusLine = protocol + " " + statusCode + " " + statusReason;
System.out.println("状态行: " + statusLine);
out.write(statusLine.getBytes("ISO8859-1"));
out.write(13);//CR
out.write(10);//LF
System.out.println("发生状态行完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送响应头
*/
private void flushResponseHead() {
try {
System.out.println("准备发送响应头..");
Set> entries = responseHeadParam.entrySet();
for (Map.Entry e : entries) {
String line = e.getKey() + ": " + e.getValue();
out.write(line.getBytes("ISO8859-1"));
out.write(13);
out.write(10);
}
out.write(13);
out.write(10);
System.out.println("发送响应头完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 发送响应正文
*/
private void flushResponseContext() {
try {
System.out.println("准备发送响应正文...");
FileInputStream in = new FileInputStream(entity);
System.out.println("响应正文: " + entity.getName());
byte[] data = new byte[1024];
int len = -1;
while ((len = in.read(data)) != -1) {
out.write(data, 0, len);
}
System.out.println("发送响应正文完毕!");
} catch (Exception e) {
e.printStackTrace();
}
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(
int statusCode) {
this.statusCode = statusCode;
}
public String getStatusReason() {
return statusReason;
}
public void setStatusReason(String statusReason) {
this.statusReason = statusReason;
}
public File getEntity() {
return entity;
}
public void setEntity(File entity) {
this.entity = entity;
String ext = entity.getName().substring(entity.getName().lastIndexOf(".") + 1);
String type = HttpContext.getTypeMapping(ext);
responseHeadParam.put("Content-Type", type);
responseHeadParam.put("Content-Length", String.valueOf(entity.length()));
}
**
* 注册业务类
*/
public class HttpRegServlet extends HttpServlet {
/**
* 注册用户
*
* @param request
* @param response
*/
public void services(HttpRequest request, HttpResponse response) {
String username = request.getRequestParam("username");
String password = request.getRequestParam("password");
Integer age = Integer.parseInt(request.getRequestParam("age"));
String nicknane = request.getRequestParam("nickname");
File file = null;
if (username == null || password == null || nicknane == null || age == null) {
forward(request, response, "/myweb/reg_fail.html");
return;
}
try (RandomAccessFile raf = new RandomAccessFile("user.dat", "rw")) {
//判断用户是否存在
for (int i = 0; i < raf.length(); i += 100) {
raf.seek(i);
byte[] data = new byte[32];
raf.read(data);
String oldNames = new String(data, "UTF-8").trim();
if (oldNames.equals(username)) {
forward(request, response, "/myweb/reg_fail.html");
return;
}
}
//新增用户
raf.seek(raf.length());
byte[] data = username.getBytes("UTF-8");
data = Arrays.copyOf(data, 32);
raf.write(data);
data = password.getBytes("UTF-8");
data = Arrays.copyOf(data, 32);
raf.write(data);
data = nicknane.getBytes("UTF-8");
data = Arrays.copyOf(data, 32);
raf.write(data);
raf.writeInt(age);
forward(request, response, "/myweb/reg_success.html");
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 用户登录
*/
public class HttpLoginServlet extends HttpServlet {
public void services(HttpRequest request, HttpResponse response) {
String username = request.getRequestParam("username");
String password = request.getRequestParam("password");
File file = null;
if (username == null || password == null) {
forward(request, response, "/myweb/login_fail.html");
return;
}
try (RandomAccessFile raf = new RandomAccessFile("user.dat", "rw")) {
for (int i = 0; i < raf.length(); i += 100) {
raf.seek(i);
byte[] data = new byte[32];
raf.read(data);
String oldName = new String(data, "UTF-8").trim();
if (oldName.equals(username)) {
raf.seek(i + 32);
data = new byte[32];
raf.read(data);
String oldPassword = new String(data, "UTF-8").trim();
if (oldPassword.equals(password)) {
forward(request, response, "/myweb/login_success.html");
return;
}
}
}
forward(request, response, "/myweb/login_fail.html");
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* servlet的父类,用于统一servlet的基本功能
*/
public abstract class HttpServlet {
/**
* 业务请求
*
* @param request
* @param response
*/
public abstract void services(HttpRequest request, HttpResponse response);
/**
* 发送响应
*
* @param request
* @param response
* @param filePath
*/
public void forward(HttpRequest request, HttpResponse response, String filePath) {
File file = new File("./src/main/webapp" + filePath);
response.setEntity(file);
}
HttpContext:将tomcat中文件后缀名对应的网络请求中需要发送的Content-Type初始化对应
ServletContext:储存Servlet和请求路径的对应关于,通过反射来调用
自行定义了一个网络请求中的空请求异常EmptyRequestException,亦可自行设计其他异常
启动项目,在浏览器输入:http://localhost:8088/+“自己项目文件夹”(localhost也可以是本机ip,8088在webserver中自行定义,要注意端口号冲突)
此项目 登录:http://localhost:8088/myweb/login.html
此项目 注册: http://localhost:8088/myweb/reg.html (需要自行启动此项目才可访问)
点击下载源码(提取码:3ohr)