C言語 ネットワークプログラミング selectの使い方
受付中
回答 2
投稿
- 評価
- クリップ 1
- VIEW 1,858
C言語プログラミング初心者です.
ネットワークプログラミングでわからないところがあり,また質問させていただきました.
前回質問したときよりも少し簡単なものを作ろうと思いました.
クライアントが一つに対し,複数のサーバがつながっているネットワークモデルでデータのやり取りを行いたいと考え,プログラミングしてみたのですが,selectの使い方が悪いのか上手く動作してくれません.
プログラムの内容としては複数のサーバーが同時に容量の大きいデータ(下記のコードでは160MB)を作成し,送信.クライアント側でselectを用いて受信し受信時間を計測.といった形で書いてみたつもりなのです.
しかし,サーバー1(172.168.0.2),サーバー2(172.168.0.3)を起動した後,クライアント(172.168.0.1)を起動するとサーバー2のパケットは受信できたのですが,サーバー1で送信は完了するのですが,クライアント側ではサーバ1のデータ受信の計測時間が出力されず,強制終了と出力されてしまいます.
サーバー側
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
typedef struct {
double deg;
double dist;
}point;
//ダミーのデータを詰める
void makeData(int datanum,point *data){
int i;
double col,val;
for(i=0;i<datanum;i++){
col = (double)i;
data[i].deg = col;
val += 0.01;
data[i].dist = val;
}
printf("%f %f\n",col,val);
}
int main()
{
FILE *fpr;
int sock0;
struct sockaddr_in addr;
struct sockaddr_in client;
int len;
int sock;
int yes = 1;
const static int DATANUM = 10000000;
int i;
point *pbuf;
int res;
//ソケットOpen
sock0 = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(11111);
addr.sin_addr.s_addr = INADDR_ANY;
setsockopt(sock0, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
bind(sock0, (struct sockaddr *)&addr, sizeof(addr));
listen(sock0, 5);
len = sizeof(client);
sock = accept(sock0, (struct sockaddr *)&client, &len);
//point型の配列をDATNUMだけ確保。その中にデータを入れる。
pbuf = (point *)malloc(sizeof(point)*DATANUM);
makeData(DATANUM,pbuf);
/* サーバからデータを送信 */
res = send(sock,pbuf,sizeof(point)*DATANUM,0);
printf( "%d bytes sent, %s\n",res, strerror( errno ) );
close(sock);
close(sock0);
free(pbuf);
return 0;
}
クライアント
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <time.h>
typedef struct {
double deg;
double dist;
}point;
typedef struct {
int datanum;
point *meas;
}frame;
void recvFrame(frame *f,int sock);
void recvFrame2(frame *f,int sock);
void recvFrame(frame *f,int sock){
int i;
int ret;
int bytenum;
int cur_pos = 0;
char *rbuf;
char *pbuf;
point p1;
struct timespec startTime, endTime;
printf("sizeof(point)=%d\n",sizeof(point));
bytenum = f->datanum * sizeof(point);
printf("bytenum=%d\n",bytenum);
f->meas = (point *)malloc(bytenum);
rbuf = (char *)malloc(bytenum);
pbuf = (char *)malloc(bytenum);
clock_gettime(CLOCK_REALTIME, &startTime);
while(bytenum > cur_pos){
ret = recv(sock,rbuf,bytenum,0);
memcpy(&(pbuf[cur_pos]),rbuf,ret);
cur_pos += ret;
}
memcpy(f->meas,pbuf,bytenum);
clock_gettime(CLOCK_REALTIME, &endTime);
//printf("START = %10ld.%09ld\n", startTime.tv_sec, startTime.tv_nsec);
//printf(" END = %10ld.%09ld\n", endTime.tv_sec, endTime.tv_nsec);
printf("SOCK1 TIME = ");
if(endTime.tv_nsec < startTime.tv_nsec){
printf("%10ld.%09ld", endTime.tv_sec -startTime.tv_sec - 1, endTime.tv_nsec + 1000000000 - startTime.tv_nsec);
}else{
printf("%10ld.%09ld",endTime.tv_sec - startTime.tv_sec, endTime.tv_nsec - startTime.tv_nsec);
}
printf("(sec)\n");
free(pbuf);
free(rbuf);
}
void recvFrame2(frame *f,int sock){
int i;
int ret;
int bytenum;
int cur_pos = 0;
char *rbuf;
char *pbuf;
point p1;
struct timespec startTime, endTime;
printf("sizeof(point)=%d\n",sizeof(point));
bytenum = f->datanum * sizeof(point);
printf("bytenum=%d\n",bytenum);
f->meas = (point *)malloc(bytenum);
rbuf = (char *)malloc(bytenum);
pbuf = (char *)malloc(bytenum);
clock_gettime(CLOCK_REALTIME, &startTime);
while(bytenum > cur_pos){
ret = recv(sock,rbuf,bytenum,0);
memcpy(&(pbuf[cur_pos]),rbuf,ret);
cur_pos += ret;
}
memcpy(f->meas,pbuf,bytenum);
clock_gettime(CLOCK_REALTIME, &endTime);
//printf("START = %10ld.%09ld\n", startTime.tv_sec, startTime.tv_nsec);
//printf(" END = %10ld.%09ld\n", endTime.tv_sec, endTime.tv_nsec);
printf("SOCK2 TIME = ");
if(endTime.tv_nsec < startTime.tv_nsec){
printf("%10ld.%09ld", endTime.tv_sec -startTime.tv_sec - 1, endTime.tv_nsec + 1000000000 - startTime.tv_nsec);
}else{
printf("%10ld.%09ld",endTime.tv_sec - startTime.tv_sec, endTime.tv_nsec - startTime.tv_nsec);
}
printf("(sec)\n");
free(pbuf);
free(rbuf);
}
int main()
{
struct sockaddr_in server, server2;
int sock,sock2;
fd_set fds, readfds;
int maxfd;
int len, len2;
int n;
struct timeval tv;
sock = socket(AF_INET, SOCK_STREAM, 0);
sock2 = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_port = htons(11111);
server.sin_addr.s_addr = inet_addr("172.168.0.2");
server2.sin_family = AF_INET;
server2.sin_port = htons(22222);
server2.sin_addr.s_addr = inet_addr("172.168.0.3");
connect(sock, (struct sockaddr *)&server, sizeof(server));
connect(sock2, (struct sockaddr *)&server2, sizeof(server2));
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
FD_SET(sock2, &readfds);
tv.tv_sec = 10;
tv.tv_usec = 0;
if(sock > sock2){
maxfd = sock;
}else{
maxfd = sock2;
}
frame f1,f2;
f1.datanum = 10000000;
f2.datanum = 10000000;
while (1) {
memcpy(&fds, &readfds, sizeof(fd_set));
n = select(maxfd+1, &fds, NULL, NULL, &tv);
if(n==0){
printf("timeout\n");
break;
}
if(FD_ISSET(sock, &fds)){
recvFrame(&f1,sock);
}
if(FD_ISSET(sock2, &fds)){
recvFrame2(&f2,sock2);
}
}
close(sock);
close(sock2);
return 0;
}
selectの使い方がまずいのでしょうか?
どなたかお力添えよろしくお願いします.
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
0
サーバーのコードを見ると送信が完了したらソケットをクローズしています。
fd_setの領域にはあくまで「通信中のファイルディスクリプターのみ」を設定すべきですが、ご質問のコードではそれを気にせずサーバー側がクローズ済みかも知れないファイルディスクリプターも含めて毎回readfdsに指定してしまってますね?
通信相手がソケットをクローズした場合もselectはそれを検知できます(EOFとして)。そのソケットでの通信が完了したことを検知したら速やかにcloseし、以降のselectではそのファイルディスクリプターをselectの対象外にすべきでしょう。そうしないとselectは期待通りの動作(監視対象のソケットがreadyになるまでプロセスをブロック)になってくれないと思います。
writefdsを省略する(NULL)のはよいですけど、エラーを検知できた方がより精密な処理が作れる気がするのでerrorfdsは省略しない方がよいかも知れません。エラーが起きた時それを検出しないでいるとselectはどうなるんでしょうか・・・-1が返ってくるからエラーが起きたことはわかるのでしょうが、「どのソケットで異常が発生したか」などは処理を書きにくい気がします。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
これは「1クライアント」が「複数のサーバー」に接続することを意図したプログラムのように思えます。そういうシステム設計が存在しないわけではありませんが、変則的ではあります。初心者ということならば、まずは一般的な形から試してみることなんじゃないでしょうか。select()の使い所を学ぶためであれば1対1通信でもできます。その場合でも、select()による待ち処理を持たせるのはサーバー側のプログラムでしょう。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.34%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる