【问题标题】:Multiple HttpExchanges back-to-back, cleaning up from the first exchange using com.sun.net.httpserver.HttpServer多个 HttpExchange 背靠背,使用 com.sun.net.httpserver.HttpServer 从第一个交换中清理
【发布时间】:2013-04-29 13:09:33
【问题描述】:

我有两个 HttpURLConnections 从客户端连接到服务器。这不是典型的 HttpServer,我正在编写客户端和服务器的代码,但只能使用端口 80/443(用于测试任何端口的工作原理)。第一个请求顺利通过,但第二个请求(在第一个请求之后立即发生)无法到达服务器。我正在调用HttpExchange.closeHttpURLConnection.disconnect 并刷新所有流,但我无法获得第二个到达服务器的请求。我正在使用 Oracle 的 JDK 1.7_17 x64 并在 Win7 x64 上运行。下面是 SSCCE 的输出和 SSCCE。我错过了什么!!??

服务器

Started server @ /127.0.0.1:8765
Handling HttpExchange!
Processing request from user: joe.smith

客户:

ClientID: 0
Response code: 200

这里是 SSCCE:

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
import javax.swing.JOptionPane;

public class CommsTest {

    public static void main(String[] args) throws IOException {
        Object startMe = JOptionPane.showInputDialog(null, "Start client or server?", "Launcher", JOptionPane.QUESTION_MESSAGE, null,
                new Object[]{"Client", "Server"}, "Server");
        if (startMe.equals("Server")) {
            HttpServer httpServer = HttpServer.create(new InetSocketAddress("localhost", 8765), 50000);
            httpServer.createContext("/test", new HttpHandler() {
                private byte clientIdCounter = 0;

                @Override
                public void handle(HttpExchange he) throws IOException {
                    System.out.println("Handling HttpExchange!");
                    int requestCode = Integer.parseInt(he.getRequestHeaders().getFirst("ReqCode"));
                    switch (requestCode) {
                        case 0:
                            // request '0' sends username and accepts a byte back
                            try (ObjectInputStream ois = new ObjectInputStream(he.getRequestBody())) {
                                System.out.println("Processing request from user: " + ois.readUTF());
                            }
                            // send a response of code of 200, all is well! and i'm sending 1 byte back.
                            he.sendResponseHeaders(200, 1);
                            he.getResponseBody().write(new byte[]{clientIdCounter++});
                            he.close();
                            break;
                        case 1:
                            // request '1' sends 3 bytes
                            try (ObjectInputStream ois = new ObjectInputStream(he.getRequestBody())) {
                                byte clientId = ois.readByte();
                                byte opCode = ois.readByte();
                                byte argument = ois.readByte();
                                System.out.println(String.format("Received op request %d from client %d with argument %d", clientId, opCode,
                                        argument));
                                // send a response code of 200, all is well! no data is being sent back though.
                                he.sendResponseHeaders(200, 0);
                                he.close();
                            }
                            break;
                    }
                }
            });
            httpServer.start();
            System.out.println("Started server @ "+ httpServer.getAddress());
        } else if (startMe.equals("Client")) {
            // first phase of the connection, send a opCode of 0, send the username, receive the client id
            HttpURLConnection con = create(true, true, 0);
            try (ObjectOutputStream oos = new ObjectOutputStream(con.getOutputStream())) {
                oos.writeUTF(System.getProperty("user.name"));
            }
            byte clientId = (byte) con.getInputStream().read();
            System.out.println("ClientID: " + clientId);
            System.out.println("Response code: "+ con.getResponseCode());
            con.disconnect();
            // next phase of connection, send an opCode of 1 and 3 bytes, receive nothing
            con = create(false, true, 0);
            try (ObjectOutputStream oos = new ObjectOutputStream(con.getOutputStream())) {
                oos.writeByte(0);
                oos.writeByte(4);
                oos.writeByte(2);
                oos.flush(); //I've also tried closing the oos manually and calling con.disconnect, to no avail
            }
        }
    }

    private static HttpURLConnection create(boolean input, boolean output, int reqCode) throws IOException {
        URL url = new URL("http://localhost:8765/test");
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setUseCaches(false);
        //I have tried #setRequestMethod("POST") but that didn't help
        con.setDoInput(input);
        con.setDoOutput(output);
        con.setRequestProperty("ReqCode", Integer.toString(reqCode));
        return con;
    }
}

【问题讨论】:

  • 看起来问题与发回响应标头有关,例如200 可以。这显然算作响应数据(我认为标题只是一个标题,而不是真正的数据)。无论如何,我仍在努力弄清楚如何正确地清理它 - 如果在这个 HTTP 世界中出现任何问题,它就会悄悄地下山!如果/当我把它全部清理干净时,我会发布 SSCCE。

标签: java networking httpurlconnection httpserver


【解决方案1】:

这是清理后的工作 SSCCE!如果有人有任何进一步的建议,尤其是任何可以加快客户端-服务器交互的建议,请告诉我。我知道我将拥有少于 20 个客户端并且只有一个服务器。来自佛罗里达的欢呼!

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.Executors;
import javax.swing.JOptionPane;

public class CommsTest2 {

    /**
     * Starts a server or a client (depending on UI input). The server listens on http://localhost:8765/test/register and
     * http://localhost:8765/test/registerFor. The server sends back a clientID on registration and accepts 3 bytes on 'registerFor'. In
     * all cases the server sends back a 200/OK status code if all goes well.
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        Object startMe = JOptionPane.showInputDialog(null, "Start client or server?", "Launcher", JOptionPane.QUESTION_MESSAGE, null,
                new Object[]{"Client", "Server"}, "Server");
        if (startMe.equals("Server")) {
            HttpServer httpServer = HttpServer.create(new InetSocketAddress("localhost", 8765), 50000);
            httpServer.setExecutor(Executors.newFixedThreadPool(20));
            httpServer.createContext("/test/" + Op.REGISTER.urlOpPath, new HttpHandler() {
                private byte clientIdCounter = 0;

                @Override
                public void handle(HttpExchange he) throws IOException {
                    System.out.println("Handling 'register' HttpExchange");
                    // 'register' sends username and accepts a byte back (the client ID)
                    try (ObjectInputStream ois = new ObjectInputStream(he.getRequestBody())) {
                        System.out.println("Processing request from user: " + ois.readUTF());
                    }
                    // send a response of code of 200, all is well! and i'm sending 1 byte back, which is the client id
                    he.sendResponseHeaders(200, 1);
                    he.getResponseBody().write(new byte[]{clientIdCounter++});
                    he.close();
                    System.out.println("Registration complete!");

                }
            });
            httpServer.createContext("/test/" + Op.REGISTER_FOR.urlOpPath, new HttpHandler() {
                @Override
                public void handle(HttpExchange he) throws IOException {
                    System.out.println("Handling 'registerFor' HttpExchange");
                    // request '1' sends 3 bytes
                    try (ObjectInputStream ois = new ObjectInputStream(he.getRequestBody())) {
                        byte clientId = ois.readByte();
                        byte opCode = ois.readByte();
                        byte argument = ois.readByte();
                        System.out.println(String.format("Received op request %d from client %d with argument %d", clientId, opCode,
                                argument));
                        // send a response code of 200, all is well! no data is being sent back though.
                        he.sendResponseHeaders(200, 0);
                    }
                    he.close();
                    System.out.println("Register-For complete!");
                }
            });
            httpServer.start();
            System.out.println("Started server @ " + httpServer.getAddress());
        } else if (startMe.equals("Client")) {
            // first phase of the connection, send a opCode of 0, send the username, receive the client id
            {
                HttpURLConnection con = create(Op.REGISTER);
                try (ObjectOutputStream oos = new ObjectOutputStream(con.getOutputStream())) {
                    oos.writeUTF(System.getProperty("user.name"));
                }
                byte clientId = (byte) con.getInputStream().read();
                System.out.println("Received ClientID: " + clientId + " and response code: " + con.getResponseCode());
                con.disconnect();
            }
            // next phase of connection, send an opCode of 1 and 3 bytes, receive nothing
            {
                HttpURLConnection con = create(Op.REGISTER_FOR);
                try (ObjectOutputStream oos = new ObjectOutputStream(con.getOutputStream())) {
                    oos.writeByte(0);
                    oos.writeByte(4);
                    oos.writeByte(2);
                }
                System.out.println("Received response code: " + con.getResponseCode());
                con.disconnect();
            }
            System.out.println("Done sending!");
        }
    }

    private static HttpURLConnection create(Op op) throws IOException {
        HttpURLConnection con = (HttpURLConnection) op.url.openConnection();
        con.setUseCaches(false);
        con.setDoInput(op.input);
        con.setDoOutput(op.output);
        return con;
    }

    private enum Op {

        REGISTER(true, true, "register"),
        REGISTER_FOR(true, true, "registerFor");
        private final boolean input, output;
        private final String urlOpPath;
        private final URL url;

        private Op(boolean input, boolean output, String opPath) {
            this.input = input;
            this.output = output;
            this.urlOpPath = opPath;
            try {
                this.url = new URL("http://localhost:8765/test/" + opPath);
            } catch (MalformedURLException ex) {
                throw new RuntimeException(ex);
            }
        }
    }
}

【讨论】:

  • 那么问题到底出在哪里?过于本地化。
  • 发生了两个 HTTPExchanges,第一个定义它将进行输入和输出 (HttpURLConnection#setDoInput,setDoOutput),第二个表示它只是进行输出(发送 3 个字节)并且不接收输入。但是,服务器在这一秒发回了响应代码(OK/200)。根据 HttpExchange javadoc:“在不消耗所有请求正文的情况下关闭交换不是错误,但可能会使底层 TCP 连接无法用于后续交换。”但是,我在 current 交换中遇到了问题!
猜你喜欢
  • 1970-01-01
  • 2017-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
相关资源
最近更新 更多