我想要做的只是通过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有效地)忽略此范围并发送整个文件时,音频播放器开始表现得好像它是一个音频流(或者至少变得非常困惑)。这仍然只发生在移动浏览器上。
解决方案可能是开始接受范围请求,以便玩家不会“混淆”。我会在我有时间尝试这个时发布结果。
答案 0 :(得分:2)
我认为问题出在您未显示的代码中。我的猜测是你接受连接,然后简单地发送响应而不读取请求。但是如果在未读取请求的情况下关闭连接,则会导致连接重置。此重置如何影响客户端取决于时间,即可能是客户端在重置之前处理了响应,或者在有时间处理响应之前发现了重置。
要修复它,您需要在发送响应之前阅读HTTP请求。