如何使用单个Java ServerSocket读取安全的Web请求并处理Web套接字请求?

时间:2018-12-28 17:44:11

标签: java server

简介

我的代码可以处理发送到服务器的所有字节,并决定是否让它们通过并最终发送响应。我想用它来将服务器用作Web服务器,Web套接字服务器和tcp服务器。

尽管我的代码是为Minecraft编写的,但我并不是在Minecraft论坛上问这个问题,因为回答此问题不需要任何有关Minecraft或其代码库的先验知识。

所有您需要了解的Minecraft

Minecraft是可以在线玩的Java游戏。在线播放时,有一台服务器打开ServerSocket,所有播放器都有自己的客户端,该客户端打开一个Socket,该Socket与服务器的ServerSocket通信。

任何人都可以创建Minecraft服务器并在其服务器上安装服务器端修改(对于那些了解Minecraft的人来说,这些通常称为插件)。我的应用程序就是这样的服务器端修改。大多数Minecraft服务器由Minecraft托管公司托管。服务器的所有者可以访问管理服务器文件的主机部分。

目标

我修改的目的是让Minecraft服务器为更多的客户端提供服务,而不仅仅是Minecraft客户端。我希望同一台服务器也可以用作Web服务器(用于http和https请求)以及(安全的)web套接字服务器和tcp服务器。

为什么没有多个服务器套接字

最常见的解决方案是为其他服务器类型创建一个ServerSocket并为所有其他服务器类型分配一个不同的端口。但是,就我而言,这不是一个选择。大多数主机禁止您打开其他端口或要求额外的费用。因此,我只需要使用Minecraft ServerSocket来完成所有操作。

到目前为止我取得的成就

到目前为止,我已经设法让发送到Minecraft服务器的所有字节都首先通过我的代码。我的代码可以选择是否让字节继续到Minecraft服务器代码。它也可以自行发送响应,而无需通知Minecraft服务器代码。

原则上,我设法完成的工作足以实现我的目标,但是我希望在继续方面有所帮助。我将在下面说明到目前为止我尚未完成的工作。

Minecraft客户端发送到服务器的第一个字节始终相同,即16。这非常好,因为它使我能够轻松区分Minecraft客户端与Web浏览器和tcp客户端。

HTTP请求和Websocket连接始终以相同的字节(即71)开头。HTTPS和安全Websocket始终以字节22开头。我正在谈论的TCP连接将由我自己的应用程序发送,因此我可以选择确切的内容他们将发送的字节,我可以简单地编写我的修改来对此做出响应。

我设法通过http连接和websocket连接的连接属性来区分它们。 Http请求始终发送“连接:保持活动”,而websocket连接始终发送“连接:升级”。 (尽管有些浏览器使用大写字母表示k,a和u,而其他浏览器则不使用。)

处理正常的http请求并不是一件难事。处理TCP连接也不会很困难,因为我将控制一切。但是我对其余的连接类型有疑问:

我需要帮助的问题

Web套接字协议非常大,我希望不要仅用我的代码来完全处理它。 (我之前曾尝试过,但是对于那些很少使用且未经测试的部件,我仍然会遇到问题。)因此,我想使用一些库,该库仅让我担心有效负载而不是整个协议。不幸的是,Web套接字库通常要创建ServerSocket,在我看来,这是不可能的。 那么有人在这里做什么建议吗?

我没有找到有关如何正确读取https请求的任何信息。 有人可以告诉我在哪里可以找到该协议的详细信息或提供一个不错的链接吗? 对于安全的Web套接字,在了解了如何读取请求之后,我将面临与“普通” Web套接字连接相同的问题。

代码

到目前为止,我的所有代码都可以在https://github.com/knokko/Multi-Purpose-Server上找到。最有趣的部分可能是我的代码有机会在所有字节到达Minecraft代码之前对其进行处理的部分,该代码如下所示。

简短问题

对于那些不完全了解我的问题的人(您可以将其视为两个密切相关的问题):

-我应该如何阅读https请求和安全的Web套接字握手?

-是否有人知道可以处理不需要创建ServerSocket本身的Web套接字输入的库?

// This channel handler will be registered for every connection client that will
                    // inspect
                    // any message before it reaches the Minecraft code.
                    pipeline.addFirst("multipurpose_handler_inspector", new ChannelInboundHandlerAdapter() {

                        private boolean deactivated;

                        private ChannelListener listener;

                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                        //super.channelRead will send the content to the minecraft code
                            try {
                                if (!deactivated) {
                                    ByteBuf message = (ByteBuf) msg;
                                    if (listener != null) {
                                        listener.read(ctx, message);
                                    } else {
                                        byte firstByte = message.getByte(0);

                                        // All Minecraft connections start with the byte 16
                                        if (firstByte == 16) {
                                            deactivated = true;
                                            super.channelRead(ctx, msg);
                                        }

                                        // All insecure web connections start with the byte 71
                                        else if (firstByte == 71) {
                                            byte[] data = new byte[message.readableBytes()];
                                            message.getBytes(0, data);
                                            WebHandler.Type type = WebHandler.determineConnectionType(data);
                                            if (type == WebHandler.Type.HTTP) {
                                                listener = new HTTPListener();
                                                listener.readInitial(ctx, message);
                                            } else if (type == WebHandler.Type.WEBSOCKET) {
                                                // TODO Find a nice way to handle web socket connections
                                                listener = new WebSocketListener();
                                                listener.readInitial(ctx, message);
                                            } else {
                                                deactivated = true;
                                                super.channelRead(ctx, msg);
                                            }
                                        }

                                        // All secure web connections start with the byte 22
                                        else if (firstByte == 22) {
                                            // TODO implement the secure web protocols and find a way to read this stuff
                                            // and find the difference
                                            System.out.println(
                                                    "We are dealing with a secure websocket or https connection");
                                            byte[] data = new byte[message.readableBytes()];
                                            message.getBytes(0, data);
                                            System.out.println(new String(data));
                                        }

                                        // My applications
                                        else if (firstByte == 31) {
                                            listener = new TCPListener();
                                            listener.readInitial(ctx, message);
                                        } else {
                                            System.out.println("Unknown connection type");
                                            deactivated = true;
                                            super.channelRead(ctx, msg);
                                        }
                                    }
                                } else {
                                    super.channelRead(ctx, msg);
                                }
                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }
                        }
                    });

1 个答案:

答案 0 :(得分:1)

如果您始终可以识别Minecraft流量,则最好的选择是在同一框中运行apache / httpd和/或tomcat服务器,并将所有非Minecraft流量转发给该服务器。如果这样做,HTTPS可能只是正确配置HTTP服务器以进行https流量的问题。

您可能必须将代码配置为http代理-实际上(只是想到这一点),您可能想出去寻找开源的HTTP代理,并对其进行调整,以提取Minecraft流量并在完成其余代理工作之前将其转发。

我不会从头开始做HTTPs的事情,这并不是很难,但是我称其为平凡的。

哦,如果您的问题是“将Minecraft HTTPS流量与同一端口上的其他HTTPS连接区分开”,我无能为力,只能说这可能是您提出问题的一个好主题:)