0

我试图创建一个基本的符合 HTTP/1.1 的 Web 服务器,它支持具有持久连接的简单 GET 请求。我得到一个 SocketException: Connection Reset 错误发生在第 61 行(if (line==null || line.equals(""))。我正在通过运行它然后将我的 chrome 浏览器定向到 localhost 端口号来测试它。当我使用具有多个图像的页面对其进行测试时,似乎在发生异常之前只处理了一个请求,但我不确定出了什么问题,因为这是我第一次尝试任何类型的套接字编程。

这是删除 DataOutputStream 后我更新的代码:

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.StringTokenizer;

public class webserve
{
    public static void main(String[] args) throws Exception
    {
        String rootPath = "~/Documents/MockWebServerDocument/";
        int port = 10000;

        if(rootPath.startsWith("~" + File.separator))
        {
            rootPath = System.getProperty("user.home") + rootPath.substring(1);
        }

        String requestLine="";
        StringTokenizer tokens=null;
        String line, command;
        Date date = new Date();
        String connectionStatus="";


        //Create new server socket listening on specified port number
        ServerSocket serverSocket = new ServerSocket(port);

        while(true)
        {
            //Wait for a client to connect and make a request
            Socket connectionSocket = serverSocket.accept();
            System.out.println("Socket opened");

            //Input stream from client socket
            BufferedReader incomingFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
            //PrintWriter to send header to client socket
            PrintWriter outgoingHeader = new PrintWriter(connectionSocket.getOutputStream(),true);
            //OutputStream to send file data to client socket
            ObjectOutputStream outgoingFile = new ObjectOutputStream(connectionSocket.getOutputStream());
            //Date format for HTTP Header
            SimpleDateFormat HTTPDateFormat = new SimpleDateFormat("EEE MMM d hh:mm:ss zzz yyyy");


            //Create a HashMap to store the request header information
            HashMap<String,String> requestHeader = new HashMap<String,String>();

            while(connectionSocket.isConnected())
            {
                //requestHeader.clear();

                while((line = incomingFromClient.readLine()) != null)
                {
                    if(line.isEmpty())
                    {   
                        break;
                    }
                    //If this is the first line of the request, i.e doesnt contain a colon
                    if(!(line.contains(":")))
                    {
                        requestLine = line;
                        requestHeader.put("Request", requestLine);
                    }
                    else
                    {
                        //Otherwise, find the colon in the line and create a key/value pair for the HashMap
                        int index = line.indexOf(':')+2;
                        String header = line.substring(0,index-1);
                        line = line.substring(index).trim();

                        requestHeader.put(header, line);

                        System.out.println(header + " " + line);
                    }
                }   

                connectionStatus = (String)requestHeader.get("Connection:");
                requestLine = (String)requestHeader.get("Request");

                System.out.println("RequestLine: " + requestLine);

                if(!requestLine.equals("")||!(requestLine.equals(null)))
                {
                    tokens = new StringTokenizer(requestLine);

                    command = tokens.nextToken();
                    String filename = tokens.nextToken();
                    filename = cleanUpFilename(filename);
                    String fullFilepath = rootPath + filename;
                    System.out.println("Full FilePath: " + fullFilepath);

                    File file = new File(fullFilepath);

                    //Get the number of bytes in the file
                    int numOfBytes=(int)file.length();

                    //Open a file input stream using the full file pathname
                    FileInputStream inFile = new FileInputStream(fullFilepath);

                    //Create byte array to hold file contents
                    byte[] fileInBytes = new byte[numOfBytes];

                    inFile.read(fileInBytes,0,numOfBytes);

                    inFile.close();


                    //Write the header to the output stream 
                    outgoingHeader.print("HTTP/1.1 200 OK\r\n");
                    outgoingHeader.print("Date: " + HTTPDateFormat.format(date)+"\r\n");
                    outgoingHeader.print("Server: BC-Server\r\n");
                    outgoingHeader.print("Last-Modified: " + HTTPDateFormat.format(file.lastModified())+"\r\n");
                    outgoingHeader.print("Connection: keep-alive\r\n");
                    outgoingHeader.print("Content-Length: " + numOfBytes);
                    outgoingHeader.print("\r\n\r\n");               

                    //When the header has been printed, write the byte array containing the file
                    //to the output stream
                    outgoingFile.writeObject(fileInBytes);

                    if(!(connectionStatus.equals("keep-alive")))
                    {
                        System.out.println("Closing: " + connectionStatus);
                        outgoingHeader.close();
                        outgoingFile.close();
                        break;
                    }
                    else
                        continue;

                }       

            }

        }
    }

    public static String cleanUpFilename(String filename)
    {
        //If there is a "/" at the start of the filename, then remove it
        if(filename.charAt(0) == '/')
        {
            filename = filename.substring(1);
        }

        //If we are given an absolute URI request, strip all characters
        //before the third "/"
        if(filename.startsWith("http://"));
        {
            try
            {
                URI httpAddress = new URI(filename);

                //Get the path from the supplied absolute URI, that is remove
                //all character before the third "/"
                filename = httpAddress.getPath();

                //Again, we may have to trim this modified address if there is an
                //extra "/" at the start of the filename
                if(filename.charAt(0) == '/')
                {
                    filename = filename.substring(1);
                }
            }
            catch (URISyntaxException e)
            {                   
                e.printStackTrace();
            }
        }

        return filename;
    }

}

这是我的错误跟踪:

Exception in thread "main" java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:185)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:282)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:324)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:176)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:153)
    at java.io.BufferedReader.readLine(BufferedReader.java:316)
    at java.io.BufferedReader.readLine(BufferedReader.java:379)
    at webserve.main(webserve.java:61)

任何帮助将不胜感激,因为我完全不知所措。

4

4 回答 4

0

尝试使用 telnet、wget 或 curl 而不是 chrome 来测试连接,因为这样您就可以控制 TCP/IP 连接的两端。

我认为您的网络客户端正在从它的一侧关闭连接,并且您尝试再次从该套接字读取(是的,isConnected()当远程方关闭连接时甚至会抛出此错误)。我也很遗憾地说,除了捕获异常并优雅地处理它之外,没有简单的方法来解决这个问题。

这是同步套接字经常发生的问题。尝试改用java.nio通道和选择器。

于 2013-02-26T23:09:10.660 回答
0

您的服务器最明显的问题是它不是多线程的。重新阅读您对问题的描述后,这似乎是根本原因。每个连接需要一个线程。之后serverSocket.accept(),创建一个新线程来处理connectionSocket。

    while(true)
    {
        //Wait for a client to connect and make a request
        Socket connectionSocket = serverSocket.accept();

        new Thread()
        {
            public void run()
            {
                //Input stream from client socket
                BufferedReader incomingFromClient = ...

                etc

            }
        }.start();
于 2013-02-27T01:38:32.823 回答
0

同时使用多个输出流是很成问题的。在这种情况下,您不应该创建,ObjectOutputStream直到您确定要编写一个对象并且您已经写入并刷新了标头,因为ObjectOutputStream将标头写入输出,在您当前的代码中,它可能会出现在任何标头之前导致客户呕吐。

通常,SocketException: Connection Reset通常意味着您已写入已被对等方关闭的连接。在这种情况下,对等方是客户端,客户端是 Web 浏览器,这可能意味着任何事情,例如用户停止加载页面,他浏览离开,退出浏览器,关闭选项卡。这不是你的问题。只需关闭套接字并忘记它。

出于同样的原因,您的服务器还应该设置一个合理的读取超时时间,比如 10-30 秒,并在它触发时退出。

于 2013-02-27T00:55:03.420 回答
-1

您不能使用DataOutputStream,它是用于 Java-Java 通信的。尝试Writer写入标头,原始 O​​utputStream 用于写入文件内容。

发生的事情是浏览器看到无效响应并关闭连接。服务仍在写入客户端,客户端响应 RST,因为连接消失了。

于 2013-02-26T23:37:49.067 回答