1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

connect関数のループ内における不具合

Last updated at Posted at 2012-08-24

作成中のプログラムは大学での課題「1対1のチャットプログラム」名づけて”Talk”
マルチスレッドでクライアントとサーバーを同時に動かす方法で実現を試みる。

目的:
対話相手がいつもオンラインである保証がないので、クライアント側はconnectを繰り返し試行する。

症状:
ループ2回目以降、毎回connect関数が(Invalid argument)エラーを吐いて無限ループ。
接続できるときは一回目のconnectで成功する。このときはループを抜けるのでOK
接続失敗するときは一回目に限り ”Connection refused”エラーを吐く。
2回目以降は前述の通り。

試行した解決策:
①2回目以降、アドレス構造体sinが全てクリアされているのが確認できたため、ループする度に構造体の変数に値を入れなおした 
→ 2回目以降のconnect実行時におけるアドレス構造体の値は適切になったが、エラーは消えず。

②5秒間スリープさせてから相手へのconnectを開始する。(2台の端末上で、同タイミングでプログラムを実行しておけば5秒の間に両者ともサーバー側スレッドはaccept体制に入っているハズ)
→うまくいった。ただし5秒過ぎても相手のサーバーが死んでいれば、やっぱり症状が発症。

③エラーがInvalid argumentなので、1回目と2回目のconnect実行前にアドレス構造体の値を出力させて変化がないかチェック。
→変化なし。

オチ:
 ...結果、色々ググッていると、こういう記事が・・・↓
 http://d.hatena.ne.jp/n_a_u/20091221/1261360709
 この記事のコメントに、
 「connect(2)のmanで見る限りconnectはストリームでは1回限りしか成功しない。」
 とある。
 Manのページ↓見てみたら、確かに書いてあった。
 http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/connect.2.html

 コメント通りならアドレス構造体をリセットすればうまくいくはずだが、それも失敗済み。
 目的通りの処理をするためにはどうすればいいか、近いうちに大学の先輩方にお聞きする。
→お聞きしたところ、ソケットを作り直さないといけなかったらしい。
  プログラムのソケット作成からやり直して実行した途端にうまくいくようになった。
  
'''
//メッセージを受信する。(クライアント)
void *receiveMessage(void *para){
struct sockaddr_in sin;
int socketID = 0;
int recvSize = 0;

pthread_mutex_lock(&mutex);
printf("(i) Preparing for receiver.\n");

//ソケット作成
printf("Building socket...");
if((socketID = socket(PF_INET, SOCK_STREAM,0))< 0){
	printf("\n(!)Failed to building socket for receiver.\n");
	pthread_exit(0);
}
printf("OK\n");

//sock_addr_in構造体を初期化
printf("Initializing sender...");
if(init_sockaddr_in(hostName, &sin)<0){
	perror("Failed.\n");
	goto close_socket;
}
printf("OK\n");

//アドレス構造体情報を出力(デバッグ用)
printf("Addr_inStructure(Socket:%d Family:%d IP-Address:%s Port:No.%d len:%d) size:%lu on %p \n",socketID,sin.sin_family,inet_ntoa(sin.sin_addr),sin.sin_port,sin.sin_len,sizeof(sin),&sin);    //接続状況出力

//コネクションを張る
printf("Sending request to connect....\n");
sleep(5);
while(connect(socketID,(struct sockaddr *)&sin, sizeof(sin))== -1){
	printf("(!)Failed to connect with errno:%d(%s)\n",errno,strerror(errno));
	sleep(1);
	if(init_sockaddr_in(hostName, &sin)<0){
		perror("(!)Failed to initialize addr_in\n");
		goto close_socket;
	}
	printf("Addr_inStructure(Socket:%d Family:%d IP-Address:%s Port:No.%d len:%d) size:%lu on %p \n",socketID,sin.sin_family,inet_ntoa(sin.sin_addr),sin.sin_port,sin.sin_len,sizeof(sin),&sin);    //接続状況出力
}
printf("Request was accepted.\n");

//この段階まできたら受信準備は整ったことになる。
printf("(◯) Ready to receive message completed.\n");
pthread_mutex_unlock(&mutex);

//メッセージ受信
while(1){
	//メッセージを受信
	if ((recvSize=recv(socketID,&buffer, sizeof(buffer),0))<=0) {
		perror("(!)Failed to receive messages");
		clean(buffer);
		goto close_socket;
	}
	receivedMessage = malloc(sizeof(buffer));
	strcpy(receivedMessage, buffer);
	clean(buffer);
	printf("%s>%s (%d bytes)",inet_ntoa(sin.sin_addr),receivedMessage,recvSize);
	free(receivedMessage);
}

//ソケットを閉じる。

close_socket:
printf("Closing socket.\n");
pthread_mutex_unlock(&mutex);
if(close(socketID)!=0) perror("Failed to close");
pthread_exit(0);
}

'''

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?