ネットワークプログラミング

解決


とら  2003-11-10 20:59:22  No: 52432  IP: [192.*.*.*]

はじめまして、とらです。
ネットワークプログラミングについての質問です。

下のとき(送信 : 76 * 23 = 1,748)はうまくいくのですが、
PrtScSend関数の4行目 : while( m_i <= 23 )
それ以上(送信 : 76 * 46 = 3,496)送信しようとするとうまくいきません。
PrtScSend関数の4行目 : while( m_i <= 46 )

パケットアナライザで、パケットをみてみると
うまくいっているときは(送信 : 76 * 23 = 1,748)、
サーバーにすべてのパケットを送信し終わってから
サーバーからクライアントにACKが返されているのですが、

クライアント → サーバー
ACK : 1,460
PSH , ACK : 440
サーバー → クライアント
ACK

うまくいっていないときは(送信 : 76 * 46 = 3,496)、
サーバーにすべてのパケットを送信し終わるまえに
サーバーからクライアントにACKが返されてしまいます。 

クライアント → サーバー
ACK : 1,460
ACK : 1,460
サーバー → クライアント
ACK
クライアント → サーバー
PSH , ACK : 728
サーバー → クライアント
ACK

今うまくいっていないものも、
サーバーにすべてのパケットを送信し終わってから
サーバーからクライアントにACKが返されるようにしたいのですが、
(そこがうまくいっていない理由と考えているので)
どのようにすればいいのか、アドバイスを頂ければと思います。


//////////////////////////////////////////////////////CEkssSock

void CEkssSock::OnSend(int nErrorCode)
{
  //エラーが発生していない場合
  if( nErrorCode == 0 ){
    //CEkssDlg::OnSend関数を呼び出す
    ( ( CEkssDlg* ) m_pdDlgWnd ) -> OnSend();
  }
}

void CEkssSock::OnReceive(int nErrorCode)
{
  //エラーが発生していない場合
  if( nErrorCode == 0 ){
    //CEkssDlg::OnReceive関数を呼び出す
    ( ( CEkssDlg* ) m_pdDlgWnd ) -> OnReceive();
  }
}

//////////////////////////////////////////////////////CEkssDlg

void CEkssDlg::OnTimer(UINT nIDEvent) 
{
  if( nIDEvent == 1 ){
    //PrtScSend関数を呼び出す
    PrtScSend();
  }
  
  CDialog::OnTimer(nIDEvent);
}

void CEkssDlg::OnSend()
{
  //クライアントの場合 : m_iCS == 0
  if( m_iCS == 0 ){
    //SetTimer関数を呼び出す
    SetTimer( 1 , 10000 , NULL );
  }
}

void CEkssDlg::OnReceive()
{
  //サーバーの場合 : m_iCS == 1
  if( m_iCS == 1 ){
    //PrtScReceive関数を呼び出す
    PrtScReceive();
  }
}

void CEkssDlg::PrtScSend()
{
  CString sPS = "abcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789"; //Length=76
  while( m_i <= 23 ){
    sPS.Insert( sPS.GetLength() , "abcdefghijklmnopqrstuvwxyz01234567890123456789012345678901234567890123456789" );
    m_i = m_i + 1;
  }
  int iPSLength = sPS.GetLength();
  LPBYTE pbPS = ( LPBYTE ) malloc( iPSLength );
  CopyMemory( pbPS, sPS , iPSLength );
  
  int iSend = m_EsConnectSock.Send( pbPS , iPSLength );
  if( iSend == SOCKET_ERROR ){
  }
  else{
    m_i = 0;
  }
}

void CEkssDlg::PrtScReceive()
{
  int iPSLength = 1048576;
  char *pcPS = new char[iPSLength];
  Sleep( 1000 );
  
  int iReceive = m_EsConnectSock.Receive( pcPS , iPSLength );
  if( iReceive == SOCKET_ERROR ){
  }
  else{
    pcPS[iReceive] = NULL;
  }
  
  MessageBox( pcPS );
}

//////////////////////////////////////////////////////結果

クライアント → サーバー
送信 : 76 * 23 = 1,748

クライアント → サーバー
ACK : 1,460
PSH , ACK : 440
サーバー → クライアント
ACK

1460 + 440 = 1,900

編集 削除
YuO  2003-11-10 22:22:20  No: 52433  IP: [192.*.*.*]

CAsyncSocket::Send(というか,WinSockのsend)は,
指定したバイト数全てを送信する,という仕様ではないです。
ちゃんとsendの戻り値(=送信したバイト数)を吟味して処理する必要があります。
#というわけで,実質的にsendはループ中でのみ使われることになる。

編集 削除
とら  2003-11-10 23:06:59  No: 52434  IP: [192.*.*.*]

YuOさん、アドバイスありがとうございます。

この文章を読むと、
Receive()に問題があるのではなく、
(私が考えていた問題)
Send()の方に問題があるのですか?

これから、
Send()の戻り値は、iSendなので、
指定したバイト(iPSLength)と戻り値(iSend)が
等しくなっているかをしっかり判断する文を加えてみたいと思います。

しかし、パケットアナライザでパケットをみたときには、
すべてのデータをしっかり送っていたような気がします。
もう一度、パケットアナライザでしっかり確認してみたいと思います。

YuOさん、本当にアドバイスありがとうございました。これかもよろしくお願いします。

とら

編集 削除
とら  2003-11-10 23:29:35  No: 52435  IP: [192.*.*.*]

とらです。
下のようなif文を加えてみましたが、
すべてのデータを送信しているみたいです。
(エラーのメッセージが表示されないので)

if( iSend != iPSLength ){
      MessageBox( "すべてのパケットが送信されていません。" , "OnSend関数 Error" );
    }

パケットアナライザでも確認してみましたが、
すべてのデータを送信していることを確認しました。

おそらく、YuOさんのアドバイスを間違って認識していると思うので、
どの部分を、誤認しているのかを教えていただければと思います。

編集 削除
YuO  2003-11-10 23:43:04  No: 52436  IP: [192.*.*.*]

ちゃんと送られているなら,受信側の問題だと思います。
#なんでReceiveに問題があると思うのならそのヘルプを見ないかなぁ……。

SocketでTCP/IPを利用した場合,要求したバイト数分の送受信が行われる保証はありません。
send/recvの戻り値をちゃんと使って下さい。

編集 削除
とら  2003-11-11 00:15:18  No: 52437  IP: [192.*.*.*]

YuOさん、何度もありがとうございます。

私は、今うまくいっていないもの(送信 : 76 * 46 = 3,496)も
下のようになると考えていたのですが、

クライアント → サーバー
ACK : 1,460
ACK : 1,460
PSH , ACK : 728
サーバー → クライアント
ACK

実際のパケットをみてみると違っていたので、
うまくいってない理由は、
サーバーにすべてのパケットを送信し終わるまえに
サーバーからクライアントにACKが返されてしまっていることだと考えました。
よって、Receive()に問題があると私は考えました。

説明が足りなくて、本当に申し訳なく思っています。これかもよろしくお願いします。

とら

編集 削除
b  2003-11-11 10:34:13  No: 52438  IP: [192.*.*.*]

こんにちは。
YuOさんも書いていますが、送信されたデータが一度のReceiveで受信
出来るとは限らないのではないかと。
戻り値で何バイト受信したか調べて希望のバイト数になるまでループを
させるなりしてみてはどうでしょう。

編集 削除
とら  2003-11-11 11:41:31  No: 52439  IP: [192.*.*.*]

bさん、こんにちは。アドバイスありがとうございます。
YuOさんの書かれている”戻り値をしっかり使ってください”というのは、
そういう意味だったのですね。
そのことを考えて、下のようにしてみました。

  iL = 0;
  iReceive = 0;
  while( iL != iPSLength ){
    iReceive = m_EsConnectSock.Receive( pcPS + iL , iPSLength -iL );
    if( iReceive == SOCKET_ERROR ){
    }
    else{
      iL = iL + iReceive;
    }
  }
  pcPS[iPSLength] = NULL;

Receive()を何度も呼び出しているのですが、これであっているのでしょうか?

bさん、アドバイスありがとうございました。これかもよろしくお願いします。

とら

編集 削除
b  2003-11-12 01:28:58  No: 52440  IP: [192.*.*.*]

特に問題ないように思えます。(やってみるのが一番ですね)
とはいっても私もそれほど経験があるわけではないので確証は
ありませんが。。すいません。

正常系でのテストが完了したら、エラーテスト(ケーブル抜いたり
環境悪い所での通信)を行ったりして、正常に復帰が行えるかを
検証してみるとよいかと思います。
多分そっちのテスト&処理のほうがめんどくさいかもしれません。

編集 削除
とら  2003-11-13 22:09:32  No: 52441  IP: [192.*.*.*]

とらです。
PrtScReceive()と同じようにPrtScSend()も
書き換えてやったらうまくいきました。

YuOさん、bさん、アドバイスありがとうございました。

編集 削除