简体   繁体   English

BufferReader卡在readline()中

[英]BufferReader stuck in readline()

I am making an HTTP server and HTTP web client for simple Http request and response. 我正在为简单的Http请求和响应制作HTTP服务器和HTTP Web客户端。

This is the code for Server 这是服务器的代码

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

The code for HttpRequest class is as follow: 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

The Code works fine when I make a request from Chrome webbrowser. 当我从Chrome浏览器发出请求时,该代码运行正常。 However, I made WebClient as well. 但是,我也制作了WebClient。 When I make request from WebClient, I am stuck as the program runs forever. 当我从WebClient发出请求时,由于程序永远运行,我陷入了困境。

As far I have tracked, the pointer does not move from the br.readline on the while loops on the Server Side. 据我所跟踪,指针并没有从服务器端的while循环中的br.readline移动。

The code for my client is as follow. 我的客户的代码如下。

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

Can anyone help me figure out the problem. 谁能帮我解决问题。

Thanks. 谢谢。

First of all, you have to remove line fis.close(); 首先,您必须删除fis.close(); (right before os.close(); ) in your HttpRequest class: if no file exists, this line raises NullPointerException because fis is null, so after sending Not Found response to the browser, your server does not close the socket accepted from that browser, that's why even though you see Not Found in your browser, your request never ends. (右前os.close(); )在你HttpRequest类:如果没有文件存在,此行引发NullPointerException ,因为fis为空,那么发送后Not Found浏览器的响应,您的服务器不关闭从浏览器接受插座,这就是为什么即使您在浏览器中看到“ Not Found ,您的请求也永远不会结束的原因。

Secondly, the reason of why your client gets stuck is writeUTF() method that you used for sending request header. 其次,您的客户端卡住的原因是用于发送请求标头的writeUTF()方法。 Seems that this line out.writeUTF(CRLF); 似乎这一行out.writeUTF(CRLF); does not really send an empty string but adds some other UTF-related character(s) (you may notice that in your server's console output), so your server gets stuck at while((headerLine = br.readLine()).length()!=0) waiting for the client to send an empty string, but never receives it. 并不会真正发送空字符串,而是会添加其他一些与UTF相关的字符(您可能会注意到在服务器的控制台输出中),因此服务器会卡在while((headerLine = br.readLine()).length()!=0)等待客户端发送空字符串,但从未收到。 You need to replace out.writeUTF(CRLF); 您需要替换out.writeUTF(CRLF); with out.writeBytes(CRLF); out.writeBytes(CRLF); .

Also, it makes little sense to use BufferedReader for receiving binary files from socket. 同样,使用BufferedReader从套接字接收二进制文件也没有意义。 Reader in general is used with character-input stream, so it is not applicable for your case. 通常, Reader用于字符输入流,因此不适用于您的情况。 You may use InputStream instead, by replacing this fragment: 您可以通过替换以下片段来使用InputStream

String headerLine = null;
     while((headerLine = br.readLine()).length()!=0){
            System.out.println("asd"+headerLine);
        }

with this (I chose buffer size of 4096, you may replace it with your preferred value): 这样做(我选择的缓冲区大小为4096,可以将其替换为您的首选值):

 int readBytes;
 byte[] cbuf = new byte[4096];
 while((readBytes=inFromServer.read(cbuf, 0, 4096))>-1){
        System.out.println("read: " + readBytes);
    }

Note: You may easily notice here that InputStream.read() will fetch not only the file itself but also statusLine , contentTypeLine and two CRLF s, so in case if you would like to separate them from the file, you may read them first, by issuing two "readLines" and then fetch the file only by read() 注意:在这里您可能会很容易注意到InputStream.read()不仅将获取文件本身,还将statusLinecontentTypeLine和两个CRLF ,因此,如果您希望将它们与文件分开,则可以先读取它们,通过发出两个“ readLines”,然后仅通过read()获取文件

In your server, you use writeBytes() 在您的服务器中,使用writeBytes()

Writes out the string to the underlying output stream as a sequence of bytes. 将字符串作为字节序列写出到基础输出流中。 Each character in the string is written out, in sequence, by discarding its high eight bits. 字符串中的每个字符通过丢弃高八位来依次写出。 If no exception is thrown, the counter written is incremented by the length of s. 如果未引发异常,则写入的计数器将增加s的长度。

While you may worry about non-ASCII text, generally this is what you need. 尽管您可能担心非ASCII文本,但是通常这是您所需要的。

In your client you attempt to use writeUTF() 在您的客户端中,您尝试使用writeUTF()

First, two bytes are written to the output stream as if by the writeShort method giving the number of bytes to follow. 首先,将两个字节写入输出流,就像通过writeShort方法给出要跟随的字节数一样。 This value is the number of bytes actually written out, not the length of the string. 该值是实际写入的字节数,而不是字符串的长度。 Following the length , each character of the string is output, in sequence, using the modified UTF-8 encoding for the character. 按照该长度 ,使用修改后的字符的UTF-8编码,依次输出字符串的每个字符。 If no exception is thrown, the counter written is incremented by the total number of bytes written to the output stream. 如果未引发异常,则写入的计数器将增加写入输出流的字节总数。 This will be at least two plus the length of str, and at most two plus thrice the length of str. 这将是至少2加str的长度,最多2加3的str的长度。

While that 2-byte length in the beginning can be useful in other cases, it is not what web servers expect, including yours (and that is correct). 尽管开头的2字节长度在其他情况下很有用,但这不是Web服务器所期望的,包括您的服务器(这是正确的)。 So use writeBytes() everywhere in your client, and it will suddenly work: 因此,在客户端的任何地方都使用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);


In fact those extra bytes may be visible in your server output, at least when I ran it in Eclipse, I saw garbage characters, as a combination of mysterious empty space and a tiny question mark in a rectangle (note how they also appear at the end of the lines when CRLF is sent separately): 实际上,这些多余的字节可能会在您的服务器输出中可见,至少当我在Eclipse中运行时,我看到了垃圾字符,它们是神秘的空白空间和矩形中问号的组合(请注意,它们也如何显示在分别发送CRLF时的行末):

在此处输入图片说明

(The first request is the one issued with writeUTF , and the second one comes from Chrome) (第一个请求是使用writeUTF发出的请求,第二个请求是来自Chrome的请求)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM