为什么 Android 套接字周期性读取如此缓慢?
Why is Android socket reading so slow periodically?
套接字创建如下:
Socket socket = new Socket();
socket.setReuseAddress(true);
socket.setSoTimeout(iTimeout);
socket.connect(new InetSocketAddress(InetAddress.getByName(uri.getHost()), iPort), 6000);
socket.setReceiveBufferSize(iReceiveBufferSize); //iReceiveBufferSize = 1024*256
final InputStream is = socket.getInputStream();
为了问这个问题,创建并测试了一个调试方法:
public void SocketDebug(InputStream isSocketInput)
{
try {
byte[] abBuffer = new byte[1024*256];
for(int i = 0; i < 1000; i++)
{
long lStart = System.currentTimeMillis();
int iRead = isSocketInput.read(abBuffer, 1024 * 10, 1024 * 128);
int iElapse = (int)(System.currentTimeMillis() - lStart);
if(iElapse > 100)
{
utility.logd("Debug", "i:" + i + " iElapse:" + iElapse + " iRead:" + iRead);
}
}
}
catch(Exception ex)
{
}
}
以下是logcat记录的部分内容:
01-Jun 37:26.8 i:9 iElapse:234 iRead:1448
01-Jun 37:29.5 i:114 iElapse:299 iRead:1448
01-Jun 37:29.8 i:126 iElapse:298 iRead:1448
01-Jun 37:30.1 i:132 iElapse:300 iRead:1448
01-Jun 37:30.4 i:139 iElapse:283 iRead:1448
01-Jun 37:30.7 i:146 iElapse:287 iRead:1448
01-Jun 37:31.0 i:160 iElapse:269 iRead:1448
01-Jun 37:31.3 i:169 iElapse:251 iRead:44888
01-Jun 37:31.5 i:170 iElapse:192 iRead:1448
01-Jun 37:31.7 i:185 iElapse:170 iRead:1448
01-Jun 37:32.0 i:198 iElapse:171 iRead:1448
01-Jun 37:32.2 i:217 iElapse:158 iRead:1448
01-Jun 37:32.5 i:240 iElapse:162 iRead:1448
01-Jun 37:32.7 i:259 iElapse:135 iRead:1448
01-Jun 37:33.0 i:281 iElapse:103 iRead:1448
01-Jun 37:34.2 i:324 iElapse:826 iRead:1448
01-Jun 37:34.4 i:330 iElapse:233 iRead:1448
01-Jun 37:34.7 i:336 iElapse:264 iRead:1448
01-Jun 37:35.0 i:341 iElapse:299 iRead:1448
01-Jun 37:35.3 i:346 iElapse:300 iRead:1448
01-Jun 37:35.6 i:352 iElapse:297 iRead:1448
01-Jun 37:36.0 i:354 iElapse:317 iRead:21720
01-Jun 37:36.3 i:355 iElapse:304 iRead:13032
数据源为视频流服务器。大多数时候,isSocketInput.read() 只需要 1 到 3 毫秒(上面的日志中没有显示)。但是,周期性地需要 100 到 1000 毫秒。查看红色字节数,显然 1448 是 TCP 负载。红色字节的所有数字都是1448的倍数。有人可能认为服务器发送一个TCP数据包可能需要这么长时间。难以理解的是 isSocketInput.read() 有时会读取如此多的数据包(例如 31 个数据包 = 44888 字节)并且需要很长时间才能 return。是不是有数据就尽快return?
当 SocketDebug() 为 运行 时,应用程序的所有其他线程基本上进入睡眠状态(即在包含 Thread.sleep() 的循环中)。
谁能提供有关套接字读取时间长的可能原因的提示?
更新(2015-06-03):
以上测试是使用单核 CPU 平板电脑 Android (Asus MeMO) 完成的。当使用具有四核 CPU (AGPTek TP714AQ) 的低端通用 Android 平板电脑完成测试时,isSocketInput.read() 使用的时间显着改善。在前 200 次迭代后,长时间流逝的次数降为零。即使在最初的 200 次左右的迭代中,也只有少数长时间的流逝(> 100 毫秒)。
目前我认为单核CPU设备的时间过长主要是由答案中提到的"process or thread got rescheduled"[=引起的34=] 这在单核 CPU 设备上发生得更频繁。
它读取的速度不能比数据到达的速度快,也不能比数据发送的速度快。别怪收件人的密码,怪罪发件人,怪网络。
当您获得更多字节时,这可能是因为您阻塞了更长的时间,并且在您的进程或线程再次重新安排之前到达了更多数据。您也无法通过代码控制它。
备注:
- 您应该在 连接之前调用
setReceiveBufferSize()
,而不是之后。这样 window 缩放就可以生效了。
- 如果您没有指定本地端口,则调用
setReuseAddress()
是没有意义的。
套接字创建如下:
Socket socket = new Socket();
socket.setReuseAddress(true);
socket.setSoTimeout(iTimeout);
socket.connect(new InetSocketAddress(InetAddress.getByName(uri.getHost()), iPort), 6000);
socket.setReceiveBufferSize(iReceiveBufferSize); //iReceiveBufferSize = 1024*256
final InputStream is = socket.getInputStream();
为了问这个问题,创建并测试了一个调试方法:
public void SocketDebug(InputStream isSocketInput)
{
try {
byte[] abBuffer = new byte[1024*256];
for(int i = 0; i < 1000; i++)
{
long lStart = System.currentTimeMillis();
int iRead = isSocketInput.read(abBuffer, 1024 * 10, 1024 * 128);
int iElapse = (int)(System.currentTimeMillis() - lStart);
if(iElapse > 100)
{
utility.logd("Debug", "i:" + i + " iElapse:" + iElapse + " iRead:" + iRead);
}
}
}
catch(Exception ex)
{
}
}
以下是logcat记录的部分内容:
01-Jun 37:26.8 i:9 iElapse:234 iRead:1448
01-Jun 37:29.5 i:114 iElapse:299 iRead:1448
01-Jun 37:29.8 i:126 iElapse:298 iRead:1448
01-Jun 37:30.1 i:132 iElapse:300 iRead:1448
01-Jun 37:30.4 i:139 iElapse:283 iRead:1448
01-Jun 37:30.7 i:146 iElapse:287 iRead:1448
01-Jun 37:31.0 i:160 iElapse:269 iRead:1448
01-Jun 37:31.3 i:169 iElapse:251 iRead:44888
01-Jun 37:31.5 i:170 iElapse:192 iRead:1448
01-Jun 37:31.7 i:185 iElapse:170 iRead:1448
01-Jun 37:32.0 i:198 iElapse:171 iRead:1448
01-Jun 37:32.2 i:217 iElapse:158 iRead:1448
01-Jun 37:32.5 i:240 iElapse:162 iRead:1448
01-Jun 37:32.7 i:259 iElapse:135 iRead:1448
01-Jun 37:33.0 i:281 iElapse:103 iRead:1448
01-Jun 37:34.2 i:324 iElapse:826 iRead:1448
01-Jun 37:34.4 i:330 iElapse:233 iRead:1448
01-Jun 37:34.7 i:336 iElapse:264 iRead:1448
01-Jun 37:35.0 i:341 iElapse:299 iRead:1448
01-Jun 37:35.3 i:346 iElapse:300 iRead:1448
01-Jun 37:35.6 i:352 iElapse:297 iRead:1448
01-Jun 37:36.0 i:354 iElapse:317 iRead:21720
01-Jun 37:36.3 i:355 iElapse:304 iRead:13032
数据源为视频流服务器。大多数时候,isSocketInput.read() 只需要 1 到 3 毫秒(上面的日志中没有显示)。但是,周期性地需要 100 到 1000 毫秒。查看红色字节数,显然 1448 是 TCP 负载。红色字节的所有数字都是1448的倍数。有人可能认为服务器发送一个TCP数据包可能需要这么长时间。难以理解的是 isSocketInput.read() 有时会读取如此多的数据包(例如 31 个数据包 = 44888 字节)并且需要很长时间才能 return。是不是有数据就尽快return?
当 SocketDebug() 为 运行 时,应用程序的所有其他线程基本上进入睡眠状态(即在包含 Thread.sleep() 的循环中)。
谁能提供有关套接字读取时间长的可能原因的提示?
更新(2015-06-03):
以上测试是使用单核 CPU 平板电脑 Android (Asus MeMO) 完成的。当使用具有四核 CPU (AGPTek TP714AQ) 的低端通用 Android 平板电脑完成测试时,isSocketInput.read() 使用的时间显着改善。在前 200 次迭代后,长时间流逝的次数降为零。即使在最初的 200 次左右的迭代中,也只有少数长时间的流逝(> 100 毫秒)。
目前我认为单核CPU设备的时间过长主要是由答案中提到的"process or thread got rescheduled"[=引起的34=] 这在单核 CPU 设备上发生得更频繁。
它读取的速度不能比数据到达的速度快,也不能比数据发送的速度快。别怪收件人的密码,怪罪发件人,怪网络。
当您获得更多字节时,这可能是因为您阻塞了更长的时间,并且在您的进程或线程再次重新安排之前到达了更多数据。您也无法通过代码控制它。
备注:
- 您应该在 连接之前调用
setReceiveBufferSize()
,而不是之后。这样 window 缩放就可以生效了。 - 如果您没有指定本地端口,则调用
setReuseAddress()
是没有意义的。