send-recvで複数データの送受信する方法と問題点

このQ&Aのポイント
  • WindowsVista VC6とLinux2.6.18-at9 Debian PowerPCでunsigned longのデータ数千個を順次送受信するプログラムを作成していますが、問題があります。
  • 受信側のループにprintfやsleep(1)を入れないと受け取れないという現象が発生しています。
  • また、毎回内容ゼロのデータがもう一つ加わるという問題も発生しており、データ量が2倍になってしまいます。
回答を見る
  • ベストアンサー

send-recvで複数データの送受信

初歩的な質問で恐縮ですがよろしくお願いいたします。 send側 :WindowsVista VC6 recv側 :Linux2.6.18-at9 Debian PowerPC でunsigned longのデータ数千個を順次送受信するプログラムを組んでいるのですが不調です。 ともにBlockingモードで動作しているのだから双方にWhileループを組めば特段のHandShakeは不要で受信側所定バッファに逐次取り込めるのだと思っていました。しかし: 1.受信側ループにprintf、sleep(1)等を入れないと受からない。  なお受信側はMainとは別のスレッドにしています。 2.毎回内容ゼロのデータがもう一つ加わってしまう。  recvが毎回データ到着までBlockつまり待ちにしていると期待したのですが、2回通り抜けたような効果があり、各データにゼロデータがもう一行付加されてしまう。つまりデータ量が2倍になる。 プログラム: 送信側: SOCKET s; unsigned long dataBuf char buf[20]; int ok; while ( count < DATANO) ){  fread(&dataBuf, sizeof(dataBuf),1,fp);      // ファイルより読込み sprintf(buf, "%d", dataBuf); ok = send(s, buf, sizeof(buf), 0); // Blocking Mode ? if(ok==SOCKET_ERROR){ printf("Command送信不良"); exit(1); } count++; } 受信側: void* dataReceiveThread(void* pParam) {  char buf[10];  int recvSize, count = 0;  unsigned long val, memBuf[4096];  while(1){   memset(buf, 0, sizeof(buf));   recvSize = recv(conn_fd, buf, sizeof(buf), 0); // Blocking Mode   if(recvSize == 0){    printf("conn_fd broken by Host\n");    close(conn_fd);    break;   }   else if (recvSize == -1) {    perror("recv");    exit(EXIT_FAILURE);   } val = atol(buf); ⇒ printf("val %x\n", val);   if (count < DATANO){ memBuf[count] = val; else break; count++; } このプログラムは初回のデータは問題なく受信できます。 以降のデータは⇒でループ速度を下げないと受信できません。しかしこれは仕様に合いません。 ただしデータ量が2倍になってしまうのでFlipFlopを入れてループを間引いて強引に辻褄合わせをしたところデータはそれらしく受信できます。しかし指定回数ループできません。 recvのBlock機能を誤解しているのかもしれない考え、selectを入れるなどしたのですが解決しません。send-recvの基本仕様を理解していないためと思われます。 ご教示願えれば幸いです。

質問者が選んだベストアンサー

  • ベストアンサー
  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.5

>テーマはずれてきているのですが、スレッド間で変数の変化をリアルタイムで検出する方法が分かりません。 >Mainスレッドでは受信スレッドの状況をcountで見ています。 volatileを使用。というのが最初に浮かぶ方法ですかね。 http://d.hatena.ne.jp/yupo5656/20040618/p1 という意見もあるので微妙ですが。 # Linuxでマルチスレッドプログラミングしたことないので…。 >sleep(1)を使っていたのですが、遅いですし 1秒では遅い…でしょうね。 nanosleep()使ってみたらどうでしょうか? http://archive.linux.or.jp/JM/html/LDP_man-pages/man2/nanosleep.2.html 引数で指定する時間はもう少し考慮が必要かも知れませんが。

gammodler
質問者

お礼

Wr5様 nanosleep試みてみました。 両方のスレッドに200μsのnanosleepをいれたとことろ全体としては動作するのですが、DATANOに達する直前で受信スレッド内のループを回らなくなります。 この原因はわかっていません。 それにあと二桁短縮しないと仕様に合いません。しかし最初から起動しなくなります。 他の方法を試みて問題を整理の上再度質問させていただきます。 ありがとうございました。

その他の回答 (4)

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.4

>1回目に'1','2','3','4','5','6','7','8','9','0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'を送信。 >1回目に'1','2','3','4','5','\0','7','8','9','0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'を送信。 1回目に~ 2回目に~ の間違いです。

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.3

>sprintf(buf, "%d", dataBuf); >ok = send(s, buf, sizeof(buf), 0);// Blocking Mode ? 1回目に'1','2','3','4','5','6','7','8','9','0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'を送信。 1回目に'1','2','3','4','5','\0','7','8','9','0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'を送信。 という感じにゴミデータも送るのは仕様ということでよろしいですか? # 2回目に送ったつもりのデータをrecvで先頭から7バイト受信していたら、続くゴミデータが有効データとして処理される可能性がありますが… memset(buf, '\0', sizeof(buf)) 等で明示的にクリアしておくべきです。 また、既に回答着いていますがrecv()の戻り値はちゃんと確認するべきです。 「send()で10Byte送ったからrecv()で10Byte指定すればちゃんと10Byte受信できる。」 と思っているのでしたら、『そうとは限らない』というコトを考えておいた方がいいです。(ストリームですし…) # まぁ、指摘されている通り、(ゴミデータ付きで)20Byte送って、(最低でも)10Byteを2回という受信を行っているので… # ループの2回目ではゴミデータをatol()に渡して0が代入されている…でしょう。(もちろん、ゴミ次第で0以外が入りますが) >1.受信側ループにprintf、sleep(1)等を入れないと受からない。 > なお受信側はMainとは別のスレッドにしています。 スレッド切り替えが発生しない(タイムスライス使い切るまで切り替わらない?)ので、受信データをバッファから受け取れないのでしょう。 printf()でいけるのは、カーネルのシステムコールが発生した時点でスレッド切り替えが走っているのでしょう。 >2.毎回内容ゼロのデータがもう一つ加わってしまう。 > recvが毎回データ到着までBlockつまり待ちにしていると期待したのですが、2回通り抜けたような効果があり、各データにゼロデータがもう一行付加されてしまう。つまりデータ量が2倍になる。 既に指摘した通りです。 有効データ+ゴミデータで20byte送り、受信側では有効データ(10Byte)+ゴミデータ(10byte)をatol()に突っ込みます。 # recv()で10Byteのデータが受け取れなかった時はさらにヘンなことになります。 # ゴミデータ次第でSegmentation Fault発生します。

gammodler
質問者

補足

Wr5様 Resありがとうございます。 データ量が2倍になってしまう件はひとつ前で報告しましたように解決しました。 テーマはずれてきているのですが、スレッド間で変数の変化をリアルタイムで検出する方法が分かりません。 Mainスレッドでは受信スレッドの状況をcountで見ています。 while(count < DATANO){ printf("count %x\n", count); // sleep(1); } count = 0; // Burst転送終了 ・・・・・ sleep(1)を使っていたのですが、遅いですし受信スレッドのcountをモニタする機能はprintf文で代用できることがわかったので代替しました。 一方受信スレッド側もタイムスライス分を使い切る前にMainから見えるようにするにはシステムコールを使わなければならないことが分かりました。問題点として: 1.非常に低速、printf文は省きたい。 2.なぜか所定数(DATANO)分受信のスレッドを回らず、countの値が少し手前で変化しなくなる。つまり受信スレッドのwhileループを抜けなくなります。 printf文挿入により低速化、送信側と同期せず受信バッファが切り捨てられているのではと推察しています。 この二つの困難はどう克服すればいいのでしょうか?。マルチスレッドは殆ど初めて。見当がつきません。 お教え願えれば幸いです。

  • trapezium
  • ベストアンサー率62% (276/442)
回答No.2

自分でこう書いたけど、 > recvSize == sizeof(buf) になってるかは一応見たほうがいいんじゃないかな。完全に挙動が把握できてるならいいけど。 やっぱり send したバイト数 recv されるまで、recv() 繰り返すように書くよな。 かなり頭回らず。

  • trapezium
  • ベストアンサー率62% (276/442)
回答No.1

> char buf[20]; > ok = send(s, buf, sizeof(buf), 0);// Blocking Mode ? ここで 20 バイト send して > char buf[10]; >  recvSize = recv(conn_fd, buf, sizeof(buf), 0);// Blocking Mode 10 バイト recv してるように見えるけど? それに block してるとはいえ、 >  if(recvSize == 0){ >  else if (recvSize == -1) { recvSize == sizeof(buf) になってるかは一応見たほうがいいんじゃないかな。完全に挙動が把握できてるならいいけど。

gammodler
質問者

お礼

trapezium様 ご指摘の通りでした。 PC側をchar buf[10]とすることでデータ数が一見2倍になってしまう問題、解決しました。 else if(recvSize != sizeof(buf)){ printf("recvSize sizeof(buf) %x %x\n",recvSize, sizeof(buf)); recvSize = recv(conn_fd, buf, sizeof(buf), 0); } としていますがこのエラーは出力されません。 recvの仕様をよく確認していませんでした。 ありがとうございました。

関連するQ&A

  • C言語のsendとrecv

    C言語でクライアントとサーバのプログラムを作りたいのですが、かなりの初心者なので、とりあえず練習用として、クライアント側に整数を入力すると、サーバ側でその整数を二乗して、結果をクライアント側に返すという簡単なプログラムを作ろうと思いました。 ソケットの作成、通信ドメインの指定等は、参考書を見ながら(というか、ほとんど丸写しみたいな感じなんですが…)、なんとかできたんですが、sendとrecvで整数を送受信するにはどうすればいいのかが分かりません。 私が持っている参考書には、 int send(SOCKETS const char FAR *buf int len int flags); int recv(SOCKETS char FAR *buf int len int flags); とあるのですが、const char FAR *buf、int len、char FAR *bufのところがよく分からないのです。 例えば、a=1000を送信したい場合、aはchar型ではないのでsendでは送れないのですか? ほんと初心者ですいませんが、どなたか教えてください。 よろしくお願いします。

  • TCP通信でrecvで不明な点。

    今、クライアントPCが接続してきて、接続できたら、そのクライアントが渡してくるデータを受け取るプログラムを作っています。下のプログラムを使用しています。 j = recv(NewSocket, RecvBuf, sizeof(RecvBuf), 0); ここで、データをRecvBufというのに入れて、そのデータ量が1212byteだというということがわかりました。それがだいたい5秒に1回程度受信されることがわかりました。 このプログラムだと1212byte受信後に、すぐに j = recv(NewSocket, RecvBuf, sizeof(RecvBuf), 0); このコードを実行しても j = 0が代入されていて、closesocket(NewSocket);が実施されて、接続を切断しているので、その接続を維持したままにできないかと思い、 if(j <= 0) // データを受け取ったか? break; // エラーなら抜ける この部分のプログラムをコメントアウトしてclosesocket(NewSocket);を実施しないようにしたら、5秒以上たっても、1212byteを受信できなくなってしまいました。 この方法だとなぜデータが受信できないのかご教授よろしくお願い致します。 (現在使用しているコードの一部) printf("%d番ポートで接続待機中\n", PORT); // 接続待機中のメッセージ表示 while(1) // サーバループ { i = sizeof(AddrInfo); // SOCKADDR構造体の長さをセットしておく NewSocket = accept(RootSocket, (SOCKADDR *)&AddrInfo, &i); // 接続を待ち受ける if(NewSocket == 0) // エラーがあるか? break; // エラーがあればサーバループを抜ける printf("%sと接続しました\n", inet_ntoa(AddrInfo.sin_addr)); // 接続元のIPアドレスを表示 // TCP/IPデータの受信 while(1) { j = recv(NewSocket, RecvBuf, sizeof(RecvBuf), 0); // クライアントからのデータを読み込む if(j <= 0) // データを受け取ったか? break; // エラーなら抜ける RecvBuf[j] = 0; // 受信データに文字列終端をセットする //if(strstr(RecvBuf, "q")) // qという文字列が含まれているか? // break; // そうならループを抜ける //for(i = 0; i < 30; i++) // 受信した文字列の長さだけ繰り返す // RecvBuf[i] += 1; // 1文字進める if(j != 0) // データを受け取ったか? { for(i = 0; i < 30; i++) // 受信した文字列の長さだけ繰り返す ShowBuf[i] = RecvBuf[i]; // 1文字進める //send(NewSocket, RecvBuf, strlen(RecvBuf), 0); // 加工したデータをクライアントに送信 for(i = 0; i < 30; i++) printf("%#x " , ShowBuf[i]); // サーバから受け取った文字列を表示 printf("cnt %d \n",j); } } //send(NewSocket, RecvBuf, strlen(RecvBuf), 0); // 加工したデータをクライアントに送信 closesocket(NewSocket); // 生成されたソケットを開放 printf("%sと切断しました\n", inet_ntoa(AddrInfo.sin_addr)); // 切断メッセージ表示 }

  • 配列とポインタについて

    こんばんわ。 WinSockを利用してネットワークプログラミングを行っています。 以下のようなプログラムを作成したのですが、実行できません。 以下のプログラムは質問箇所を抜き出したものです。 [プログラム] int Receive(u_short portNo) { char recv_Buf[1025],recv_Buff[1025]; size = recvfrom(s2, recv_Buf, (int)sizeof(recv_Buf) - 1, 0, (SOCKADDR *)&from, &fromlen); recv_Buff = recv_Buf; Sender(portNo,szServer,recv_Buff); } int Send(unsigned short portNo,char *szServer,char *recv_Buff) { sendto(s1, recv_Buff, (int)strlen(recv_Buff), 0, (LPSOCKADDR)&addrin1, sizeof(addrin1)); } このプログラムは、Receive関数内のrecvfrom関数で1024Byte(recv_Buf[1025])受信したデータをSend関数内のsendto関数で送信するというプログラムです。 recv_Buf = recv_Buffにてエラーが発生します。なにか解決策はりますでしょうか? また、Receive関数およびSend関数は何回も呼ばれるのですが、配列は初期化する必要があるのでしょうか? よろしくお願いします。

  • 中身が同じような関数を一つの関数にしたい

    void C_Commu::Recv( vector<void *> Data ) { ----BYTE *Buf; ----int TotalSize, RecvSize; --------TotalSize = Data.size()*sizeof(Data[0]); --------Buf = new BYTE[TotalSize]; --------while( RecvSize < TotalSize ) --------{ ------------RecvSize += recv( Sock, (char *)Buf, TotalSize-RecvSize, 0 ); --------} --------memcpy( &Data[0], Buf, TotalSize ); } このような関数を作ってみたのですが、 C_Commu::Recv( Data->CharaNum ); //vector<char> CharaNum C_Commu::Recv( Data->Model ); //vector<S_Model> Model(S_Modelは構造体) としてみると 'C_Commu::Recv' : 1 番目の引数を 'std::vector<_Ty>' から 'std::vector<_Ty>' に変換できません。 とエラーになります。 使いたい動的配列が違うだけで残りは同じ関数なので一つに纏めてみたのですがどうしてエラーになるのか分かりません。 どのようにしたらいいのでしょうか?

  • recv関数でフリーズしてしまう

    現在Winsockを用いた簡単なパケット送受信のソフトを作成しています。 開発環境はVisual Studio2008で、ダイアログベースで作成しています。 パケットの受信側の処理なんですが、いつパケットが来てもいいように、 Ontimerで、定期的に受信処理をしようと思っています。 そこでOntimer内に以下のようにプログラムしたところ、コンパイルエラー はないものの、数秒経つと応答なしとなりフリーズしてしまいます。 原因を探ったところrecv関数が原因で、recv関数をコメントアウトした ところ、フリーズはしなくなりました。また、エラー処理は省略していま すが、ソケットの作成失敗などはありませんでした。 なぜrecv関数でフリーズが起きてしまうのか、原因が分かる方は いらっしゃいますか? OnTimer(UINT nIDEvent){ WSAData wsaData; SOCKET sock; struct sockaddr_in addr; char buf[2048]; WSAStartup(MAKEWORD(2,0), &wsaData); sock = socket(AF_INET, SOCK_DGRAM, 0); addr.sin_family = AF_INET; addr.sin_port = htons(12345); addr.sin_addr.S_un.S_addr = INADDR_ANY; bind(sock, (struct sockaddr *)&addr, sizeof(addr)); memset(buf, 0, sizeof(buf)); recv(sock, buf, sizeof(buf), 0); closesocket(sock); WSACleanup(); }

  • VC++2008Enterpriseでwinsockを扱っていいるので

    VC++2008Enterpriseでwinsockを扱っていいるのですが、 クライアント側(PC)からファイルを読み込んで、ソケットを使って、サーバー側に渡し、何もせず、そのままクライアントに返したものをprintfすると。ファイルの後ろに大量のゴミがくっついて、それの除去ができないのです。 サーバー側では普通に出力できるのですが、何でクライアント側だけ汚いか、よくわからないんです。 送ったファイルはテキストファイルで中身は aaa bbb 111 とこんな感じです。 クライアントのソースは、受信部分を抜粋しますと //ファイルを1行づつ読み込む while((fgets(buffer1, size, fp))!=NULL){ len = strlen(buffer1); sprintf(str,"%4d%s",len,buffer1); //printf("%d行目:バイト数=%s\n%s\n",n,str,buffer1); ok = send(s, str, (int)strlen(str), 0); if(ok==SOCKET_ERROR){ printf("送信できません\n"); exit(1); }else if(ok ==-1){ perror("send"); break; } n++; } shutdown(s,1); while(1){ ok = recv(s, recv_buf, sizeof(recv_buf),0); if(ok == -1) { printf("受信エラー\n"); shutdown(s,2); closesocket(s); break; }else if(ok ==0){ printf("送信終了\n"); shutdown(s,2); closesocket(s); break; }else { printf("%s\n",recv_buf); } } } } これで、出力すると大量のゴミがつくんです。 どなたか、ご指導願い無いでしょうか?

  • ファイルの受信

    c言語で、クライアント側のファイルを開き、内容をそのまま送信しているはずなのですが上手くいきません。 テキストファイルは正しく送れるようですが、他の実行ファイルなどはダメみたいです。 送信側は、"rb"でオープンし,whileで fread(send_buf,1024,1,fp); send(soc,send_buf,strlen(send_buf),0); を繰り返しています。send_buf[1025]です。 送信側は Recv_buf[1025];で size = recv(soc,Recv_buf,1024,0); fwrite(Recv_buf,size,1,fp); whileで繰り返し受信がなくなったらselectでタイムアウトしています。 いろいろ調べたのですがSleepが必要らしいそうですが・・・どうなんでしょう?

  • ソケットのrecvの戻り値が0

    linuxにてソケットの勉強をしようと思い、 簡単なソケット通信のプログラムをCで組みました。 ret = recv(ID, Buff, sizeof(Buff), 0); といった感じでサーバ側を組んだのですが、 この戻り値retに0が入ることがあり、思うように動いてくれません。 私の認識だと、recvは受信するまで待ち、 受信したサイズを返すと思っているのですが、 それが0とは、いったいどういう意味を持つのでしょうか?

  • socket: recvはいつ,どれだけ受け取るのか?

     現在,参考書にしたがってC++でソケットプログラミングを書いています.  sendとrecvを非同期にするために,本では select関数やWSAAsyncSelect関数などを利用していて,実際,本のとおりに書いて上手く動いています.  ここで伺いたいのですが,recvは,どうやって「データが届いたか」を知るのでしょうか.  同期ならば,トランシーバでの会話のように送信側が「どうぞ」といって送受信を交代させることができますが,非同期ならばそれができません.  NICとかが,プログラムに「届いたぞ!( or これから届くぞ!)」と教えてくれるのでしょうか.あるいは逆に,プログラムがNICに「届いてる?」と聞いているのでしょうか.仮に,ここに書いたような方法で届いたことが分かったとしても,どれくらい受け取ればいいかは分かりません(それも併せて教えてもらっているのでしょうか.データを送るときには,どれだけ送ればいいか分かりますよね.受信するときはどうしてるのかを知りたいと思っています).

  • TCPでの非同期型select関数について

    マッキントッシュOS-X上で動くTCP通信プログラムを作る場合について教えてください。 ウィンドウアプリを考えています。 ウィンドウ環境なので、アプリがフリーズしてしまわないために、 以下のような非同期処理が考えられます。 この場合、データが来ていなくても、while(1)がぐるぐる回るので 無駄にプロセスを食ってしまうと思うのですが、これはしかたのないことでしょうか。 もっとよい方法がるのでしょうか。 Winsockだとイベント応答関数での受信処理処理ができてエレガントなのですが、 バークレイソケットでは、どうすべきなのか疑問に思い質問させていただきました。 よろしくお願いします。 //ソケットを非同期モードにセット val = 1; ioctl(sock, FIONBIO, &val); while (1) {   memcpy(&fds, &readfds, sizeof(fd_set));   select(2, &fds, NULL, NULL, NULL);   // sockに読み込み可能データが届いている場合は、受信   if (FD_ISSET(sock, &fds)) {     memset(buf, 0, sizeof(buf));     recv(sock, buf, sizeof(buf), 0);   }   else{     //受信データがなかったときの処理   } }

専門家に質問してみよう