• ベストアンサー

winsockで送受信されるデータの効率的な扱い方について

Winsock2.hを使って通信プログラムを作っています。 座標などのパラメータを短いスパンで送受信して処理をするものなのですが、 send()で送れるデータ型がchar*型ということなのでパラメータをwsprintf()を使って SOCKET s; int x=100; int y=100; int z=100; char buf[16]; wsprintf(buf,"#%04d#%04d#%04d",x,y,z); send(s,buf,strlen(buf),0); のようにして送信して、受信されたデータはstrtok()で区切りながらそれぞれ格納しています。 SOCKAT s; int x,y,z; int nResult; char buf[16]; char *tp; nResult = recv(s,buf,sizeof(buf),0); tp = strtok(buf,"#"); x=atoi(tp); tp = strtok(buf,"#"); y=atoi(tp); tp = strtok(buf,"#"); z=atoi(tp); という感じなのですが、パラメータが増えていくにつれなんというか冗長というか、 もうちょっと賢いやり方があるような気がしていろいろ調べてはみたものの… あまりデータの扱い方に関しての解説が見つからなかったので質問させていただきました。 ということで、他に複数の変数を纏めて送受信して処理する手法(できれば高速に)をご存知でしたらご教授お願いします。

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

  • ベストアンサー
  • i-kujou
  • ベストアンサー率50% (13/26)
回答No.2

#1です。 すいません。なんか誤ってプログラム部分だけ送信してしまいました。 sendの引数はchar*ですが、別にバイナリーデータを送信しても問題ありません。 同じi386CPUのWindowsマシン間で通信する、という条件があるのでしたら、送信データを構造体にして、構造体のデータを送信するのが一番てっとりばやいと思います。 その具体例が#1のソースになります。 なお、ソースは動作は確認しておりません。 あしからずご了承ください。

cabin_
質問者

お礼

回答ありがとうございます。 バイナリで送れたんですね。 パラメータが増えてきて複雑なトークン分けを行っていたのでおかげでかなり楽になりました。

その他の回答 (2)

  • chie65535
  • ベストアンサー率43% (8514/19356)
回答No.3

#1の回答にあるように「バイナリデータのまま送受信」が効率的ですが、その場合は、longやintやshortの扱いに注意しましょう。 送信側と受信側で、intやshortのデータの「バイト並び順」が一致している保証は無いので「バイトの並び順を統一する」必要があります。 以下のページを参考に http://msdn.microsoft.com/ja-jp/library/3thek09d(VS.71).aspx 送信側では「ホストのバイト順からネットワークのバイト順に変換」を、受信側では「ネットワークのバイト順からホストのバイト順に変換」を行う必要があります。 送信側の例(x,y,zは32ビットと想定) senddata.x = htonl(x); senddata.y = htonl(y); senddata.z = htonl(z); 受信側の例(x,y,zは32ビットと想定) x = ntohl(senddata.x); y = ntohl(senddata.y); z = ntohl(senddata.z);

cabin_
質問者

お礼

回答ありがとうございます。 他の方法を調べていたとき、構造体を送る方法も見つけたのですがエンディアンの都合であまりお勧めできないというようなことが書いてあって見送っていたのですが、 ネットワークバイトオーダーの変換を利用すればよかったんですね。

  • i-kujou
  • ベストアンサー率50% (13/26)
回答No.1

typedef struct _TSendData { int x; int y; int z; } TSendData; // 送信側 int SendData(SOCKET s, int x, int y, int z) { // 送信データの作成 TSendData senddata = {0}; senddata.x = x; senddata.y = y; senddata.z = z; int done = 0; int r; char* sendptr = (char*)&senddata; // 送信する先頭アドレスを取得 // 送信データを全て送り終わるまでループする while( done < sizeof(TSendData) ) { // 未送信の部分を送信してみる r = send(s, sendptr, sizeof(TSendData) - done, 0); // 送信結果がエラーなら終了 if( r <= 0 ) return r; // 未送信データアドレスと送信完了バイト数を更新 sendptr += r; done += r; } return done; } // 受信側 // 返り値は読み込んだバイト数orエラーコード // data に受信結果が挿入される int RecvData(SOCKET s, TSendData* data) { // 受信データアドレスの設定 char* recvptr = (char*)data; int done = 0; // 受信データを1つぶん読み込むまでループする while( done < sizeof(TSendData) ) { // 未受信データアドレスに実際にデータを読み込む int r = recv(s, recvptr, sizeof(TSendData)-done, 0); // エラーだったら終了する if( r <= 0 ) return r; // 未受信データアドレスと受信完了バイト数を更新 recvptr += r; done += r; } return done; }

関連するQ&A

  • 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; } -------------------------------------------------------- 以上なのですが、おかしな点や、改善点などありましたら お教えいただけたら幸です。

  • Run-Time Check Failure #3 と表示されてしまうことについて

    初歩的な質問で申し訳ありません。 Visual Studio C++にて、入力された値を基に最短(最小値)を求めていくプログラムを作成しているのですが、 ”Run-Time Check Failure #3 - The variable 'x' is being used   without being defined.” と表示されて、コマンドプロンプトが実行されません。 なぜこうなってしまうのですか? 参考までに下記に作成したソースコードを示します。 初心者ゆえ書き方がしっかりとできておらず、大変わかりにくいソースかとは思いますが、助言をいただければ幸いです。 #include "stdafx.h" #include "stdlib.h" #define MAX_LINE 256 #define MIN_DATA 3 int _tmain(int argc, _TCHAR* argv[]) { char buf[MAX_LINE]; int i,x,y,z,min_data; int data[MIN_DATA] = {x,y,z}; printf("Sからaまでの距離を入力して下しい。\n"); gets(buf); /*キーボードから値を入力*/ x = atoi(buf); printf("a=%dです。\n",x); printf("Sからbまでの距離を入力して下しい。\n"); gets(buf);/*キーボードから値を入力*/ y = atoi(buf); printf("b=%dです。\n",y); printf("Sからcまでの距離を入力して下しい。\n"); gets(buf);/*キーボードから値を入力*/ z = atoi(buf); printf("c=%dです。\n",z); printf("並べ替えると\n"); min_data = data[30];/*入力された値を降順で並べ最小値を表示*/ for (i = 0; i < MIN_DATA; i++) { if (min_data > data[i]) { min_data = data[i]; } } printf("最短は %d\n", min_data); printf("Enterで終了"); return (0); }

  • C言語でファイルの内容を strtok関数 を使って数字と文字を分けて

    C言語でファイルの内容を strtok関数 を使って数字と文字を分けて配列に格納したいのですが、うまくできません。 どこが駄目なのかご指摘をお願いします! ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int i=0; int num[10]; char na[10]; fp=fopen(argv[1],"r"); while(fgets(str,sizeof str,fp)!=NULL); tp = strtok ( str, " " ); while(tp != NULL ) { num[i]=atoi(tp); tp = strtok( NULL," "); if ( tp != NULL ){ na[i]=*tp; } i++; } printf("%d\n%s",num[0],na[0]); printf("%d\n%s",num[1],na[1]); fclose(fp); return 0; }

  • 配列内に通番(文字列)を挿入したいのですが・・・(Winsock利用)

    こんばんわ。 A端末(送信端末)→B端末(受信端末)というように、A端末から複数パケットを送信し、B端末でパケットを受信するというプログラムをUDPを用いて作成しています。 [実装したいこと] ・A端末において各パケットに対して、通番(TCPのシーケンス番号)のよ うなものを挿入し、パケットを送出。 ・B端末で、どの通番を持つパケットを受信することができたか?を確 認したい。 と思っています。 そこで、上記をふまえ以下のようなプログラムを作成しました。 [A端末(送信側)] //main main(int argc ,char *argv[]){   UDPSending(s_port,szServer); } //whileループにてsend_packet関数を何回も呼び出す。packet_Num変数よりカウントアップ。 UDPSending(unsigned short s_port,char *szServer){  int packet_Num = 1;   while((n = fread(send_Buf,1,SEND_DATA_SIZE,fp)) != 0) {    send_packet(packet_Num, s_port, szServer, send_Buf, n);    packet_Num++;  } } //sprintf関数を使用し各パケットに通番を付加 send_packet(int packet_Num, unsigned short s_port, char *szServer, char *send_Buf, int n){   char send_Buff[1500];   //配列初期化   memset(send_Buff,'\0',sizeof(send_Buff));   //send_Bufに文字列を付加?   sprintf(send_Buf+32,"%d\n",packet_Num);   //send_Bufの内容をsend_Buffへコピー   memcpy(send_Buff,send_Buf,n);   UDPDataSend(s_port, szServer, send_Buff, n); } //パケット送出 UDPDataSend(unsigned short s_port, char *szServer, char *send_Buff, int n){   sendto(省略) } 上記のように、sprintf関数を使用しpacket_Num変数の文字列を挿入することで、送出されるパケットに通番を割り振っていることになるのでしょうか? よろしくお願い致します。

  • C言語 strtok

    失礼します。現在こちらでアドバイスを頂きfgetcを使用して配列に格納をすることができたのですが、CSVをカンマ区切りで格納したいのですが上手くいかず困っています。strtokを使用方法をドキュメントを読んでもうまく区切ったものを配列に入れる方法がわかりません 何卒よろしくお願いします。 ソースコード #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include<string.h> #define MAXITEM 1400 int split(char *str, const char *delim, char *outlist[]) { char *tk; int cnt = 0; tk = strtok(str, delim); while (tk != NULL && cnt < MAXITEM) { outlist[cnt++] = tk; tk = strtok(NULL, delim); } return cnt; } int main(void) { FILE *fp; char *fname = "testfile.csv"; char *tp; char *array[1400]; char *test[11][1400]; char c; int i = 0; int n,y; char *tp[1400]; fp = fopen(fname, "r"); if (fp == NULL) { printf("%sファイルが開けません¥n", fname); return -1; } while ((c = fgetc(fp)) != EOF) { array[i] = (char)c; i++; } tp = strtok(array, ","); puts(*tp); while (tp != NULL) { tp = strtok(NULL, ","); if (tp != NULL)puts(tp); } for (n = 0; n < 11; n++) { for (y = 0; y < 1400; y++) { test[n][y] = tp[y]; printf("%c", test[n][y]); } } fclose(fp); return 0; }

  • C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問

    C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問です! ファイルを開いた後でエラーとなるのですが、何が足りないのでしょうか? ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int k,i=0; int num[10]; char na[10][20]; fp=fopen(argv[1],"r"); if(fp==NULL){ printf("ファイルを開けません\n"); return 1; }else{ printf("開けた\n"); } while(fgets(str,sizeof str,fp)!=NULL){ tp=strtok(str," "); num[i]=atoi(tp); tp=strtok(NULL," "); strcpy(na[i],tp); i++; } printf("%d\n%s\n",num[0],na[0]); printf("%d\n%s\n",num[1],na[1]); fclose(fp); return 0; }

  • ファイルの入出力に関する質問

    CSVファイルを読み込んで、処理をするプログラムを書いています。 しかし、うまくいきません。 CSVファイルは 単語1,数値データ 単語2,数値データ のようになっており、 これをsの配列に格納したいと思っています。 プログラムは以下の通りなんですが。。。 strtokはhttp://www9.plala.or.jp/sgwr-t/lib/strtok.html を参考にしました。 どなたかおしえていただけないでしょうか? #include<stdio.h> #include <string.h> int main(void) { FILE *fp; char s[1000][1000]; char tp[256]; int i=0; if((fp=fopen("in.csv","r"))==NULL){ printf("ファイルオープンできませんよ\n"); exit(1); } while(fgets(tp,256,fp)!=NULL){ tp=strtok(fp,","); puts(s[i][0]=tp); while (tp != NULL ) { tp = strtok(NULL,","); if (tp= NULL ){ puts(s[i][1]=tp); }}i++; } return(0); }

  • ポインタについて

    途中までのソースコード typedef struct node{ char moji[128]; --------(1) }NODE; int main(int argc, char *argv[]){ FILE *fp; char buf[128]; NODE p[128]; char *tp; int i = 0; int j,k; int res; NODE temp; fp = fopen(argv[1],"r"); while(fscanf(fp,"%s",&buf) == 1){ tp = strtok(buf," ,.-"); strcpy(p[i].moji,tp); ---------(2) i++; while(tp != NULL){ tp = strtok(NULL," ,.-"); if(tp != NULL){ strcpy(p[i].moji, tp); --------(2) i++; } } } for(j = 0;j < i; j++) puts(p[j].moji); return 0; } file.txt これ以下の文字を読み込む -------------- The Java programming language is a general-purpose, concurrent, class-based, object-oriented language. It is designed to be simple enough that many programmers can achieve fluency in the language. 例えばこんなソースコードがあって、(1)をポインタにして、(2)のstrcpyを使わずにポインタだけで表現するとしたらどのようになるんですか?

  • winsockについて教えてください。

    winsockについて教えてください。 ネットワーク系は初心者なので、理解が遅いと思いますが、お手柔らかに御願いします。 始めは、perlでクライアント側もサーバ側もソケットで通信するプログラムを書いたんですが、 クライアント側のpcはperlが標準装備ではないので、クライアント側だけVC++で書き直そうと、現在しているのですが、うまくクライアント側で受信してくれません。 書いたソースは、 #include <stdlib.h> #include <conio.h> #include <stdio.h> #include <winsock2.h> #include <cstdlib> #include <iostream> #include <fstream> #define BUFSIZE 256 #define FILENAME "C:\\abc.dat" int main(void) { FILE *fp; int nRtn; char fname[BUFSIZE] = FILENAME; char StrRcv[RECVSIZE]; SOCKET s; //ソケット //接続するサーバの情報 struct sockaddr_in dest; //接続するサーバのIPアドレス //xxx.xxx.xxx.xxxの形式で指定する char destination[] = "xxx.xxx.xxx.xxx"; char buffer[BUFSIZE]; char send_buf[BUFSIZE]; //ソケット通信の準備 WSADATA data; WSAStartup(MAKEWORD(2,0), &data); //接続先(サーバ)のアドレス情報を設定 memset(&dest, 0, sizeof(dest)); //ポート番号はサーバプログラムと共通 dest.sin_port = htons(3001); dest.sin_family = AF_INET; dest.sin_addr.s_addr = inet_addr(destination); //ソケットの生成 s = socket(AF_INET, SOCK_STREAM, 0); //サーバへの接続 if(connect(s, (struct sockaddr *) &dest, sizeof(dest))){ printf("%sに接続できませんでした\n", destination); return -1; } printf("%sに接続しました\n", destination); //ファイルの読込み if((fp=fopen(fname,"r"))==NULL){ printf("ファイルを開けませんでした\n"); exit(1); } //ファイルサイズ取得(フルパスで書く) ifstream ifs(FILENAME, ios_base::binary); //VC++の場合、peek()で読み込まないとファイルが開かれない ifs.peek(); streamsize size = ifs.rdbuf()->in_avail(); //ファイルサイズを出力します printf("size = %d\n",size); while((fgets(send_buf,size,fp)) != 0){ send(s,send_buf,size, 0); }    fclose(fp); //サーバからデータを受信 while(1){ memset(StrRcv, '\0', sizeof(StrRcv)); nRtn = recv(s, StrRcv, (int)sizeof(StrRcv) - 1, 0); printf("→ %s\n\n", StrRcv); if(nRtn == 0) break; if(nRtn ==SOCKET_ERROR){ perror("recvエラーです。\n"); break; } } recv(s, buffer, size , 0); printf("→ %s\n\n", buffer); // Windows でのソケットの終了 shutdown(s,2); closesocket(s); WSACleanup(); return 0; } とこんな感じです。 abc.datの中身はabc。 どこがおかしいかどなたかご教授ください。

  • 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の基本仕様を理解していないためと思われます。 ご教示願えれば幸いです。

専門家に質問してみよう