通信プログラムの試作ーーー
UDP
を用いたじゃんけんゲームシステム
ーーーー裘彬濱
南山大学 情報理工学部
ソフトウェア工学科 青山研究室
1:UDP を用いたじゃんけんゲームシステムの概要
●本システムは通信プロトコル UDP を用いた簡単なじゃんけんゲームシス
テムであり、単一のユーザ(クライアント)が参加し、パソコン(サーバ)
とじゃんけんゲームするシステムである。
●本システムはユーザがゲームに参加できる時間を制限しており、制限時間
にオーバーすると、サーバが止まるようになっている。
●本システムは優勝条件を定めており、優勝条件を満たした側に優勝者にな
り、ゲームを終了させる。
2:
●応用プロトコルの説明(通信順)
C S
| | |--- 開始 --->| (UDP の開始メッセージ) | | |<-ゲーム参加要請--| | | | | |--- 参加/不参加-->| (じゃんけんの手、推定した数字) | | |<---- 応答 ---| (結果) | | | | (以後くりかえし) | | | | |<---- 終了 ---| (優勝/制限時間オーバーしたら、UDP の終了メッセージ) | | time V V●応用プロトコルの説明(メッセージ形式)
メッセージは簡単な文字列とする、区切りに CRLF を使い、この単位で送受
信する。
<1>クライアントからサーバ(コマンド/要求)
開始メッセージ CRLF ー→最初にクライアントからの開始メッセージが1文
字でなくてもいいが、メッセージの内容が重要ではないので、1文字でデー
タにした。
参加/不参加 CRLF ー→参加は y、不参加は n
手/終了命令 CRLF ー→手はグー(r)チョキ(s)パー(p)で、ゲーム終了は q で、
処理が簡単になる。
<2>サーバからクライアント(応答)
OK SP 説明文字列 CRLF
NG SP 説明文字列 CRLF
説明文字列は簡潔なものにする
3:プログラム処理の流れの説明
クライアント サーバ
sock = socket() ssock= socket()bind(ssock,,) -- 受付ポート設定 ループ始め 一回目 send(sock) ---> recv(sock,,) recv(sock) <--- send(sock,,) 二回目以降 ループ始め send(sock) ---> recv(sock,,) recv(sock) <--- send(sock,,) ---> |--- (制限時間 <--- (制限時間オーバー)---| | オーバー通知) ::: | | ---> | | 結果通知 <--- send(sock,,)(優勝) | | ループ終わり ループ終わり | ↓ ↓ 終了 終了
4:プログラムの実現
<1>優勝の判断
サーバ側
int champion=3;
:::
winS++;
if(winS==champion){
remotelen);return 0;
}else if ((handC==0 &&handS==2)
||(handC==1 &&handS==0)
||(handC==2 &&handS==1)){
winC++;
if(winC==champion){
sendto(sock,"+You're the CHAMPION",20, 0,(const struct sockaddr *)
&remote, remotelen);return 0; }
}
クライアント側
if(recvbuf[0]=='+'||recvbuf[0]=='-')return 0;
/*******************************************************************
もしサーバが優勝したら、クライアントに”-You lost!Game over”というメッ
セージを送る、 メッセージの頭文字’ー’と’+’はクライアント側にゲーム結
果の符号である。
*******************************************************************/
<2>制限時間をオーバーした場合の処理
struct fd_set fds,readfds;
int n;
struct timeval tv;
:::
while(1){
:::
FD_ZERO(&readfds);
FD_SET(sock,&readfds);
tv.tv_sec=10;
tv.tv_usec=0;
memcpy(&fds,&readfds,sizeof(fd_set));
n=select(sock+1,&fds,NULL,NULL,&tv);
if(n==0){
sendto(sock,"-Time over!!",20, 0,(const struct sockaddr *) &remote,
remotelen);
printf("time over\n");
return 0; }
:::
}
5:テスト
<1>テスト項目
---1:ゲームに参加/不参加の返事に対するサーバ側の処理
ー→y/n を入力された場合と他のアルファベットを入力された場合に対する
サーバ側の処理を確認する。
---2:制限時間をオーバーした場合に対するサーバ側の処理
ー→クライアントは制限時間内に返事しない時に、サーバが正しく止まるか
を確認する。
---3:制限時間オーバーによりサーバが止まった後、クライアントは送信した
場合に対するクライアント側の処理
ー→サーバからの終了メッセージを正しく受信したか、クライアントが正し
く処理したかを確認する。
---4:優勝に対する処理
ー→優勝の条件を満たした場合に対する処理を確認する。
---5:クライアントからの手に対する処理
ー→クライアントから r/p/s または q を受けた場合とそれら以外の場合の処理。
テスト結果
---1:ゲームに参加/不参加の返事に対するサーバ側の処理結果
クライアントの入力
サーバの応答 テスト結果 y/n Input r/p/s or q. ○ w Erro,input y / n! ○---2:制限時間をオーバーした場合に対するサーバ側の処理
制限時間を過ぎると、サーバが time over を出力し、止まった。しかし、こ
の時点で、クライアントに終了メッセージを正しく送ったかは確認できない。
3確認結果に移る。
---3:制限時間オーバーによりサーバが止まった後、クライアントは送信した
場合に対するクライアント側の処理
サーバが止まった後、クライアントは送信し、送信前に受け取ったサーバ
の終了メッセージ”-Time over!!”を表示し、クライアントプログラムが終了し
たことを確認した。
---4:優勝に対する処理
サーバ クライアント テスト結果 サーバが優勝した 止まった サーバから受信
-You lost!Game over
○
クライアントが優勝 した
止まった サーバから受信
+You're the CHAMPION
○
---5:クライアントからの手に対する処理
クライアントの手 サーバの応答 テスト結果 r/p/s 正しく判断 ○ q ”-Game over”を送信した後、止まった ○ w Erro,input again! ○6:考察と感想
サーバとクライアントとのデータの送受信については、どうも二回連続送
受信ができない(と思っている)、送信した後は受信を待ちである。また、
送信するデータの長さが長くなると、一部しか送信(受信)できない。この
点については、自分が間違っていると思っているが、サーバからの sock に
データの長さを書いても、クライアント側が一部しか表示できなかった。ど
こが門題になるのがどうしても見つからず、結局やり方を変えて、簡潔な
メッセージを送受信することにした。
7:参考文献
●UDP を聞ってみよう (2)
URL
ーー http://x68000.q-e-d.net/~68user/net/udp-2.html
●select を聞う(タイムアウト付き)
URL
ーー http://www.geekpage.jp/programming/winsock/select-with-timeout.php
●クライアントプログラム #include <stdio.h>
#include <sys/socket.h> #include <netinet/in.h>
#include <sys/socket.h> // inet_addr #include <netinet/in.h> // inet_addr #include <arpa/inet.h> // inet_addr #include <string.h> // strncpy int main(){
struct sockaddr_in remote; char sendbuf[10], recvbuf[20]; int sock, ret;
sock = socket(PF_INET,SOCK_DGRAM,0); remote.sin_family = AF_INET; remote.sin_port = htons(60000); remote.sin_addr.s_addr = inet_addr("127.0.0.1"); strncpy(sendbuf,"1",1); while(1){ ret = sendto(sock, sendbuf,6,
0, (const struct sockaddr *) &remote, sizeof(remote)); ret = recvfrom(sock, recvbuf, sizeof(recvbuf)+20, 0, NULL, NULL); if (ret < 0) return -1; recvbuf[ret] = '\0'; printf("%s\n", recvbuf); if(recvbuf[0]=='+'||recvbuf[0]=='-')return 0; fgets(sendbuf,sizeof(sendbuf),stdin); } return 0; }
●サーバプログラム #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> int main(){
struct sockaddr_in local, remote;
char recvbuf[1500],join[1024]="Join the game?(y/n)"; int sock,champion=3,winC=0,winS=0;
socklen_t remotelen = sizeof(remote); sock = socket(PF_INET,SOCK_DGRAM,0); local.sin_family = AF_INET; local.sin_port = htons(60000); local.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock,
(struct sockaddr *) &local, sizeof(local)) < 0)
{printf("error\n"); return -1;}
recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *) &remote,
&remotelen); // remotelen
sendto(sock,join,strlen(join)+1, 0,
(const struct sockaddr *) &remote, remotelen); while(1){
//返事待ち
recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *) &remote,
&remotelen); // remotelen if(recvbuf[0]=='n')break;
else if(recvbuf[0]=='y'){
sendto(sock,"Input r/p/s or q.",20, 0,(const struct sockaddr *) &remote, remotelen); while(1){
int handC,handS=(int) (3.0*random()/(RAND_MAX+1.0));//サーバの手 recvfrom(sock, recvbuf, sizeof(recvbuf), 0,
(struct sockaddr *) &remote, &remotelen); // remotelen //クライアントの手の決定 if (recvbuf[0] == 'q') break;
else if (recvbuf[0] == 'r') handC = 0; else if (recvbuf[0] == 'p') handC = 1; else if (recvbuf[0] == 's') handC = 2; else {
sendto(sock,"Erro,input again!",20,0,
(const struct sockaddr *) &remote, remotelen); continue;} if((handC==0 &&handS==1) ||(handC==1 &&handS==2) ||(handC==2 &&handS==0)){ winS++; if(winS==champion){
sendto(sock,"-You lost!Game over",20, 0,(const struct sockaddr *) &remote, remotelen);return 0;
}
sendto(sock,"You lost!Try again",20, 0,(const struct sockaddr *) &remote, remotelen); }
else if ((handC==0 &&handS==2) ||(handC==1 &&handS==0) ||(handC==2 &&handS==1)){ winC++;
if(winC==champion){
sendto(sock,"+You're the CHAMPION",20, 0,(const struct sockaddr *) &remote, remotelen);return 0;
}
sendto(sock,"You won!Next game",20, 0,(const struct sockaddr *) &remote, remotelen); }else if((handC==1&&handS==1)
||(handC==2&&handS==2) ||(handC==0&&handS==0)){
sendto(sock,"Same,input again",20, 0,(const struct sockaddr *) &remote, remotelen); }// y/n以外の文字を入力された場合
}
}else sendto(sock,"Erro,input y / n!",20,0,
(const struct sockaddr *) &remote, remotelen); }
return 0; }