はじめまして、とらです。
ネットワークプログラミングについての質問です。
下のとき(送信 : 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
CAsyncSocket::Send(というか,WinSockのsend)は,
指定したバイト数全てを送信する,という仕様ではないです。
ちゃんとsendの戻り値(=送信したバイト数)を吟味して処理する必要があります。
#というわけで,実質的にsendはループ中でのみ使われることになる。
YuOさん、アドバイスありがとうございます。
この文章を読むと、
Receive()に問題があるのではなく、
(私が考えていた問題)
Send()の方に問題があるのですか?
これから、
Send()の戻り値は、iSendなので、
指定したバイト(iPSLength)と戻り値(iSend)が
等しくなっているかをしっかり判断する文を加えてみたいと思います。
しかし、パケットアナライザでパケットをみたときには、
すべてのデータをしっかり送っていたような気がします。
もう一度、パケットアナライザでしっかり確認してみたいと思います。
YuOさん、本当にアドバイスありがとうございました。これかもよろしくお願いします。
とら
とらです。
下のようなif文を加えてみましたが、
すべてのデータを送信しているみたいです。
(エラーのメッセージが表示されないので)
if( iSend != iPSLength ){
MessageBox( "すべてのパケットが送信されていません。" , "OnSend関数 Error" );
}
パケットアナライザでも確認してみましたが、
すべてのデータを送信していることを確認しました。
おそらく、YuOさんのアドバイスを間違って認識していると思うので、
どの部分を、誤認しているのかを教えていただければと思います。
ちゃんと送られているなら,受信側の問題だと思います。
#なんでReceiveに問題があると思うのならそのヘルプを見ないかなぁ……。
SocketでTCP/IPを利用した場合,要求したバイト数分の送受信が行われる保証はありません。
send/recvの戻り値をちゃんと使って下さい。
YuOさん、何度もありがとうございます。
私は、今うまくいっていないもの(送信 : 76 * 46 = 3,496)も
下のようになると考えていたのですが、
クライアント → サーバー
ACK : 1,460
ACK : 1,460
PSH , ACK : 728
サーバー → クライアント
ACK
実際のパケットをみてみると違っていたので、
うまくいってない理由は、
サーバーにすべてのパケットを送信し終わるまえに
サーバーからクライアントにACKが返されてしまっていることだと考えました。
よって、Receive()に問題があると私は考えました。
説明が足りなくて、本当に申し訳なく思っています。これかもよろしくお願いします。
とら
こんにちは。
YuOさんも書いていますが、送信されたデータが一度のReceiveで受信
出来るとは限らないのではないかと。
戻り値で何バイト受信したか調べて希望のバイト数になるまでループを
させるなりしてみてはどうでしょう。
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さん、アドバイスありがとうございました。これかもよろしくお願いします。
とら
特に問題ないように思えます。(やってみるのが一番ですね)
とはいっても私もそれほど経験があるわけではないので確証は
ありませんが。。すいません。
正常系でのテストが完了したら、エラーテスト(ケーブル抜いたり
環境悪い所での通信)を行ったりして、正常に復帰が行えるかを
検証してみるとよいかと思います。
多分そっちのテスト&処理のほうがめんどくさいかもしれません。
とらです。
PrtScReceive()と同じようにPrtScSend()も
書き換えてやったらうまくいきました。
YuOさん、bさん、アドバイスありがとうございました。