在 Android 上同时从服务器循环读取和写入
Reading and Writing from Server in loop in the same time on Android
我需要创建一个到服务器的连接并在一段时间内从服务器读取消息。同时,我需要每X秒向服务器发送一个字符串来更新数据。我正在使用 HttpUrlConnection
和以下代码:
NTRIPClient.kt
class NTRIPClient {
private val bufferSize = ByteArray(64)
private val crlf = "\r\n"
private var inputStream: BufferedInputStream? = null
fun startConnection(gga: String, usbService: UsbService) {
con = url.openConnection() as HttpURLConnection?
con?.doOutput = true
con?.setChunkedStreamingMode(0)
con?.setRequestProperty("Ntrip-GGA", gga)
con?.setRequestProperty("Connection", "Keep-Alive")
con?.setRequestProperty("Accept-Encoding", "identity")
try {
inputStream = BufferedInputStream(con!!.inputStream)
var readBuffer = inputStream!!.read(bufferSize)
while (readBuffer > 0) {
usbService.write(bufferSize)
readBuffer = inputStream!!.read(bufferSize)
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
con?.disconnect()
}
}
fun updateGGA(gga: String) {
try {
val outputStream = OutputStreamWriter(con!!.outputStream) //this line create error
outputStream.write(gga + crlf)
outputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
在ViewModel.kt
中调用 NTRIPClient
fun createNtripConnection(usbService: UsbService){
ntripScope.launch {
awaitAll(
async {
Log.d(TAG, "createNtripConnection: START")
ntripClient.startConnection(currentGGA, usbService)
},
async {
delay(5000)
ntripClient.updateGGA(currentGGA)
}
)
}
}
当我 运行 方法 createNtripConnection
一切正常,但是当需要执行方法 updateGGA
时,我收到以下错误:
W/System.err: java.io.IOException: unexpected end of stream on com.android.okhttp.Address@5170b14c
at com.android.okhttp.internal.http.Http1xStream.readResponse(Http1xStream.java:203)
at com.android.okhttp.internal.http.Http1xStream.readResponseHeaders(Http1xStream.java:129)
at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:750)
at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:622)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:475)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:248)
at com.hq.advian.newtwork.NTRIPClient.startConnection(NTRIPClient.kt:43)
at com.hq.advian.viewmodel.ArViewModel$createNtripConnection.invokeSuspend(ArViewModel.kt:207)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
W/System.err: at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: java.io.EOFException: \n not found: size=0 content=...
at com.android.okhttp.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:202)
at com.android.okhttp.internal.http.Http1xStream.readResponse(Http1xStream.java:188)
... 14 more
如果在 updateGGA
一开始我尝试关闭 inputStream
,在这一行我得到 java.lang.NullPointerException
。你能帮忙吗?或者有其他工具可以解决这个问题?
所以我找到了解决方案。 HttpUrlConnection
不能同时读写数据。为此,您需要使用 Socket
。首先你需要创建一个Socket
并连接到主机:
InetAddress address = InetAddress.getByName(new URL("http://fi.nrtk.eu:7801/MSM5").getHost());
Socket socket = new Socket(address, 7801);
System.out.println(socket.isConnected());
如果您连接成功,那么您将在控制台中看到 true
。
现在您必须打开两个流才能与服务器通信:InputStream
和 OutputStream
:
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
BufferedInputStream input = new BufferedInputStream(socket.getInputStream());
现在您可以发送 GET、POST 和其他请求。为此,我们将使用 PrintWriter
:
String requestMessage = "GET /MSM5 HTTP/1.1\r\n";
output.println(requestMessage);
我们使用消息GET /{path} {protocol (HTTP/1.1 for example)}
。在每一行的末尾,您必须使用 CRLF \r\n
。然后可以根据scheme添加必要的header:
output.println("{header name}: {header value}\r\n");
output.println("Connection: Keep-Alive");
output.println("Accept-Encoding: identity");
之后就可以从服务器读取数据了。就我而言,这是无穷无尽的消息流,因此我将使用以下选项:
byte[] buffer = new byte[128];
int readBuffer = input.read(buffer);
while (readBuffer > 0) {
System.out.println(Arrays.toString(buffer));
readBuffer = input.read(buffer);
}
如果在读取数据的过程中需要更改header,可以重新向服务器发送字符串:
output.println("Connection: close");
完整方法如下所示:
void createSocket() {
try {
InetAddress address = InetAddress.getByName(new URL("http://fi.nrtk.eu:7801/MSM5").getHost());
Socket socket = new Socket(address, 7801);
System.out.println(socket.isConnected());
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
BufferedInputStream input = new BufferedInputStream(socket.getInputStream());
String requestMessage = "GET /MSM5 HTTP/1.1\r\n";
output.println(requestMessage);
output.println("Ntrip-Version: Ntrip/2.0\r\n");
output.println("Connection: Keep-Alive");
output.println("Accept-Encoding: identity");
output.println("\r\n");
byte[] buffer = new byte[128];
int readBuffer = input.read(buffer);
while (readBuffer > 0) {
System.out.println(Arrays.toString(buffer));
readBuffer = input.read(buffer);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
我需要创建一个到服务器的连接并在一段时间内从服务器读取消息。同时,我需要每X秒向服务器发送一个字符串来更新数据。我正在使用 HttpUrlConnection
和以下代码:
NTRIPClient.kt
class NTRIPClient {
private val bufferSize = ByteArray(64)
private val crlf = "\r\n"
private var inputStream: BufferedInputStream? = null
fun startConnection(gga: String, usbService: UsbService) {
con = url.openConnection() as HttpURLConnection?
con?.doOutput = true
con?.setChunkedStreamingMode(0)
con?.setRequestProperty("Ntrip-GGA", gga)
con?.setRequestProperty("Connection", "Keep-Alive")
con?.setRequestProperty("Accept-Encoding", "identity")
try {
inputStream = BufferedInputStream(con!!.inputStream)
var readBuffer = inputStream!!.read(bufferSize)
while (readBuffer > 0) {
usbService.write(bufferSize)
readBuffer = inputStream!!.read(bufferSize)
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
con?.disconnect()
}
}
fun updateGGA(gga: String) {
try {
val outputStream = OutputStreamWriter(con!!.outputStream) //this line create error
outputStream.write(gga + crlf)
outputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
在ViewModel.kt
中调用 NTRIPClientfun createNtripConnection(usbService: UsbService){
ntripScope.launch {
awaitAll(
async {
Log.d(TAG, "createNtripConnection: START")
ntripClient.startConnection(currentGGA, usbService)
},
async {
delay(5000)
ntripClient.updateGGA(currentGGA)
}
)
}
}
当我 运行 方法 createNtripConnection
一切正常,但是当需要执行方法 updateGGA
时,我收到以下错误:
W/System.err: java.io.IOException: unexpected end of stream on com.android.okhttp.Address@5170b14c
at com.android.okhttp.internal.http.Http1xStream.readResponse(Http1xStream.java:203)
at com.android.okhttp.internal.http.Http1xStream.readResponseHeaders(Http1xStream.java:129)
at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:750)
at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:622)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:475)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:248)
at com.hq.advian.newtwork.NTRIPClient.startConnection(NTRIPClient.kt:43)
at com.hq.advian.viewmodel.ArViewModel$createNtripConnection.invokeSuspend(ArViewModel.kt:207)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
W/System.err: at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: java.io.EOFException: \n not found: size=0 content=...
at com.android.okhttp.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:202)
at com.android.okhttp.internal.http.Http1xStream.readResponse(Http1xStream.java:188)
... 14 more
如果在 updateGGA
一开始我尝试关闭 inputStream
,在这一行我得到 java.lang.NullPointerException
。你能帮忙吗?或者有其他工具可以解决这个问题?
所以我找到了解决方案。 HttpUrlConnection
不能同时读写数据。为此,您需要使用 Socket
。首先你需要创建一个Socket
并连接到主机:
InetAddress address = InetAddress.getByName(new URL("http://fi.nrtk.eu:7801/MSM5").getHost());
Socket socket = new Socket(address, 7801);
System.out.println(socket.isConnected());
如果您连接成功,那么您将在控制台中看到 true
。
现在您必须打开两个流才能与服务器通信:InputStream
和 OutputStream
:
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
BufferedInputStream input = new BufferedInputStream(socket.getInputStream());
现在您可以发送 GET、POST 和其他请求。为此,我们将使用 PrintWriter
:
String requestMessage = "GET /MSM5 HTTP/1.1\r\n";
output.println(requestMessage);
我们使用消息GET /{path} {protocol (HTTP/1.1 for example)}
。在每一行的末尾,您必须使用 CRLF \r\n
。然后可以根据scheme添加必要的header:
output.println("{header name}: {header value}\r\n");
output.println("Connection: Keep-Alive");
output.println("Accept-Encoding: identity");
之后就可以从服务器读取数据了。就我而言,这是无穷无尽的消息流,因此我将使用以下选项:
byte[] buffer = new byte[128];
int readBuffer = input.read(buffer);
while (readBuffer > 0) {
System.out.println(Arrays.toString(buffer));
readBuffer = input.read(buffer);
}
如果在读取数据的过程中需要更改header,可以重新向服务器发送字符串:
output.println("Connection: close");
完整方法如下所示:
void createSocket() {
try {
InetAddress address = InetAddress.getByName(new URL("http://fi.nrtk.eu:7801/MSM5").getHost());
Socket socket = new Socket(address, 7801);
System.out.println(socket.isConnected());
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
BufferedInputStream input = new BufferedInputStream(socket.getInputStream());
String requestMessage = "GET /MSM5 HTTP/1.1\r\n";
output.println(requestMessage);
output.println("Ntrip-Version: Ntrip/2.0\r\n");
output.println("Connection: Keep-Alive");
output.println("Accept-Encoding: identity");
output.println("\r\n");
byte[] buffer = new byte[128];
int readBuffer = input.read(buffer);
while (readBuffer > 0) {
System.out.println(Arrays.toString(buffer));
readBuffer = input.read(buffer);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}