java - html5 音频 GET 连接由移动设备上的对等方重置(在 PC 上工作正常)

标签 java android sockets http-headers html5-audio

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

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

Java 服务器端有一个 ServerSocket 并接受基本上任何连接。然后,它将发送硬编码的 http header ,后跟 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 消息。我尝试了多种类型的 header ,甚至从现有网络服务器复制了许多 header ,但似乎没有任何效果。

即使是简单的标题,例如

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

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

更新

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

我猜测在发送大请求时会出现某种高级协议(protocol)“保护”,也许专门针对不稳定的移动网络。 有什么方法可以绕过这个吗?不管怎么说,似乎都有点过分了。

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

解决方案可能是开始接受范围请求,这样玩家就不会感到“困惑”。一旦我有时间尝试这个,我就会发布结果。

最佳答案

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

要修复此问题,您需要在发送响应之前读取 HTTP 请求。

关于java - html5 音频 GET 连接由移动设备上的对等方重置(在 PC 上工作正常),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34968440/

相关文章:

python 套接字 : how to detect that client has closed/disconnected from the server side

java - 停止 RMI 服务器的干净方法

java - 向文件写入/读取一系列不同的序列化对象

java - ServletRequest的getRemoteHost()和getServerName()有什么区别

android - Android中的这个组件是什么?

c - 如何使用ZeroMQ实现多个套接字?

java - 理解 java.nio.file.Path.relativize(其他路径)

android - 如何合并两个 ListView 的数据

android - Assets 分配在 adb shell dumpsys meminfo 中意味着什么

c - getpeername() 不会返回正确的端口,但会返回正确的远程主机套接字地址 C 语言