質問編集履歴

2

問題文の要約

2019/08/08 19:02

投稿

tosakasimon
tosakasimon

スコア8

test CHANGED
File without changes
test CHANGED
@@ -1,66 +1,6 @@
1
- ### 複数クライアント接続対応のチャットサーバープログラム
2
-
3
-
4
-
5
- linux環境下のc言語を用いて複数クライアント接続対応のサーバプログラムを作成しているのですがあまりうまく行きません。他者が作成したクライアントプログラムとも通信をするために以下のようなプロトコル仕様があります。
6
-
7
- クライアントの仕様
8
-
9
-
10
-
11
- [状態c1](初期状態)ソケットを生成し(socket),引数で指定されたホストへ接続要求を出す(connect).[状態c2]へ移る.
12
-
13
-
14
-
15
- [状態c2](参加)サーバから17文字のメッセージを受信する(read).もしメッセージが接続受理("REQUEST ACCEPTED\n")ならば状態c3へ,さもなければ(接続拒否または何らかの例外)[状態c6]へ移る.
16
-
17
-
18
-
19
- [状態c3](ユーザ名登録)引数で指定されたユーザ名(最後に改行)を送信する.(write)続いて20文字の文字列を受信し(read),受信文字列が登録完了メッセージ("USERNAME REGISTERED\n")であれば,[状態c4]へ,さもなければ,エラーメッセージを表示し,[状態c6]へ移る.
20
-
21
-
22
-
23
- [状態c4](メッセージ送受信)標準入力とサーバ両方の入力を待ち(select),標準入力からの文字列はサーバへ送信し(write),サーバからの受信文字列は標準入力へ出力する(read).標準入力がEOFなら[状態c5]へ,さもなければ[状態c4]へ移る.
24
-
25
-
26
-
27
- [状態c5](離脱)ソケットを閉じて(close),クライアントプログラムを正常終了する(exit(0)).
28
-
29
-
30
-
31
- [状態c6](例外処理)ソケットを閉じて(close),クライアントプログラムを異常終了する(exit(1)).
32
-
33
-
34
-
35
- サーバ側のプロトコル仕様状態
36
-
37
-
38
-
39
- [s1](初期状態)ソケットを生成(socket)し,接続要求を待つように設定する(bind,listen).参加クライアント数kを0に初期化する.[状態s2]へ移る.
40
-
41
-
42
-
43
- [状態s2](入力待ち)接続要求を待つソケットからの入力,および,参加しているすべてのクライアントのソケットからの入力3を一定時間監視する(select).[状態s3]へ移る.
44
-
45
-
46
-
47
- [状態s3](入力処理)入力があったすべてのソケットに対して入力処理を行う.接続要求あり:[状態s4]へ移る.クライアントi(i= 1; : : : ; k)から入力あり:[状態s6]へ移る.入力があったソケットすべてに対して処理を終えたら[状態s2]へ移る.
48
-
49
-
50
-
51
- [状態s4](参加受け付け)接続要求を受理する(accept).次に,もし現在の参加クライアント数kがMAXCLIENTS未満であれば,接続受理のメッセージ(文字列"REQUEST ACCEPTED\n")を返し(write),クライアントソケット4をk+ 1番目のクライアントとして登録し,[状態s5]へ移る.さもなければ接続拒否のメッセージ(文字列"REQUEST REJECTED\n")を返し(write),クライアントソケットを閉じて(close),[状態s3]へ移る.
52
-
53
-
54
-
55
- [状態s5](ユーザ名登録)\ユーザ名+改行"を受信し(read),1~k番目のユーザのいずれかと同じ名前ならば,登録拒否のメッセージ(文字列"USERNAME REJECTED\n")を返し(write),登録拒否したユーザ名を分かりやすく標準出力に出力し,クライアントソケットを閉じて(close),[状態s3]へ移る.未登録のユーザならば,k+1番目のクライアントのユーザ名として登録する.登録完了メッセージ(文字列"USERNAME REGISTERED\n")を送信し(write),登録したユーザ名を分かりやすく標準出力に出力し,kを1増やす.[状態s3]へ移る.
56
-
57
-
58
-
59
- [状態s6](メッセージ配信)クライアントiから文字列を入力する(read).もしEOFならば[状態s7]へ移る.さもなければその入力文字列の先頭にユーザ名を追加して,全クライアントに対して送信し(write),[状態s3]へ移る.
60
-
61
-
62
-
63
- [状態s7](離脱処理)クライアントiのソケットを閉じる(close).離脱したユーザ名を分かりやすく標準出力に出力し,クライアント数kを1減らすなどの離脱処理を行い,[状態s3]へ移る
1
+ ### 複数クライアント接続
2
+
3
+
64
4
 
65
5
  ###問題
66
6
 

1

文法の大幅修正

2019/08/08 19:01

投稿

tosakasimon
tosakasimon

スコア8

test CHANGED
File without changes
test CHANGED
@@ -68,7 +68,9 @@
68
68
 
69
69
  ```
70
70
 
71
+ ユーザー名の登録などがうまく行かないです。空白文字で区切ったりしてはいるのですが。
72
+
71
- 状態遷移などがうく行かないです。サーバプログラムを実行し、クライアントから、サーバー側端末名、クライアント側のゲスト名引数にて実行したら、最初にREQUEST承認の是非やユーザー確認作業にはいるはずが、if(FD_ISSET〜以降のソケットからの入力処理ところ入ってしまいます。
73
+ 、クライアントがEOF入力したら、のユーザーののソケットを閉じて入力、受付待ちに戻るはずなですが、ユーザー名を大量出力し作業が強制的に終了てしまいます。
72
74
 
73
75
  ```
74
76
 
@@ -124,9 +126,11 @@
124
126
 
125
127
  struct hostent *cp;
126
128
 
127
- int clen,nbytes,kmax;
129
+ int clen,nbytes,fdmax,flag;
130
+
128
-
131
+ int Nbyte[5];
132
+
129
- char USERNAME[5][32];
133
+ char USERNAME[5][32],Nbuf[32];
130
134
 
131
135
  fd_set rfds;/* select()で用いるファイル記述子集合*/
132
136
 
@@ -182,184 +186,164 @@
182
186
 
183
187
  }
184
188
 
185
-
186
-
187
- while(1){/*入力を監視するファイル記述子の集合を変数rfdsにセットする*/
189
+ while(1){
188
190
 
189
191
  FD_ZERO(&rfds);
190
192
 
191
- FD_SET(0,&rfds);
192
-
193
193
  FD_SET(sock,&rfds);
194
194
 
195
- kmax=sock;
196
-
197
- clen=sizeof(clt);
198
-
199
-
200
-
201
- fprintf(stderr,"01\n");
202
-
203
- for(h=0;h<MAXCLIENTS;h++){
204
-
205
- if(csock[h]!=-1){
206
-
207
- FD_SET(csock[h],&rfds);
208
-
209
- if(csock[h] > kmax ) {
210
-
211
-
212
-
213
- kmax = csock[h];
214
-
215
- write(csock[h],"1connected\n",9);
216
-
217
- write(1,"connected\n",9);
218
-
219
- printf(" \n");
220
-
221
-
222
-
223
- if(sock==MAXCLIENTS-1){
224
-
225
- write(sock,"REQUEST REJECTED\n",17);
226
-
227
- close(sock);
228
-
229
- continue;
230
-
231
- }else if(sock<MAXCLIENTS-1){
232
-
233
- write(sock,"REQUEST ACCEPTED\n",17);
234
-
235
- read(sock,rbuf,sizeof(rbuf));
236
-
237
- for(int i=0;i<5;i++){
238
-
239
- if(strcmp(rbuf,USERNAME[i])==0){
240
-
241
- write(sock,"USERNAME REJECTED\n",18);
242
-
243
- close(sock);
244
-
245
- continue;
246
-
247
- }else{
248
-
249
- strcpy(USERNAME[i],rbuf);
250
-
251
- write(sock,"USERNAME REGISTERED\n",20);
252
-
253
- sock++;
254
-
255
- }
195
+ fdmax=sock;
196
+
197
+ for(int i=0;i<MAXCLIENTS;i++){
198
+
199
+ if(csock[i]>0){
200
+
201
+ if(csock[i]>fdmax){
202
+
203
+ fdmax=csock[i];
204
+
205
+ }
206
+
207
+ FD_SET(csock[i],&rfds);
208
+
209
+ }
210
+
211
+ }
212
+
213
+ tv.tv_sec = 1;
214
+
215
+ tv.tv_usec = 0;/*標準入力とソケットからの受信を同時に監視する*/
216
+
217
+ section3:
218
+
219
+ if(select(fdmax+1,&rfds,NULL,NULL,&tv)>0){
220
+
221
+ if(FD_ISSET(sock,&rfds)){
222
+
223
+ for(int i=0;i<MAXCLIENTS;i++){
224
+
225
+ clen=sizeof(clt);
226
+
227
+ if ( ( csock[i] = accept(sock,(struct sockaddr *)&clt,&clen) ) <0 ) {
228
+
229
+ perror("accept");
230
+
231
+ exit(2);
232
+
233
+ }
234
+
235
+ if(sock<MAXCLIENTS){
236
+
237
+ write(csock[i],"REQUEST ACCEPTED\n",17);
238
+
239
+ nbytes=read(csock[i],rbuf,sizeof(rbuf));
240
+
241
+ strncpy(Nbuf,rbuf,nbytes-1);
242
+
243
+ Nbyte[i]=nbytes;
244
+
245
+ flag=0;
246
+
247
+ for(int j=0;j<MAXCLIENTS;j++){
248
+
249
+ if(strcat(USERNAME[j],Nbuf)==0){
250
+
251
+ flag=1;
252
+
253
+ char name[32]="REJECTED";
254
+
255
+ write(csock[i],"USERNAME REJECTED\n",18);
256
+
257
+ strcat(name,Nbuf);
258
+
259
+ for(int k=0;k<MAXCLIENTS;k++)
260
+
261
+ write(csock[k],name,sizeof(name));
262
+
263
+ close(csock[i]);
264
+
265
+ goto section3;
256
266
 
257
267
  }
258
268
 
259
269
  }
260
270
 
261
-
271
+ if(flag==0){
272
+
273
+ write(csock[i],"USERNAME REGISTERED\n",20);
274
+
275
+ strncpy(USERNAME[i],Nbuf,Nbyte[i]-1);
276
+
277
+ char nll[]="\0";
278
+
279
+ strcat(USERNAME[i],nll);
280
+
281
+ write(1,USERNAME[i],sizeof(USERNAME[i]));
282
+
283
+ k++;
284
+
285
+ goto section3;
286
+
287
+ }
288
+
289
+
290
+
291
+
292
+
293
+ }
262
294
 
263
295
  }
264
296
 
265
297
  }
266
298
 
299
+ for(int i=0;i<MAXCLIENTS;i++){
300
+
301
+ if(FD_ISSET(csock[i],&rfds)){
302
+
303
+ if(read(csock[i],rbuf,sizeof(rbuf))!=0){
304
+
305
+ char STRING[64]="<";
306
+
307
+ strcat(STRING,USERNAME[i]);
308
+
309
+ strcat(STRING,"\0");
310
+
311
+ strcat(STRING,">");
312
+
313
+ strcat(STRING,rbuf);
314
+
315
+ for(int j=0;j<MAXCLIENTS;j++){
316
+
317
+ write(csock[j],STRING,sizeof(STRING));
318
+
319
+ }
320
+
321
+ }else{
322
+
323
+ fprintf(stderr,"01\n");
324
+
325
+ close(csock[i]);
326
+
327
+ for(int j=0;MAXCLIENTS;j++){
328
+
329
+ write(csock[j],USERNAME[i],sizeof(USERNAME[i]));
330
+
331
+ }
332
+
333
+ USERNAME[i][0]='\0';
334
+
335
+ k--;
336
+
337
+
338
+
339
+ }
340
+
341
+ }
342
+
343
+ }
344
+
267
345
  }
268
346
 
269
- /*監視す る待ち時間を1秒に設定*/
270
-
271
- tv.tv_sec = 1;
272
-
273
- tv.tv_usec = 0;/*標準入力とソケットからの受信を同時に監視する*/
274
-
275
- if(select(kmax+1,&rfds,NULL,NULL,&tv)>0) {
276
-
277
-
278
-
279
- if(FD_ISSET(sock,&rfds)) {
280
-
281
- for(int i=0;i<k;i++){
282
-
283
- fprintf(stderr,"02\n");
284
-
285
- if(csock[i]<0){
286
-
287
- if ( ( csock[i] = accept(sock,(struct sockaddr *)&clt,&clen) ) <0 ) {
288
-
289
- perror("accept");
290
-
291
- exit(2);
292
-
293
- }
294
-
295
- fprintf(stderr,"03\n");
296
-
297
- nbytes=read(csock[i],rbuf,sizeof(rbuf));
298
-
299
- for(int j=0;j<k;j++){
300
-
301
- fprintf(stderr,"04\n");
302
-
303
- char usr_1[128]="<";
304
-
305
- char usr_2[]="<";
306
-
307
- strcat(usr_1,USERNAME[i]);
308
-
309
- strcat(usr_1,usr_2);
310
-
311
- strcat(usr_1,rbuf);
312
-
313
- write(csock[j],usr_1,sizeof(usr_1));
314
-
315
- }
316
-
317
- }
318
-
319
- }
320
-
321
-
322
-
323
- }
324
-
325
- for(int i=0;i<k;i++){
326
-
327
- if(FD_ISSET(csock[i],&rfds)){
328
-
329
- if(read(csock[i],rbuf,sizeof(rbuf))!=EOF){
330
-
331
- close(csock[i]);
332
-
333
-
334
-
335
- }else{
336
-
337
- nbytes=read(csock[i],rbuf,sizeof(rbuf));
338
-
339
- if(nbytes<=0)exit(1);
340
-
341
-
342
-
343
- write(0,rbuf,nbytes);
344
-
345
- for(int j=0;j<k;j++){
346
-
347
- if(j!=i){
348
-
349
- write(csock[j],rbuf,nbytes);
350
-
351
- }
352
-
353
- }
354
-
355
- }
356
-
357
- }
358
-
359
- }
360
-
361
- }
362
-
363
347
  }
364
348
 
365
349
  close(sock);