BufferReader卡在readline()

时间:2018-10-30 00:56:40

标签: java http

我正在为简单的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

谁能帮我解决问题。

谢谢。

2 个答案:

答案 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);


实际上,这些多余的字节可能会在您的服务器输出中可见,至少当我在Eclipse中运行时,我看到了垃圾字符,它们是神秘的空白空间和矩形中问号的组合(请注意,它们也如何显示在分别发送CRLF时的行末):

enter image description here

(第一个请求是由writeUTF发出的,第二个请求是来自Chrome的)