Ip に接続し、hello worldを送信し、定期的に datetime を送信する小さな Android テスト tcp クライアントを作成しました。
これをテストするために、次の方法でnetcatをサーバーとして使用しています。
nc -l 5000
クライアントはソケットを介してサーバーに接続し、wifi または 3g ネットワークを使用してnetcat画面に datetime が正しく表示されます。
アプリを 3g で起動し、アプリから日時を送信したまましばらくの間 (1 秒間隔)、Wi-Fi ネットワークに変更すると、サーバー IP へのすべての接続に対してEHOSTUNREACHが取得され、TCP クライアント アプリまたはその他のアプリによって作成されます。 app を任意のポート (ブラウザなど) で使用できますが、エラーが発生した Wi-Fi ネットワークでのみ使用できます。
3g または別の Wi-Fi ネットワークに変更すると、エラーは返されませんが、その Wi-Fi ネットワークに戻ると、再度エラーが発生します。そして、電話を再起動するか、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);
}
これは、Samsumg galaxy S3 (android 4.1.2) および Samsung Galaxy S2 (android 4.1.2) で行われたテストです。ルーターでこれが発生するネットワークの 1 つは、D リンク 802.11g/2.4GHz です。