[原创]纯java实现http web服务器

本文介绍了如何使用Java从头实现一个简单的HTTP Web服务器。通过分析HTTP请求和响应的格式,作者展示了如何处理请求状态码、文件类型检测及配置文件处理等功能。文中给出了核心代码片段,包括ServerThread、Html、INIFileProcess、UserUA、UniversalInterface和DefaultERRReturn等关键类。
摘要由CSDN通过智能技术生成


先来看一下http协议先:

HTTP请求:下面是我抓包抓来的http请求数据,先来看一下:

GET / HTTP/1.1    //  这句是说浏览器以GET方法请求根目录下的默认页面的数据(通常文件名为index.*) ,以版本1.1的HTTP协议请求。
Host: 127.0.0.1   // 这句是请求的服务器ip。
Connection: keep-alive  请求连接(tcp连接)一直保持,不要断开,以方便下次请求其他文件。
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8  //请求的文件类型。
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 BIDUBrowser/8.5 Safari/537.36 //客户端的UA类型。通常我们分析用户是由什么终端访问的就是从这一行分析的
Accept-Encoding: gzip, deflate 接受的编码类型。有deflate(未压缩)和gzip(gzip压缩)
Accept-Language: zh-CN,zh;q=0.8 //告诉服务器客户端接受中文编码。
DNT: 1 // 禁止跟踪


当然如果是下载软件发出的请求还可能有Range字段以指示断点续传。

HTTP回应:每一行都由\r\n(我是在win下测试的)结尾。(除了最后一行由\r\n\r\n结束以外,最后一行结束直接到正文)。

回应第一行:HTTP状态码,HTTP/1.1 xxx xxxxxx的格式,第一个xxx是状态吗,如200(请求成功),301(重定向)等。第二个xxxxxx是对这个状态吗的解释,如200对应的是OK,301对应的是moved Permanently.在这里给个200 OK的例子:HTTP/1.1 200 OK \r\n.基本上只要是没多大问题就返回200 OK。

Server: Sunlight/0.1(\r\n)://标识服务器软件的名称,以及版本。比如我写的这个小程序我给他起了个名字叫sunlight。后面的"\0.1"表示我的服务器软件版本为1.1。
Date:(格式化字符串)         //当前时间,我用一下代码得到格式化时间字符串:

SimpleDateFormat sdf = new SimpleDateFormat("EEE,d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
Calendar cd = Calendar.getInstance();
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
String str = sdf.format(cd.getTime());


就得到了格式化时间字符串了。

Content-Length: 1251      //这个是服务器返回的正文长度。
Content-Type: //这个是指示服务器回应正文类型,html网页当然就是text/html。



好了不多说,上代码:(我弄了一个sunlight.ini文件做配置存储,这个文件不存在的话会自动创建)

ServerMainThread.java:
package com.sunlight;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;


public class ServerMainThread extends Thread {
public void run() {
try {
INIFileProcess.ReadINI("sunlight.ini");
} catch (FileNotFoundException e) {
Settings.addSetting("Port", "1254");
Settings.addSetting("WWWRoot", "/home/zhuge/wwwroot");
Settings.addSetting("404PagePATH", "[Default]");
Settings.addSetting("DefaultIndexFileName", "index.htm;index.html");
INIFileProcess.SaveINI("sunlight.ini");
} catch (IOException e) {
e.printStackTrace();
}

int port = 5280;
if (Settings.getSetting("Port") != null) {
try{
port = Integer.parseInt(Settings.getSetting("Port"));
}catch(Exception e){
Settings.ChangeSetting("Port", "80");
port=5280;
INIFileProcess.SaveINI("sunlight.ini");
}
}else{
Settings.addSetting("Port", "80");
INIFileProcess.SaveINI("sunlight.ini");
}
if(port<1 || port >65535){
port=5280;
Settings.ChangeSetting("Port", "80");
INIFileProcess.SaveINI("sunlight.ini");
}
File file=new File(Settings.getSetting("WWWRoot"));
if(!file.exists() ||!file.isDirectory()){
try{
file.mkdir();
}catch(Exception e){
e.printStackTrace();
System.out.println("ERROR!");
System.exit(-1);
}
}
ServerSocket serverSocket;
Socket socket;
UniversalInterface ui;
try {

serverSocket=new ServerSocket(7);
while(true){
socket=serverSocket.accept();
System.out.println("Have a new connection!");
ui=new UniversalInterface(UserUA.Unknown);
new ServerThread(socket,ui).start();
}
}catch (BindException e){
System.err.println("Error:Address and port already used!");
System.exit(-1);
}
catch (IOException e) {
e.printStackTrace();
}
}
}


ServerThread.java:

package com.sunlight;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;


import com.sunlight.ConnectionListener.ConnectionListenerRegister;
import com.sunlight.html.Html;


public class ServerThread extends Thread {
Socket socket;
BufferedReader br;
OutputStreamWriter pw;
String t[];
StringBuffer temp;
UniversalInterface uInterface;
long down_start = 0;
long down_end = 0;
long down_size = 0;
public boolean failed = false;
protected ArrayList<String> GET_NAMES = new ArrayList<String>();
protected ArrayList<String> GET_VALUES = new ArrayList<String>();
protected ArrayList<String> POST_NAMES = new ArrayList<String>();
protected ArrayList<String> POST_VALUES = new ArrayList<String>();


public ServerThread(Socket socket, UniversalInterface ui) {
this.socket = socket;
this.uInterface = ui;
}


@Override
public void run() {
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
temp = new StringBuffer();
temp.append(br.readLine());
t = temp.toString().split("HTTP/"); // t[0]:GET/s.aaa t[1]:1.1
String t2[];
if (!t[1].equals("1.1")) {
pw.write(DefaultERRReturn.ERR400());
pw.flush();
pw.close();
br.close();
failed = true;
}
if (t[0].indexOf('/') == -1) {
pw.write(DefaultERRReturn.ERR400());
pw.flush();
pw.close();
br.close();
failed = true;
} else {
t2 = t[0].split("/"); // t2[0]: http function t2[1]:page path
if (t2.equals("HEAD")) {
pw.write("HTTP/1.1 204 OK \n");
pw.flush();
pw.close();
br.close();
failed = true;
}
if (t2.equals("OPTIONS") || t2.equals("PUT") || t2.equals("DELETE") || t2.equals("TRACE")) {
                                  //这些方法暂时不支持
pw.write("HTTP/1.1 405 \n");
pw.flush();
pw.close();
br.close();
failed = true;
}
}
} catch (IOException e) {
e.printStackTrace();
}
if (!failed) {
temp.append('\n');
try {
while (br.ready()) {
temp.append(br.readLine());
temp.append('\n');
}
temp.deleteCharAt(temp.length() - 1);
} catch (IOException e) {
e.printStackTrace();
}
String s[] = temp.toString().split("\n");
StringBuffer askpath = new StringBuffer(
s[0].substring(s[0].indexOf('/'), s[0].indexOf("HTTP/1.", s[0].indexOf('/') + 1)));
if (askpath.charAt(askpath.length() - 1) == ' ')
askpath.deleteCharAt(askpath.length() - 1);


for (int i = 0; i <= s.length - 1; i++) {
if (s[i].indexOf("User-Agent") != -1) {
if (s[i].indexOf("Windows") != -1)
uInterface.setUA(UserUA.Windows);
else if (s[i].indexOf("Android") != -1)
uInterface.setUA(UserUA.Android);
else if (s[i].indexOf("Linux") != -1)
uInterface.setUA(UserUA.Linux);
else if (s[i].indexOf("iPad") != -1)
uInterface.setUA(UserUA.iPad);
else if (s[i].indexOf("iPhone") != -1)
uInterface.setUA(UserUA.iPhone);
}
if (i == s.length - 1) {
// System.out.println(s[i]);
if(s[0].startsWith("POST")){
if (s[i].indexOf("&&") != -1) {
String ss[] = s[i].split("&&");
for (int z = 0; z <= ss.length - 1; z++) {
if (ss[z].equals("=")) {
POST_NAMES.add(null);
POST_VALUES.add(null);
continue;
}
if (ss[z].indexOf("=") != ss[z].length() - 1) {
String sss[] = ss[z].split("=");
if (sss[0].equals("")) {
POST_NAMES.add(null);
} else {
POST_NAMES.add(sss[0]);
}
POST_VALUES.add(sss[1]);
} else {
System.out.println(s[i].indexOf("&&"));
if (ss[z].substring(0, ss[z].length() - 2).equals("")) {
POST_NAMES.add(null);
} else {
POST_NAMES.add(ss[z].substring(0, ss[z].length() - 2));
}
POST_VALUES.add(null);
}
}
} else if (s[i].indexOf("&") != -1) {
String ss[] = s[i].split("&");
for (int z = 0; z <= ss.length - 1; z++) {
if (ss[z].equals("=")) {
POST_NAMES.add(null);
POST_VALUES.add(null);
continue;
}
if (ss[z].indexOf("=") != ss[z].length() - 1) {
String sss[] = ss[z].split("=");
if (sss[0].equals("")) {
POST_NAMES.add(null);
} else {
POST_NAMES.add(sss[0]);
}
POST_VALUES.add(sss[1]);
} else {
if (ss[z].substring(0, ss[z].length() - 2).equals("")) {
POST_NAMES.add(null);
} else {
POST_NAMES.add(ss[z].substring(0, ss[z].length() - 2));
}
POST_VALUES.add(null);
}
}
} else {
if (s[i].equals("=")) {
POST_NAMES.add(null);
POST_VALUES.add(null);
} else {
if (s[i].indexOf("=") != s[i].length() - 1) {
String sss[] = s[i].split("=");
if (sss[0].equals("")) {
POST_NAMES.add(null);
} else {
POST_NAMES.add(sss[0]);
}
GET_VALUES.add(sss[1]);
} else {
if (s[i].substring(0, s[i].length() - 2).equals("")) {
POST_NAMES.add(null);
} else {
POST_NAMES.add(s[i].substring(0, s[i].length() - 2));
}
POST_VALUES.add(null);
}
}
}
}
}
}
String DefualtIndex = null;
if (!failed) {
if (Settings.getSetting("DefaultIndexFileName").indexOf(';') != -1) {
String temp[] = Settings.getSetting("DefaultIndexFileName").split(";");
for (int i = 0; i <= temp.length - 1; i++) {
if (new File(Settings.getSetting("WWWRoot") + "\\" + temp[i]).exists()
&& new File(Settings.getSetting("WWWRoot") + "\\" + temp[i]).isFile()) {
DefualtIndex = Settings.getSetting("WWWRoot") + "\\" + temp[i];
break;
}
}
}
} else {
DefualtIndex = Settings.getSetting("DefaultIndexFileName");
if ((!failed) && (!(new File(Settings.getSetting("WWWRoot") + "\\" + DefualtIndex).exists()))) {
try {
pw.write(D
回答: 根据提供的代码,这段代码是用Python编写的。它的作用是获取指定目录下的文件列表并打印出来。代码中使用了os模块的popen函数来执行系统命令,通过调用dir命令来获取目录下的文件列表。\[1\]在代码中,通过定义getDirList函数来实现获取文件列表的功能。在主程序中,通过调用getDirList函数并传入目录路径参数来获取指定目录下的文件列表。\[1\]同时,代码中使用了time模块的sleep函数来暂停程序的执行,以便在获取完文件列表后等待一秒钟再继续执行后续代码。\[1\]根据提供的代码,可以看出在第一个例子中,目录路径是"D:\2008文档\★ 123",而在第二个例子中,目录路径是"D:\2008文档\★中文"。\[1\]根据代码验证的结果,可以看出第一个例子中的目录路径被加入了一对英文双引号,而第二个例子中的目录路径没有被加入引号。\[3\]所以,根据提供的代码,第一个例子中的目录路径是"D:\2008文档\★ 123",第二个例子中的目录路径是"D:\2008文档\★中文"。 #### 引用[.reference_title] - *1* *2* *3* [解决:Python的os.popen()在read()时,出现“�Ҳ����ļ�乱码及结果错误”的问题](https://blog.csdn.net/aaaaa_ascii/article/details/129031416)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值