我正在为简单的Http请求和响应制作HTTP服务器和HTTP Web客户端。
这是服务器的代码
import java.io.*;
import java.net.*;
import java.util.*;
public final class WebServer{
public static void main(String[] args) throws Exception{
//storing port number
int port = 2048;
//open socket and wait for TCP connection
ServerSocket serverConnect = new ServerSocket(port);
System.out.println("Server started.\nListening for connections on port : " + port + " ...\n");
// we listen until user halts server execution
while (true) {
//Construct an object to process the HTTP request message.
//This will call another class where we do everything else
HttpRequest request = new HttpRequest(serverConnect.accept());
//create a new thread to process the request
Thread thread = new Thread(request);
thread.start();
} //end of while
}//end of main
}//end of the class webServer
HttpRequest类的代码如下:
import java.io.*;
import java.net.*;
import java.util.*;
final class HttpRequest implements Runnable{
final static String CRLF = "\r\n";
Socket socket;
//start of constructor
public HttpRequest(Socket socket) throws Exception{
this.socket=socket;
}//end of constructor
//Implement the run() method of the Runnable interface.
public void run(){
try{
processRequest();
}
catch(Exception e){
System.out.println(e);
}
}//end of run
private void processRequest() throws Exception{
//Get a reference to the scoket's input and output streams.
InputStream is = socket.getInputStream();
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
//set up the stream filters
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//Get the request line of the HTTP request message.
String requestLine = br.readLine();
//Display the request line
System.out.println();
System.out.println(requestLine);
//Get and display the header lines.
String headerLine = null;
while((headerLine = br.readLine()).length()!=0){
System.out.println(headerLine);
}
//System.out.println(requestLine);
//Extract the filename from the request line.
StringTokenizer tokens = new StringTokenizer(requestLine);
tokens.nextToken(); //skip over the method, which should be. "GET"
String fileName = tokens.nextToken();
//Prepend a "." so that file request is within the current directory
fileName = "." + fileName;
//printing for test
//System.out.println(fileName);
//Open the requested file
FileInputStream fis = null;
boolean fileExists = true;
try{
fis = new FileInputStream(fileName);
}
catch(FileNotFoundException e){
fileExists = false;
}
//Construct the response message
String statusLine = null;
String contentTypeLine = null;
String entityBody = null;
if(fileExists){
statusLine = tokens.nextToken();
contentTypeLine = "Content-type: " + contentType(fileName) + CRLF;
}
else{
statusLine = "HTTP/1.1 404 File Not Found";
contentTypeLine = "Content-type: " + "text/html" + CRLF;
entityBody = "<html><head><title>Not Found </title></head>" +
"<BODY>Not Found</body></html>";
}
//send the status line
os.writeBytes(statusLine);
//send the content Type
os.writeBytes(contentTypeLine);
//send a blank line to indicate the end of the header lines
os.writeBytes(CRLF);
//send the entity Body
if(fileExists){
sendBytes(fis, os);
fis.close();
}
else{
os.writeBytes(entityBody);
os.writeBytes(CRLF);
}
//Close scokets and streams.
fis.close();
os.close();
br.close();
socket.close();
}//end of processRequest
private static String contentType(String fileName){
if(fileName.endsWith(".htm") || fileName.endsWith(".html")){
return "text/html";
}
if(fileName.endsWith(".gif")){
return "image/gif";
}
if(fileName.endsWith(".jpeg") || fileName.endsWith(".jpg")){
return "image/jpeg";
}
return "application/octet-stream";
}// end of contentType
private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception{
//Construct a 1k buffer to hold bytes on their way to the Socket
byte[] buffer = new byte[1024];
int bytes = 0;
//Copy requested file into the scoket's output stream.
while((bytes = fis.read(buffer)) != -1){
os.write(buffer, 0, bytes);
}//end of while
}//end of sendBytes
} // end of the class
当我从Chrome浏览器发出请求时,该代码运行正常。但是,我也制作了WebClient。当我从WebClient发出请求时,由于程序永远运行,我陷入了困境。
据我所知,指针并没有从服务器端while循环中的br.readline移动。
我的客户的代码如下。
import java.io.*;
import java.net.*;
import java.util.*;
public class WebClient{
final static String CRLF = "\r\n";
public static void main(String [] args) {
String serverName = args[0];
int port = Integer.parseInt(args[1]);
try {
// System.out.println("Connecting to " + serverName + " on port " + port);
Socket client = new Socket(serverName, port);
System.out.println("Just connected to " + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("GET /" +args[2] +" HTTP/1.1");
out.writeUTF(CRLF);
out.writeUTF("Host: "+client.getLocalSocketAddress());
out.writeUTF(CRLF);
out.writeUTF("Connection: close" + CRLF);
out.writeUTF("User-agent: close" + CRLF);
out.writeUTF(CRLF);
//Cache-Control: max-age=0
System.out.println("Just connected to 1 ");
InputStream inFromServer = client.getInputStream();
System.out.println("Just connected to 2 ");
BufferedReader br = new BufferedReader(new InputStreamReader(inFromServer));
System.out.println("Just connected to 3 ");
String headerLine = null;
while((headerLine = br.readLine()).length()!=0){
System.out.println("asd"+headerLine);
}
System.out.println("Just connected to 4 ");
client.close();
System.out.println("Just connected to 5 ");
} catch (IOException e) {
e.printStackTrace();
}
}
}//end of the class WebClient
谁能帮我解决问题。
谢谢。
答案 0 :(得分:1)
首先,您必须删除fis.close();
类中的行os.close();
(在HttpRequest
之前):如果不存在文件,则此行将引发NullPointerException
,因为{ {1}}为空,因此在向浏览器发送fis
响应后,您的服务器不会关闭该浏览器接受的套接字,这就是即使您在浏览器中看到Not Found
的原因,您的请求也永远不会消失的原因。结束。
第二,客户端卡住的原因是用于发送请求标头的Not Found
方法。似乎该行writeUTF()
并没有真正发送空字符串,而是添加了一些其他与UTF相关的字符(您可能会注意到在服务器的控制台输出中),因此服务器陷入了out.writeUTF(CRLF);
等待客户端发送一个空字符串,但永远不会收到它。您需要将while((headerLine = br.readLine()).length()!=0)
替换为out.writeUTF(CRLF);
。
此外,使用out.writeBytes(CRLF);
从套接字接收二进制文件几乎没有意义。 BufferedReader
通常与字符输入流一起使用,因此不适用于您的情况。您可以使用Reader
来代替此片段:
InputStream
(我选择的缓冲区大小为4096,可以将其替换为您的首选值):
String headerLine = null;
while((headerLine = br.readLine()).length()!=0){
System.out.println("asd"+headerLine);
}
注意:您可能会在这里容易注意到 int readBytes;
byte[] cbuf = new byte[4096];
while((readBytes=inFromServer.read(cbuf, 0, 4096))>-1){
System.out.println("read: " + readBytes);
}
不仅将获取文件本身,还将获取InputStream.read()
,statusLine
和两个contentTypeLine
,因此如果您想要将它们与文件分开,可以先发出两个“ readLines”,然后再通过CRLF
答案 1 :(得分:1)
在您的服务器中,您使用writeBytes()
将字符串作为字节序列写到基础输出流中。字符串中的每个字符通过丢弃高八位来依次写出。如果未引发异常,则写入的计数器将增加s的长度。
尽管您可能担心非ASCII文本,但通常这就是您所需要的。
在您的客户中,您尝试使用writeUTF()
首先,将两个字节写入输出流,就像通过writeShort方法给出要跟随的字节数一样。该值是实际写入的字节数,而不是字符串的长度。按照长度 ,使用修改后的字符的UTF-8编码,依次输出字符串的每个字符。如果未引发异常,则写入的计数器将增加写入输出流的字节总数。这将是至少两个加上str的长度,最多两个加上三倍的str的长度。
尽管在其他情况下,开头的2字节长很有用,但这并不是Web服务器所期望的,包括您的服务器(也是正确的)。因此,在客户的任何地方都使用writeBytes()
,它会突然起作用:
out.writeBytes("GET /" +args[2] +" HTTP/1.1");
out.writeBytes(CRLF);
out.writeBytes("Host: "+client.getLocalSocketAddress());
out.writeBytes(CRLF);
out.writeBytes("Connection: close" + CRLF);
out.writeBytes("User-agent: close" + CRLF);
out.writeBytes(CRLF);
(第一个请求是由writeUTF
发出的,第二个请求是来自Chrome的)