64bitプロセスの4バイト境界でselect()が使えない

[戻る]
たま 2012/02/08(水) 15:45:17
よろしくお願いします。

環境は、
Windows 7 Pro 64bit
Visual Studio 2010 Pro (VC++)
を使用しております。

64ビット用のプロセスでSocket通信を作成しておりますが、
構造体メンバーのアライメントを4バイトで作成すると、
select()で失敗するようになってしまいます。
※もしかしたら、select()失敗の原因は、FD_ZEROもしくはFD_SET?(憶測)

「構造体メンバーのアライメントを4バイトにしたまま」で、
selectを使用する方法。または、select()の代わりとなる関数を使用する方法。
をご存知の方がいましたら教えていただけないでしょうか?

下記に簡単なコードを記述させて頂きます。
------------------------------------------------------------------
  #include <winsock2.h>

  unsigned short    port_no = 8050;    // ポート番号
  int               rc;                // リターンコード
  WSADATA           wWsaData;          // WinSock情報
  SOCKET            m_MySocket;        // ソケット
  SOCKADDR_IN       m_SockAddr;        // ソケット情報
  int               SockAddrSize;      // ソケットサイズ
  fd_set            fd;
  timeval           timeout;
  int               rtc;

  // WinSockの初期化
  if( WSAStartup( MAKEWORD(2,0), &wWsaData ) != 0 ){
      rc = -1;
  }

  // ソケット生成
  if( (m_MySocket = socket( PF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET ){
      rc = -2;
  }

  // Bind
  m_SockAddr.sin_family = PF_INET;
  m_SockAddr.sin_port = htons(port_no);
  m_SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  memset( m_SockAddr.sin_zero, 0x00, sizeof(m_SockAddr.sin_zero) );

  if( bind( m_MySocket, (struct sockaddr *)&m_SockAddr, sizeof(m_SockAddr) ) != 0 ){
      rc = -3;
  }

  // Listen
  if( listen( m_MySocket, 5 ) != 0 ){
      rc = -4;
  }

  // タイムアウト設定
  FD_ZERO( &fd );
  FD_SET( m_MySocket, &fd );
  timeout.tv_sec = 0;
  timeout.tv_usec = 500;

  // ここのselect()が-1を返します
  rtc = select( 1, &fd, NULL, NULL, &timeout );

  // エラーコードは10038が返ってきます
  // エラーコードの内容
  // ソケット以外に対して操作が試行されました。
  // ソケット ハンドル パラメータの参照先のソケットが有効ではないか、
  // fd_set のメンバが有効ではありません。
  rtc = WSAGetLastError();
-------------------------------------------------------------------

仲澤@失業者 2012/02/08(水) 18:18:43
>rtc = select( 1, &fd, NULL, NULL, &timeout );
rtc = select( m_MySocket + 1, &fd, NULL, NULL, &timeout );
ではないのですか。そ〜いう簡単な問題じゃないか(vv;)。

たま 2012/02/08(水) 18:34:40
仲澤@失業者さん

御指摘ありがとうございます。
第1引数は無視される仕様だったと思いますが、
お教え頂いた内容で修正して動作させてみました。
結果は、やはりselect()で-1を返してしまいます。

仲澤@失業者 2012/02/09(木) 10:06:26
そうなると本格的に
#pragma pack( push, 4)
  〜 
#pragma pack( pop)
の問題となり、WinSockを含めて、外部ライブラリの定義に対して
上のパックを「かけている」可能性があります。
当然ですが、それはあってはならないことです。
この認識は大丈夫ですか?。

たま 2012/02/09(木) 11:21:01
仲澤@失業者さん

御指摘ありがとうございます。
>#pragma pack( push, 4)
>  〜 
>#pragma pack( pop)
>の問題となり、WinSockを含めて、外部ライブラリの定義に対して
>上のパックを「かけている」可能性があります。
>当然ですが、それはあってはならないことです。
>この認識は大丈夫ですか?。
構造体メンバーのアライメントに関しては、
ソースコードに#pragma packを使用しておらず、
VisualStudio2010開発環境(プロジェクト)設定の、
コンパイラオプション(/Zp)で設定しております。
【参考URL】
http://msdn.microsoft.com/ja-jp/library/xh3e3fd0.aspx

仲澤@失業者 2012/02/09(木) 12:44:37
>コンパイラオプション(/Zp)で設定しております。

その方法では、自前のソース以外の、DLL、ライブラリを使用する
ことはできません。それらの外部のバイナリ内の各オブジェクトの
オフセット位置は、それらがコンパイルされたときに決定されています。
つまり出荷時に固定されているわけです。
Zpを指定したからと言って、それらのあらためてコンパイルされない
DLLやLibのアライメントが調整されるわけではありません。

Zpが安全に使える場合とは「一切の外部ライブラリとソースを使用しないで、
自前のコードのみをコンパイルするプロジェクト」だけです。

まず、Zpを外し、自分のコードの必要な部分だけに#pragma(・・・)
しましょう。
この方法が、Windowsアプリを作成する場合の唯一のパック方法
となります。

ITO 2012/02/09(木) 13:00:58
気になっているところが、
 64BIT版って、ヘッダーファイルのディレクトリーが別になっている場合って
ありませんでしたっけ?
 「winsock2.h」なんてありそうですよね?

たま 2012/02/09(木) 14:17:53
[[解決]]
仲澤@失業者さん

御教授ありがとうございます。

>Zpが安全に使える場合とは「一切の外部ライブラリとソースを使用しないで、
>自前のコードのみをコンパイルするプロジェクト」だけです。
Zpの使用はかなり制限される事になるのですね。

>まず、Zpを外し、自分のコードの必要な部分だけに#pragma(・・・)
>しましょう。
>この方法が、Windowsアプリを作成する場合の唯一のパック方法
>となります。
承知いたしました。
上記方法(#pragma)で対処していく事にしようと思います。

詳しく丁寧にお答え頂き、ありがとうございました。

ITOさん

御教授ありがとうございます。

>気になっているところが、
>64BIT版って、ヘッダーファイルのディレクトリーが別になっている場合って
>ありませんでしたっけ?
>「winsock2.h」なんてありそうですよね?
ヘッダーファイルによっては別になっている事があるのでしょうか。
「winsock2.h」が入っているディレクトリーは一箇所しか見つける事ができませんでした。

仲澤@失業者 2012/02/09(木) 14:56:42
ヘッダーは#ifdefで切り分けのようです(_WIN64とか)。
Libは当然分かれてますけど。

Winsock2.hは、当たり前ですが独自に#pragma pack()はしません。
アライメントはデフォルトの8Byte(だったかな)に固定のはず。

そもそも何のために4Byteにしたいのかが不明ですが。
一般にそれが必要になるのは、ストリーム(ファイル、通信)などの
データが、そうなっている場合などに限られるはずで、その場合でも
通信や保管読み込み対象のstructをパックするのが普通です。
つまり、一般に、Zpを使う機会はまったくないと断言できます。

古いスレッドには追加発言できません。