我想做的只是通过 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/