• 締切済み

Winsockでの送受信についての質問

本日はじめてOKwaveを利用させていただきます、Nimameです。 以後よろしくお願いします。 本日はwinsockの送受信について質問させていただきたく、投稿しました。 現在winsockを利用したS/Cのネットワークプログラムを組んでいるのですが、 送受信の時、同PC内だとうまくいき、外部PCからだとうまくいかずに困っています。 送受信の際(recv, send)の後にSleep(10)を入れるとうまくいくことから ・パケットが最後まで送信しきれていない ・パケットが最後まで受信しきれていない の以上が原因かと考えています。 そこでサイズ分最後まで送受信をする関数を用意したのですが、 これがどうにもうまく働いていないようでやはりうまくいきません。 -------------------------------------------------------- // 最後まで送りきる int Send(SOCKET s, char *buf, int len) { int endsize; int r; char* sendptr = buf; // 送信する先頭アドレスを取得 // 確実に全てのパケットを送信する while(endsize < len) { r = send(s, sendptr, len - endsize, 0); // 送信結果がエラーなら終了 if( r <= 0 ) return r; sendptr += r; endsize+= r; } sendptr = NULL; return endsize; } -------------------------------------------------------- // 最後まで受信する int Recv(SOCKET s, char *buf, int len) { // 受信データアドレスの設定 char* recvptr = buf; int endsize= 0; // 受信データを1つぶん読み込むまでループする while( endsize < len ) { // 未受信データアドレスに実際にデータを読み込む int r = recv(s, recvptr, len-endsize, 0); // エラーだったら終了する if( r <= 0 ) return r; recvptr += r; endsize+= r; } strext(buf, buf, 0, len); recvptr = NULL; return endsize; } -------------------------------------------------------- 以上なのですが、おかしな点や、改善点などありましたら お教えいただけたら幸です。

みんなの回答

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.14

遅くなりました。 >互いにWindowsXPですが。 SPが違うとかいうレベルでしょうか。。。 いずれにしても今回とはあまり関連性が 見えてこないですね; >まったく同じ環境、構造体です。 であれば、パディングの問題でもなさそうですが、 通信手段について、4バイトのヘッダがあると おっしゃっていますが、下で既に出ている以外の 構造体を使用されているということでしょうか? あとは、通信経路に少し特殊な処理系があり、 ネットワークバイトオーダの変換などをしてないとかでしょうか。。 サイズなどの数値を送る場合にバイトオーダの変換はされてますか? # 実際に何が起こっているのかが # 把握できていないのでこれ以上だと # 何でも指摘してやってみないと # どうしようも無いかもしれません。。。;

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.13

> char text[32]; です。 であれば、 >>格納されているデータはぶつ切りになる は起こりません。構造体の中にtextの サイズも内包されますので、 sizeof(Data)で問題ありません。 >>外部PCからだと 外部PCはサーバ側で使用してる環境と まったく同じなのでしょうか? 念のための確認ですが、 送受信側の構造体は同じものを使っていて、 同じ環境でコンパイルされているのですよね??

nimameMMM
質問者

補足

遅れて申し訳ありません。 >>外部PCはサーバ側で使用してる環境と まったくおなじではありません。 互いにWindowsXPですが。 >>送受信側の構造体は同じものを使っていて、 >>同じ環境でコンパイルされているのですよね?? まったく同じ環境、構造体です。 アライメントについても同様です。

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.12

> struct Data{ > int i; > short char text; > }pData; >strcpy(pData.text, "HELLO"); short char ??って型がよく分かりませんが、 上記であればそもそも、送信側のtextに領域がありません。 文字列などを格納する領域が明らかに不正でしょう。 >Send(socket, (char *)&pData, sizeof(Data)); さらに、これでは構造体分のサイズしか送られません。 最近の環境ですと、intが4byte + short であれば2byte. charであれば1byte分のサイズのみしか送られず、 格納されているデータはぶつ切りになるでしょう。 もしも、textがポインタであるならば、 それ以前の問題ですが。。。

nimameMMM
質問者

お礼

*追加 short char って・・・。 すみません。charの間違いです。

nimameMMM
質問者

補足

>short char text; 誤字を指摘されつつまたも; short char text[32]; です。 >Send(socket, (char *)&pData, sizeof(Data)); >さらに、これでは構造体分のサイズしか送られません。 そうなのですか? 以前はこの方法で無事送受信でき、 ローカル環境であれば無事に動作することから、 sizeof(Data)に特に問題がないようには思えますが・・・。 問題点と改善があればおねがいします。

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.11

> ヘッダー(4byte)は無事受信、その後のデータ受信にて ヘッダの4byteってその後のデータのサイズということでしょうか? > printf("%s\n", buf); 最初の投稿で載せられているコードを使っているのだと思いますが 実際にはこのbufはどのように取得された物なのでしょうか? # 単純に文字終端入れてないだけとかじゃ。。。? 例えば、以下のようになっていませんか? > int Send(SOCKET s, char *buf, int len) char Hoge[] = "Hogehoge"; Send( sock, Hoge, strlen(Hoge)); ~~~~~~~~~~~~ > int Recv(SOCKET s, char *buf, int len) char buf[200]; Recv( sock, buf, sizeof(buf)); この場合受信後の格納先であるbufには'\0'終端が無いはずなので、 そのままprintfなどで表示すると、バッファーオバーランします。

nimameMMM
質問者

補足

ヘッダー4byteはその後受信するデータタイプを格納しています。 それによって受信サイズをきめ、受信しております。 データ本体の受信には構造体を利用しています。 struct Data { int i; short char text; }pData; strcpy(pData.text, "HELLO"); Send(socket, (char *)&pData, sizeof(Data)); ----------------------------------------------- Recv(socket, (char *)&pData, sizeof(Data)); printf("%s\n", pData->text);

  • Kennis
  • ベストアンサー率100% (1/1)
回答No.10

映し出しミスだと思いますが、Send関数のendsize変数を初期化する際0を代入していないのは大丈夫ですか?

nimameMMM
質問者

お礼

おはようございます。 映し出しミスのようです; 以後誤字に気をつけたいと思います。 御指摘、ありがとうございます。

  • Yanch
  • ベストアンサー率50% (114/225)
回答No.9

> WSAGetLastError()は非同期型にしているためWSAEWOULDBLOCKを返しているようです。 > 一応取得するようにはしましたが、以前変化はないようです。 WSAEWOULDBLOCK を戻しているのがわかったなら、 WSAEWOULDBLOCK に対して適切な処理を実装する必要がありますよ。 例えば、recv()可能なパケットが届くまで待ってみるとか。 例えば、timeout処理を作ってみるとか。 等です。

nimameMMM
質問者

補足

おはようございます。今朝も寒いです。 昨日(本日)追加でかきこんだ分が反映されていなかったようで。 内部で接続ではWSAEWOULDBLOCKが発生したのですが、 外部の方に接続をしてもらったところWSAECONNRESETが発生しているようです。 始めの1度の通信(CからSへの送信)以降におきているようですが・・。 一応クライアントでは特に接続先を変えるようなアクションはしていません。

  • Yanch
  • ベストアンサー率50% (114/225)
回答No.8

nonBloking モードで通信をしているようですが、 通信の内容が、遅れて届くのは、TCP 通信によるDELAY(遅延) が原因だったりしませんか? TCP通信では、転送速度を最適化するために、ネーグルアルゴリズム と言うのを採用していますから、データ転送には、DELAYが発生したりします。 もし、転送速度よりも、応答速度を優先するのでしたら、ネーグルアルゴリズム の無効化も試してみては如何でしょう。 他には、recv()関数が、SOCKET_ERRORを戻した場合には、 WSAGetLastError()を使って、エラー処理しましょう。 多分ですが、recv()関数が、SOCKET_ERRORを戻した場合に、 WSAGetLastError()が何を戻しているのか、デバッグ表示してみると、 解決のヒントがあるのではないかと思います。

nimameMMM
質問者

補足

ご返答ありがとうございます。 WSAGetLastError()は非同期型にしているためWSAEWOULDBLOCKを返しているようです。 一応取得するようにはしましたが、以前変化はないようです。 ネーグルアルゴリズムの無効化ですか。 いろいろと調べてみます。

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.7

何度もすみません。。 > send関数も同様に、正常切断に対しては 0 、 > 異常切断に対しては -1 のいずれかを返却します。 と書きましたが上記は誤りです。 sendは非同期の場合0を許容しているようですね。。。 Winsock Programing QAでダウンロード可能な リファレンス及び、MSDNに記載がありました。 # http://msdn.microsoft.com/en-us/library/ms740149(VS.85).aspx # http://www.sockets.com/winsock2.htm#Docs リファレンスには、送信したバイト数、 またはSOCKET_ERRORを返すと記述されていました。 # なので 0を返す記述はありませんでしたので、 # 0 は送信したバイト数に含まれる?ものと思われます。 >No3さん 大変、失礼しました。

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.6

もうひとつ一番重要かも。。。 この通信のプロトコルは何ですか? できれば、ソケット生成のコードなど、 もう少し全体的にコードを載せると、 早く解決できるかもしれません。

nimameMMM
質問者

お礼

通信のプロトコルにはTCPを利用しています。 ソケット生成のコードですね。 ------------------------------------------------- // サーバー(自身) int Init() { // Winsockの設定 WSADATA data; WSAStartup(MAKEWORD(2,0), &data); // ソケットの生成 srvSocket = socket(AF_INET, SOCK_STREAM, 0); if (srvSocket == INVALID_SOCKET) { printf("socket failed\n"); return 1; } // Addr構造体のセット srvAddr.sin_family = AF_INET; srvAddr.sin_port = htons(PORT); srvAddr.sin_addr.S_un.S_addr = INADDR_ANY; dstAddrSize = sizeof(dstAddr); // ソケットのバインド bind(srvSocket, (struct sockaddr *) &srvAddr, sizeof(srvAddr)); // 接続の許可 listen(srvSocket, 1); // 正常終了 return 0; } 接続してきたクライアントに対してはacceptした後、そのsocketに対し、 u_long val=1; ioctlsocket(socket, FIONBIO, &val); //------------------------------------------------------ // クライアント if(connect(dstSocket, (struct sockaddr *) &dstAddr, sizeof(dstAddr))) return -1;

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.5

> sendは送信時に「ネットが詰まってて送れない時」には > 「0バイト送信した」で0で返って来る。 > recvも受信時に「ネットが詰まっててまだデータが来ない時」 > には「0バイト受信した」で0で返って来る。 そんな事は無いと思いますが。。。 recv関数は、接続が正しくシャットダウンを 実行した場合に 0 となります。それ以外のエラーは (タイムアウトも含めて)-1となるはずです。 send関数も同様に、正常切断に対しては 0 、異常切断に対しては -1 のいずれかを返却します。このとき WSAGetLastError() の返却値は、上記の recv() のときと同様のエラーが返されます。 # これはPOSIXでもWINSOCKでも広域エラー領域に対する設定値が # 異なるだけで動作的には同じはずですが。。。 > これがどうにもうまく働いていないようで どううまく動かないのでしょう? 送っているデータは文字列ですが? 数値などを含むバイナリですか?

参考URL:
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/bsd-compatibility.html
nimameMMM
質問者

補足

>>どううまく動かないのでしょう? ヘッダーとデータを分けて受信しているのですが ヘッダー(4byte)は無事受信、その後のデータ受信にて 大量の記号?ですか?文字化けのようなものが受信されてしまいます。 *printf("%s\n", buf);