使用HttpURLConnection时设置setChunkedStreamingMode为何抛出Socket异常?
HttpURLConnection启用setChunkedStreamingMode后抛出Broken pipe异常
我尝试使用HttpURLConnection向服务器上传文件,了解到调用setChunkedStreamingMode并传入缓冲区大小可以避免大文件传输时的内存耗尽问题,但取消注释该方法后会抛出java.net.SocketException: Broken pipe异常,注释掉则代码正常运行,希望得到正确使用该方法的指导。
示例代码
try { val dataStream = applicationContext.contentResolver.openInputStream(Uri.parse(fileUri)) val url = URL(urlString) val httpConnection = url.openConnection() as HttpURLConnection for (i in keys.indices) { httpConnection.setRequestProperty(keys[i], values[i]) } httpConnection.requestMethod = "POST" httpConnection.doOutput = true // 启用分块模式后触发Socket异常 // httpConnection.setChunkedStreamingMode(DEFAULT_BUFFER_SIZE * 2) val outPutStream = BufferedOutputStream(httpConnection.outputStream) var bytesCopied: Long = 0 val buffer = ByteArray(DEFAULT_BUFFER_SIZE) var bytes = dataStream.read(buffer) while (bytes >= 0 && !isStopped) { outPutStream.write(buffer, 0, bytes) bytes = dataStream.read(buffer) } } catch(ex) { print(ex) // 启用setChunkedStreamingMode时打印Socket异常"Broken pipe" }
抛出的异常信息
java.net.SocketException: Broken pipe java.net.SocketOutputStream.socketWrite0(Native Method) java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:116) java.net.SocketOutputStream.write(SocketOutputStream.java:156) com.android.org.conscrypt.ConscryptEngineSocket$SSLOutputStream.writeToSocket(ConscryptEngineSocket.java:762) com.android.org.conscrypt.ConscryptEngineSocket$SSLOutputStream.writeInternal(ConscryptEngineSocket.java:736) com.android.org.conscrypt.ConscryptEngineSocket$SSLOutputStream.write(ConscryptEngineSocket.java:699) com.android.okhttp.okio.Okio$1.write(Okio.java:78) com.android.okhttp.okio.AsyncTimeout$1.write(AsyncTimeout.java:157) com.android.okhttp.okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:177) com.android.okhttp.okio.RealBufferedSink.write(RealBufferedSink.java:47) com.android.okhttp.internal.http.Http1xStream$ChunkedSink.write(Http1xStream.java:327) com.android.okhttp.okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:177) com.android.okhttp.okio.RealBufferedSink$1.write(RealBufferedSink.java:199) java.io.BufferedOutputStream.write(BufferedOutputStream.java:123) myapp(upload.kt:558) myapp.invokeSuspend(Unknown Source:14) kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570) kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677) kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
问题解决方法
1. 调整方法调用顺序
setChunkedStreamingMode必须在设置请求方法、请求属性以及打开输出流之前调用,HttpURLConnection的配置有严格的顺序限制,后续修改流模式会导致HTTP协议冲突。修正后的调用顺序如下:
val httpConnection = url.openConnection() as HttpURLConnection // 先设置分块流模式 httpConnection.setChunkedStreamingMode(DEFAULT_BUFFER_SIZE * 2) // 再配置请求方法、输出属性和请求头 httpConnection.requestMethod = "POST" httpConnection.doOutput = true for (i in keys.indices) { httpConnection.setRequestProperty(keys[i], values[i]) }
2. 验证服务器对分块编码的支持
Broken pipe异常也可能是服务器不支持Transfer-Encoding: chunked导致的,服务器收到分块请求后主动断开连接。可以尝试:
- 联系后端确认服务器是否启用分块编码支持
- 调整分块缓冲区大小(比如改为8192字节)测试
3. 完善流的关闭与请求收尾
上传完成后需正确关闭流,并主动获取响应码,确保HTTP请求完整完成,避免连接异常中断:
try { val dataStream = applicationContext.contentResolver.openInputStream(Uri.parse(fileUri)) val url = URL(urlString) val httpConnection = url.openConnection() as HttpURLConnection httpConnection.setChunkedStreamingMode(DEFAULT_BUFFER_SIZE * 2) httpConnection.requestMethod = "POST" httpConnection.doOutput = true for (i in keys.indices) { httpConnection.setRequestProperty(keys[i], values[i]) } val outPutStream = BufferedOutputStream(httpConnection.outputStream) val buffer = ByteArray(DEFAULT_BUFFER_SIZE) var bytes = dataStream.read(buffer) while (bytes >= 0 && !isStopped) { outPutStream.write(buffer, 0, bytes) bytes = dataStream.read(buffer) } // 刷新并关闭输出流 outPutStream.flush() outPutStream.close() // 关闭输入流 dataStream.close() // 必须获取响应码,确保请求完成 val responseCode = httpConnection.responseCode } catch(ex) { print(ex) } finally { // 断开连接 httpConnection.disconnect() }
4. 避免冲突的流模式配置
不要同时使用setFixedLengthStreamingMode和setChunkedStreamingMode,两者只能选择其一,否则会导致协议配置冲突。
内容的提问来源于stack exchange,提问作者Nikhileshwar
相关产品推荐
相关产品推荐

