我已经构建了一个连接到 Ip、发送hello world并定期发送日期时间的小型 android 测试 tcp 客户端。
为了测试这一点,我使用netcat作为服务器,方式如下:
nc -l 5000
客户端通过套接字连接到服务器,并且使用 wifi 或 3g 网络在netcat屏幕上正确显示日期时间。
如果在 3g 中启动应用程序并让应用程序在一小段时间(一秒间隔)内发送日期时间并更改为 wifi 网络,我将获得EHOSTUNREACH用于由 tcp 客户端应用程序或任何其他连接到我的服务器 IP 的所有连接任何端口(例如浏览器)上的应用程序,但仅限于发生错误的 wifi 网络中。
如果我更改为 3g 或另一个 wifi 网络,则不会返回错误,但如果我回到那个 wifi 网络,我会再次收到错误。并保持这种状态,直到我重新启动手机或将 android 单独放置约 30 分钟。
这仅在某些网络中发生,并且仅在数据发送周期很小的情况下,每次更改时都会发生 1 秒或更短的间隔,如果周期为 5 秒,则每 4-5 次更改发生 1 次。
客户端代码如下
private void test(){
AsyncTask<Void,Void,Void> task = new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
int iterador = 0;
try {
if(mSocket!= null && !mSocket.isClosed()){
mSocket.close();
}
if(waitNetworkConnection(getApplicationContext(), 3))
{
mSocket = new Socket(Proxy.NO_PROXY);
mSocket.setReuseAddress(false);
mSocket.setSoTimeout(100);
mSocket.setTcpNoDelay(false);//false seems to work better
mSocket.setKeepAlive(false);
mSocket.setSoLinger(false, 5);
mSocket.setOOBInline(false);
mSocket.connect(new InetSocketAddress("xxx.xxx.xxx", 5000));
PrintWriter out;
OutputStream stream = mSocket.getOutputStream();
OutputStreamWriter outputStream = new OutputStreamWriter(stream);
BufferedWriter buff = new BufferedWriter(outputStream);
out = new PrintWriter(buff, true);
out.println("hello");
out.println("world");
Date date;
do {
try {
Thread.sleep(200);
if(out.checkError() || mSocket.isOutputShutdown())
{
System.out.println("some error has happed let's close the socket");
try {
mSocket.shutdownOutput();
}
catch(Exception e){}
try {
mSocket.shutdownInput();
}
catch(Exception e){}
try {
mSocket.close();
}
catch(Exception e){}
}else
{
if(waitNetworkConnection(getApplicationContext(), 3))
{
if(iterador > 10*5)
{
Thread.sleep(5000);
}
date = new Date();
String msg = "["+String.valueOf(iterador)+"] keeping connection at "+date.toString();
System.out.println("Sending: " + msg);
out.println(msg);
out.flush();
iterador++;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}while(!mSocket.isClosed());
}
}
catch(Exception se){
String err = se.getMessage();
if(null == err){
err = se.toString();
}
System.out.println(err);
}
System.out.println("Socket closed");
new Handler(getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
test();
}
}, 5000);
return null;
}
};
task.execute();
}
public static boolean waitNetworkConnection(Context context, int retries) throws InterruptedException {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = getNetworkToTest(cm);
if (ni == null || !ni.isConnected()) {
// sleep a short while to allow system to switch connecting networks.
Thread.sleep(1000);
int counter = 0;
while (counter < retries && (ni == null || (ni.isAvailable() &&
!ni.isConnected()))) {
Thread.sleep(500);
ni = getNetworkToTest(cm);
counter++;
}
}
return (cm.getActiveNetworkInfo() != null &&
cm.getActiveNetworkInfo().isConnected());
}
private static NetworkInfo getNetworkToTest(ConnectivityManager cm) {
NetworkInfo[] nis = cm.getAllNetworkInfo();
NetworkInfo ni = cm.getActiveNetworkInfo();
for (int i = 0; i < nis.length; i++) {
if (nis[i].getType() == ConnectivityManager.TYPE_WIFI && nis[i].isAvailable()) {
ni = nis[i];
return(ni);
}
}
return(ni);
}
这个测试是在三星 Galaxy S3 (android 4.1.2) 和三星 Galaxy S2 (android 4.1.2) 上进行的。发生这种情况的路由器的一个网络是 D-link 802.11g/2.4GHz