html5音频GET连接由移动设备上的同行重置(在PC上正常工作)

时间:2016-01-23 20:25:26

标签: java android sockets http-headers html5-audio

我想要做的只是通过http / tcp使用我自己的http标头发送一个mp3文件。在客户端,我有一个包含以下行的网页:

<audio src="http://192.168.0.21:14441" controls autoplay loop>

Java服务器端有ServerSocket并且基本上接受任何连接。然后它将发送硬编码的http标头,然后发送mp3文件的二进制数据。

我的服务器类:

public class Server {

    private int port = 14441;
    private String localIPAddress;

    private BufferedReader in;
    private BufferedOutputStream out;

    private ServerSocket serverSocket;
    private Socket clientSocket;

    public Server() {

    }

    public void start() {

        new Thread(new Runnable() {
            @Override
            public void run() {
                startAcceptingConnections();
            }
        }).start();

    }

    private void startAcceptingConnections() {

        tryToOpenPort();//try to open external port with upnp 
        clientSocket = null;
        try {
            serverSocket = new ServerSocket(port);
        } catch (IOException e) {
            System.err.println("Could not listen on port:" + port);
            System.exit(1);
        }

        System.out.println("Waiting for connection.....");

        try {
            clientSocket = serverSocket.accept();

            System.out.println("Connection successful");
            System.out.println("Waiting for input.....");

            out = new BufferedOutputStream(clientSocket.getOutputStream());
            in = new BufferedReader(
                    new InputStreamReader(clientSocket.getInputStream()));

            String inputLine;
            boolean isSendingMp3 = false;
            while ((inputLine = in.readLine()) != null) {

                //just output any line the client sends me
                System.out.println("Received: " + inputLine);

                //Whenever the client sends an empty line this means
                //it's ready to receive
                if (inputLine.equals("") && !isSendingMp3) {
                    isSendingMp3 = true;
                    //Making sure to keep listening to the InputStream
                    //so I send from a different thread
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            sendMp3File();
                        }
                    }).start();
                }

            }//end of listening to client loop

            out.close();
            in.close();
            clientSocket.close();
            serverSocket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendMp3File() {
        try {

            //I've tried all sorts of headers
            String response = "HTTP/1.x 200 OK\r\n"
                + "Content-Type: audio/mpeg\r\n"
                + "Content-Size: 2911084\r\n"
                + "Range: bytes 0-2911083/2911084\r\n"
                + "X-Content-Duration: 300.1\r\n"
                + "Connection: keep-alive\r\n"
                + "Content-Duration: 300.1\r\n"
                + "\r\n";


            out.write(response.getBytes(Charset.forName("UTF-8")));
            byte[] bytesRaw = new byte[1024 * 10];
            InputStream is = new FileInputStream("C:/sample.mp3");

            int byteCount = 0;
            while ((byteCount = is.read(bytesRaw)) != -1) {

                System.out.println("sending bytes:" + byteCount);
                out.write(bytesRaw, 0, byteCount);
            }
            out.flush();
            is.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    //Use Cling to open the port via upnp
    private void tryToOpenPort() {
        try {
            localIPAddress = Inet4Address.getLocalHost().getHostAddress();
            PortMapping desiredMapping
                    = new PortMapping(
                            port,
                            localIPAddress,
                            PortMapping.Protocol.TCP,
                            "Test server"
                    );

            UpnpService upnpService = new UpnpServiceImpl(new PortMappingListener(desiredMapping));
            upnpService.getControlPoint().search();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}

这个始终可以在PC浏览器(Firefox,Chrome,IE)上运行,并且机器到机器没有问题(没有防火墙干扰)。

但是,只要我从移动设备(iOS和Android)运行网页,连接就会在发送似乎是随机数据的数据后突然关闭。这是建立连接后0到2秒之间的某个时间。

Java应用程序抛出异常: java.net.SocketException:通过对等方重置连接:套接字写入错误

当我使用Wireshark进行分析时,它向我显示一切顺利,然后突然客户端开始发送一堆RST消息。我尝试了多种类型的标题,甚至从现有的Web服务器复制了许多标题,但似乎没有任何工作。

即使是简单的标题,如

"HTTP/1.1 200 OK\r\n"
+ "Content-Type: audio/mpeg \r\n"
+ "\r\n";

从计算机浏览器打开它们时工作,但在移动设备上重置连接。我忘记了重要的事情吗?

更新

在移动浏览器上,它会在发送一些数据后关闭连接。它以似乎是随机的间隔再次连接和断开,有时在标题中有一个范围,有时不是。即使收到整个文件,它也会继续打开连接。

我在发送大量请求时猜测某种高协议“保护”,特别是在不稳定的移动网络上。 有没有办法绕过这个?不管它是什么,似乎有点过分。

html5音频元素要求带有范围标题的前2个字节会发生什么。然后,当我(根据rfc2616有效地)忽略此范围并发送整个文件时,音频播放器开始表现得好像它是一个音频流(或者至少变得非常困惑)。这仍然只发生在移动浏览器上。

解决方案可能是开始接受范围请求,以便玩家不会“混淆”。我会在我有时间尝试这个时发布结果。

1 个答案:

答案 0 :(得分:2)

我认为问题出在您未显示的代码中。我的猜测是你接受连接,然后简单地发送响应而不读取请求。但是如果在未读取请求的情况下关闭连接,则会导致连接重置。此重置如何影响客户端取决于时间,即可能是客户端在重置之前处理了响应,或者在有时间处理响应之前发现了重置。

要修复它,您需要在发送响应之前阅读HTTP请求。