- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我尝试用 Java 实现一个简单的带有套接字的 HTTP 服务器。它的作用是从客户端浏览器接受文件名,在磁盘上打开该文件并在浏览器中打印出来。我当前的代码如下所示:
public class HTTPServer {
public String getFirstLine(Scanner s) {
String line = "";
if (s.hasNextLine()) {
line = s.nextLine();
}
if (line.startsWith("GET /")) {
return line;
}
return null;
}
public String getFilePath(String s) {
int beginIndex = s.indexOf("/");
int endIndex = s.indexOf(" ", beginIndex);
return s.substring(beginIndex + 1, endIndex);
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws IOException {
Socket clientSocket = null;
int serverPort = 7777; // the server port
try {
ServerSocket listenSocket = new ServerSocket(serverPort);
while (true) {
clientSocket = listenSocket.accept();
Scanner in;
PrintWriter out;
HTTPServer hs;
in = new Scanner(clientSocket.getInputStream());
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())));
hs = new HTTPServer();
// get the first line
String httpGetLine = hs.getFirstLine(in);
if (httpGetLine != null) {
// parse the file path
String filePath = hs.getFilePath(httpGetLine);
// open the file and read it
File f = new File(filePath);
if (f.exists()) {
// if the file exists, response to the client with its content
out.println("HTTP/1.1 200 OK\n\n");
out.flush();
BufferedReader br = new BufferedReader(new FileReader(filePath));
String fileLine;
while ((fileLine = br.readLine()) != null) {
out.println(fileLine);
out.flush();
}
} else {
out.println("HTTP/1.1 404 NotFound\n\nFile " + filePath + " not found.");
out.flush();
}
}
}
} catch (IOException e) {
System.out.println("IO Exception:" + e.getMessage());
} finally {
try {
if (clientSocket != null) {
clientSocket.close();
}
} catch (IOException e) {
// ignore exception on close
}
}
}
}
在 NetBeans 中运行它后,我打开浏览器并访问“localhost:7777/hello.html”(hello.html 是项目文件夹中的文件)。它只是显示页面正在加载。只有在 NetBeans 中停止服务器后,hello.html 的内容才会显示在浏览器中。
我希望我的服务器无限期地工作,响应一个又一个的 GET 请求并向客户端显示文件内容。我不确定代码的哪些部分应该放入 while(true)
循环中,哪些部分不应该放入。
最佳答案
即使对于简约的 HTTP 服务器来说,您的代码逻辑也非常不完整。您没有遵守 RFC 2616 管辖的基本规则.
您根本没有读取客户端的 HTTP 请求 header 。您只阅读第一行。 header 规定服务器需要如何运行、需要发送什么类型的响应、如何发送响应等。
您没有检查客户端的 HTTP 请求版本。不要向 HTTP 1.0 请求发送 HTTP 1.1 响应,但您可以向 HTTP 1.1 请求发送 HTTP 1.0 响应。
您没有检查客户端的 HTTP 请求是否有 Connection
header 。默认情况下,HTTP 1.0 连接不使用保持 Activity 状态,因此 HTTP 1.0 客户端必须通过发送 Connection: keep-alive
来显式请求保持 Activity 状态。 header 。 HTTP 1.1 连接默认使用保持 Activity 状态,因此 HTTP 1.1 客户端可以通过发送 Connection: close
显式禁用保持 Activity 状态。标题代替。如果 HTTP 1.0 请求不包含 Connection: keep-alive
header ,那么服务器必须假设 close
被要求。如果 HTTP 1.1 请求不包含 Connection: close
header ,那么服务器必须假设 keep-alive
被要求。无论哪种方式,您都应该发回您自己的 Connection
header 指示是否 keep-alive
或close
正在被您的服务器使用(如果 keep-alive
被请求,您不必遵守它,但如果可能的话您应该这样做)。以close
为例,发送响应后必须关闭套接字。
在您的 200
中响应,您没有发送 Content-Length
header (尽管 Transfer-Encoding: chunked
方法更适合您正在执行的发送类型,但前提是客户端发送了 HTTP 1.1 请求。有关更多详细信息,请参阅 RFC 2616 Section 3.6.1),因此您必须关闭套接字(并发送Connection: close
header )发送响应后,否则客户端无法知道何时到达 EOF。请参阅RFC 2616 Section 4.4了解更多详情。
您的代码一次只能服务一个客户端和一个请求。如果你希望你的服务器一次处理多个客户端,特别是如果你想支持 HTTP keep-alives(你应该这样做),那么你需要为每个连接的客户端创建一个工作线程,并且该线程可以服务很多客户端发送请求,直到断开连接。
尝试更像这样的东西(可能需要一些调整来编译、跟踪线程等):
public class HTTPClientThread extends Thread {
private Socket clientSocket;
public HTTPClientThread(Socket client) {
clientSocket = client;
}
public void run() {
Scanner in = new Scanner(clientSocket.getInputStream());
OutputStream out = clientSocket.getOutputStream();
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out)));
bool keepAlive = false;
do {
String requestLine = s.nextLine();
String line;
String connection = "";
keepAlive = false;
do {
line = s.nextLine();
if (line == "") {
break;
}
if (line.startsWith("Connection:") {
connection = line.substring(11).trim();
}
}
while (true);
int idx = requestLine.indexOf(" ");
if (idx == -1) {
pw.println("HTTP/1.0 400 Bad Request");
pw.println("Connection: close");
pw.println("");
pw.flush();
continue;
}
String httpMethod = line.substring(0, idx);
line = line.substring(idx+1);
idx = line.indexOf(" ");
if (idx == -1) {
pw.println("HTTP/1.0 400 Bad Request");
pw.println("Connection: close");
pw.println("");
pw.flush();
continue;
}
String httpVersion = line.subString(endIndex+1);
if (!httpVersion.equals("HTTP/1.0") && !httpVersion.equals("HTTP/1.1")) {
pw.println("HTTP/1.0 505 HTTP Version Not Supported");
pw.println("Connection: close");
pw.println("");
pw.flush();
continue;
}
if (connection != "") {
keepAlive = connection.equalsIgnoreCase("keep-alive");
}
else if (httpVersion.equals("HTTP/1.1")) {
keepAlive = true;
}
else {
keepAlive = false;
}
String filePath = line.substring(0, endIndex);
if (filePath.startsWith("/")) {
filePath = filePath.substring(1);
}
// open the file and read it
File f = new File(filePath);
if (!f.exists()) {
pw.println(httpVersion + " 404 Not Found");
pw.println("Content-Length: 0");
if (keepAlive) {
pw.println("Connection: keep-alive");
} else {
pw.println("Connection: close");
}
pw.println("");
pw.flush();
continue;
}
if (httpMethod != "GET") {
pw.println(httpVersion +" 405 Method Not Allowed");
pw.println("Allow: GET");
if (keepAlive) {
pw.println("Connection: keep-alive");
} else {
pw.println("Connection: close");
}
pw.println("");
pw.flush();
continue;
}
pw.println(httpVersion + " 200 OK");
pw.println("Content-Type: application/octet-stream");
pw.print("Content-Length: ");
pw.println(f.length());
if (keepAlive) {
pw.println("Connection: keep-alive");
} else {
pw.println("Connection: close");
}
pw.println("");
pw.flush();
FileInputStream fis = new FileInputStream(f);
BufferedOutputStream bw = new BufferedOutputStream(out);
byte[] buffer = new byte[1024];
int buflen;
while ((buflen = fis.read(buffer)) > 0) {
bw.write(buffer, 0, buflen);
bw.flush();
}
}
while (keepAlive);
clientSocket.close();
}
}
公共(public)类 HTTPServer { /** * @param args 命令行参数 */ 公共(public)静态无效主(字符串[] args)抛出IOException { 套接字 clientSocket = null; int 服务器端口 = 7777;//服务器端口 尝试 { ServerSocket ListenSocket = new ServerSocket(serverPort); 而(真){ clientSocket = ListenSocket.accept(); 新的 HTTPClientThread(clientSocket); } } catch (IOException e) { System.out.println("IO异常:"+ e.getMessage()); } } }
话虽如此,Java 有自己的 HTTP server class可用。
关于java - 使用 Socket 实现 HTTP 服务器 - 如何让它永远运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21615273/
有人可以解释预定义谓词forall如何在列表中找到最小值吗? 最佳答案 对于列表L,您可以使用: member(Min,L), forall(member(N,L), N>=Min). 但是,尽管这是
编辑:澄清一下,我正在搜索的对象数组确实已按搜索变量的字母数字顺序进行了预排序。 我做了一个二分搜索函数并将它嵌套在另一个函数中。出于某种原因,每次我使用二进制搜索都无法找到相关的字符数组。 基本上,
是否可以阻止用户(甚至是管理员)终止我的程序? 或者万一被杀死,它会迅速恢复自身? 更新:澄清一下:我正在编写一个监控程序,类似于家长控制,它记录用户对 PC 的操作。你可以通过查看我最近的其他问题来
我有一个 for 循环,我希望它永远递增。 我的代码: for a in (0...Float::INFINITY).step(2) puts a end 输出: 0.0 2.0 4.0 Et
我很困惑。我有一个运行Ubuntu 14.04的VM。我在这里遵循了以下程序:http://clang.llvm.org/docs/LibASTMatchersTutorial.html,现在正在运行
这是我的代码 #include #include #include #include #include #include #include #include #include usi
我有一个程序会或多或少地通过标准输入使用 COPY FROM 将大量数据复制到 Postgres 9 中。 这目前工作正常,但我正在缓冲数据 block ,然后分批运行 COPY FROM 操作。 我
我想我不小心在某个地方安装了 Foreverjs 并启动了它。每次我杀死这个进程时,另一个进程就会取代它的位置 ] 1 我不知道永远在哪里(或者这实际上是导致它的原因),因为我在本地安装了它。 最佳答
我得到了一个 forever: command not found 当我使用 forever 命令作为 cronjob 运行 nodejs 进程时出现错误(在亚马逊 ec2 机器中):我正在使用的 b
我创建了一些容器,它们还没有准备好使用,总是“重新启动”状态: docker ps CONTAINER ID IMAGE COMMAND
我试图永远重复一个 IO 操作,但是将一个执行的结果输入到下一个执行中。像这样的东西: -- poorly named iterateM :: Monad m => (a -> m a) -> a -
这里的代码样式问题。 我看着this问题,它询问.NET CLR是否真的总是初始化字段值。 (答案是肯定的。)但令我感到惊讶的是,我不确定执行此操作始终是个好主意。我的想法是,如果我看到这样的声明:
美好的一天,我对永久启动\停止脚本有一些问题。 中央操作系统 6.2 内核 2.6.32-220.el6.x86_64 node.js v0.6.19 npm v 1.1.24 永远@0.9.2 我创
我在让管道与 paramiko 一起工作时遇到问题。 这个有效: ssh = paramiko.SSHClient() [...] stdin, stdout, stderr = ssh.exec_c
我希望守护我的 Node.js 应用程序。 Upstart 和永远有什么区别?另外,还有其他我可能想要考虑的软件包吗? 最佳答案 正如评论中指出的,upstart将用于启动 forever脚本,因为
我有以下查询,其中包含在 5 秒内返回数据的选择查询。但是当我在前面添加创建物化 View 命令时,查询需要创建物化 View 。 最佳答案 当您创建物化 View 时,实际上是创建了 Oracle
当我今天访问我的项目的 Google Cloud 控制台并单击“计算引擎”或“云存储”时,它只会永远显示“正在加载”。几天前,我能够看到我的虚拟机和存储桶。有没有办法让控制台再次工作? 谢谢, 麦克风
我编写了一个函数,它当前显示 1000 以下的所有质数。 我可以继续增大 1000 以生成更多数字,但我不知道如何让它在运行后一直持续下去。 func generatePrimes() { l
这是由 another question 触发的. 具体来说,我有一个进程中的 COM 类,它在 CLSID registry 中定义。因为有 ThreadingModel of Both . 我们的
我正在试用新的 React Hooks的 useEffect API,它似乎永远在无限循环中运行!我只希望 useEffect 中的回调运行一次。这是我的引用代码: 单击“运行代码片段”以查看“运行
我是一名优秀的程序员,十分优秀!