Androidのソケット通信をCで書いてみた(バグフィックス)
以前書いた「Androidのソケット通信をCで書いてみた」のconnectOpen関連にバグがあったので書き直しました。
sock.nconnectOpenコマンド
nconnectOpenコマンドはIPアドレスを指定してポートに接続するコマンドです。ソースは下記です。LuaコマンドのTimeOut引数は省略されたときに5secになります。通信の番号は1か2としているので、第一引数に応じてSockNum配列の要素をセットします。それが既にコネクトしているソケットであれば閉じます。
そして、コネクトしてソケットをオープンするスレッドを起動します。このスレッドが起動したことを確認した後、指定の秒数待ちます。その後、コネクトスレッドのstatus変数を確認して、コネクトに成功したか失敗したかを判断します。
以前のソースでは、タイムアウト時間を設定しても、即コネクト失敗となり接続しないで処理が戻ってきていました。これを指定秒数の間待つように修正しました。
下記のsocknConnectOpen(lua_State *LuaLinkP)は、ほとんど変更していません。タイムアウトの秒数を設定の+1secにしたくらいです。これはスレッド内で待ち受け処理をしているのですが、もし、ループから抜けてこないときに、メインスレッドで切れるようにするためです。
//************************************************** // 2-9--.tcpソケット接続を行います: sock.nconnectOpen // sock.nconnectOpen( Number, Address, Port [,TimeOut] ) // Numberは、ソケット番号: 2つまでソケットを使うことができます。 // Addressは、ホストアドレス // Portは、ポート番号 // TimeOutは、タイムアウト(s) // 戻り値 // 0:接続できませんでした // 1:接続できました //************************************************** int socknConnectOpen( lua_State *LuaLinkP ) { struct ThOpLs ol[1]; int rtn; unsigned long tim = 5; //デフォルトの待ち時間 5s struct timespec treq, trem; long i; lua_Number lnum; int n = lua_gettop( LuaLinkP ); if( n<3 ){ LuaErrorMes( LuaLinkP, (char*)"sock.nconnectOpen" ); return( 1 ); //戻り値は1つですよ。 } //Luaのソケット番号セット if( lua_tonumber( LuaLinkP, 1 )<=1 ){ ol[0].num = 0; } else{ ol[0].num = 1; } if( SockNum[ol[0].num]!=-1 ){ //ソケットcloseします LuaSocketClose(SockNum[ol[0].num]); SockNum[ol[0].num] = -1; } //ポート番号セット ol[0].port = (unsigned long)lua_tonumber( LuaLinkP, 3 ); //ホスト名orIPアドレスセット ol[0].hostname = (char*)luaL_checklstring( LuaLinkP, 2, NULL ); //タイムアウト秒数のセット ol[0].waittime = tim; if( n>3 ){ tim = (unsigned long)lua_tonumber( LuaLinkP, 4 ); ol[0].waittime = (unsigned long)lua_tonumber( LuaLinkP, 4 ); } sem_init(&ol[0].sync, 0, 0); //syncセマフォの初期化 //pThreadを生成する rtn = pthread_create(&ol[0].th, NULL, thread_SockOpen, (void *) (&ol[0])); if (rtn != 0) { lua_settop(LuaLinkP, 0); //スタックのクリア lnum = 0; lua_pushnumber( LuaLinkP, lnum ); //1番目の戻り値 return( 1 ); } //pThreadが起動するまで待つ sem_wait(&ol[0].sync); //待ち時間の設定 treq.tv_sec = (time_t)0; treq.tv_nsec = 25000000; //25ms //最大指定の秒数待つ long il = (tim+1)*40; //設定よりも1sec多く待つようにする for(i=0; i<il; i++){ nanosleep(&treq, &trem); if( ol[0].status!=1 ){ break; } } //1なら終了していない lua_settop(LuaLinkP, 0); if( ol[0].status==1 ){ //デタッチしてスレッドを切れるようにする pthread_detach(ol[0].th); treq.tv_sec = (time_t)0; treq.tv_nsec = 20000000; //20ms nanosleep(&treq, &trem); //20ms待つ //SockNumの初期化 SockNum[ol[0].num] = -1; lnum = 0; } else if( ol[0].status==2 ){ //失敗で戻ってきている lnum = 0; } else{ lnum = 1; } lua_pushnumber( LuaLinkP, lnum ); //1番目の戻り値 return( 1 ); }
thread_SockOpen(void *thr)の修正
ソケットをOpenするスレッド内で、タイムアウトまで接続を試みるようにプログラムを修正しました。秒数しかチェックしていないので、正確な秒数をWaitしないかも知れませんが、そこは多めにみてください。
//************************************************************************* // ソケットポートのオープンスレッド // priv->status = 1:コネクト中, 2:失敗, 0:成功 //************************************************************************* void *thread_SockOpen(void *thr) { struct ThOpLs *priv = (struct ThOpLs *)thr; struct hostent *accessIP; //接続先IP int ret; struct timespec treq, trem; struct timeval tv_cmp; priv->status = 1; //コネクト中フラグを立てておく sem_post(&priv->sync); //親プロセスに起動したことを知らせる。 SockNum[priv->num] = socket( AF_INET, SOCK_STREAM, 0 ); //接続用ソケットの生成 if( SockNum[priv->num]&0x80000000 ){ SockNum[priv->num] = -1; priv->status = 2; return (void *) NULL; } // サーバのIPアドレスを検索します accessIP = gethostbyname( priv->hostname ); Saddr[priv->num].sin_family = AF_INET; Saddr[priv->num].sin_addr.s_addr = *( (unsigned long *)(accessIP->h_addr_list[0]) ); Saddr[priv->num].sin_port = htons( priv->port ); //ポート設定 //待ち時間の設定 treq.tv_sec = (time_t)0; treq.tv_nsec = 25000000; //25ms gettimeofday(&tv_cmp, NULL); time_t tsec = tv_cmp.tv_sec + (time_t)priv->waittime; while( 1 ){ ret = connect(SockNum[priv->num], (struct sockaddr *)&Saddr[priv->num], sizeof(Saddr[priv->num])); if( ret==0 ){ break; } nanosleep(&treq, &trem); //25ms待つ gettimeofday(&tv_cmp, NULL); if( tsec<= tv_cmp.tv_sec ){ break; } } if( ret!=0 ){ close( SockNum[priv->num] ); SockNum[priv->num] = -1; priv->status = 2; return (void *) NULL; } //成功 priv->status = 0; return (void *) NULL; }