コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

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;
}