LinuxでC言語のチャットプログラムを作成しています。プログラムを作ってみましたがうまく動きません。悪い個所が分からないのでお力を貸して頂きたいです課題の提出が迫っているので、よろしくお願いいたします。
<server>
#include省略
#define PORT 5320
char *show_ip(char *ip_address);
int main(void){
int soc, acc, size, child[3],width,i,count,pos,ret;
char buffer[80];
struct sockaddr_in client, server;
struct sockaddr_storage from;
struct hostent *server_host;
socklen_t len;
fd_set mask;
char host_name[257];
int temp;
memset(host_name, 0, sizeof(host_name));
gethostname(host_name, 256);
server_host = gethostbyname(host_name);
printf("\n-------- informations of server ----------\n");
printf("Host name:%s\n", host_name);
printf("IP = %s\n", show_ip(server_host->h_addr));
printf("\n\n");
soc = socket(AF_INET, SOCK_STREAM, 0);
memset((char *)&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(PORT);
bind(soc, (struct sockaddr *)&server, sizeof(server));
size = sizeof(client);
listen(soc, 5);
for (i = 0; i < 3; i++){
child[i] = -1;
}
for (;;) {
FD_ZERO(&mask);
FD_SET(soc, &mask);
for (i = 0; i < 3; i++) {
if (child[i] != -1){
FD_SET(child[i], &mask);
if(child[i]+1 > width){
width = child[i]+1;
}
}
}
switch(select(soc+1, (fd_set *) &mask, NULL, NULL, NULL))
{
case -1:
perror("select");
break;
case 0:
break;
default:
if (FD_ISSET(soc, &mask)){
len = (socklen_t) sizeof(from);
if((acc = accept(child[i], (struct sockaddr *)&from, &len)) == -1){
if(errno != EINTR){
perror("accept");
}
}else{
if(child[0]&&child[1]&&child[2]!=-1){
fprintf(stderr,"child is full : cannot accept\n");
close(acc);
}else{
if(child[0] == -1){
child[0] = acc;
}else if(child[1] == -1){
child[1] = acc;
}else{
child[2] = acc;
}
}
}
}
for(i=0;i<3;i++){
if(child[i]!=-1){
if(FD_ISSET(child[i],&mask)){
memset(buffer, '\0', sizeof(buffer));
recv(child[i], buffer, 80, 0);
printf("%s> ", show_ip((char *)&client.sin_addr));
printf("%s", buffer);
if(strncmp(buffer, "exit", 4) == 0) break;
for(i=0;i<3;i++)
{
send(child[i],show_ip((char *)&client.sin_addr),80,0);
send(child[i],buffer,80,0);
close(child[i]);
}
(void) close(child[i]);
child[i] = -1;
}
}
break;
}
}
}
}
char *show_ip(char *ip_address){
static char ip[7];
char ipnum[4];
bcopy(ip_address, ipnum, 4);
sprintf(ip, "%u.%u.%u.%u",(unsigned char)ipnum[0], (unsigned char)ipnum[1], (unsigned char)ipnum[2],(unsigned char)ipnum[3]);
return ip;
}
<client>
#include省略
#define STDIN_FD 0
#define PORT 5320
int select_func(int sockfd);
void err_func(char *msg){
perror(msg);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv){
int sockfd, len;
char buf[BUFSIZ];
struct sockaddr_in serv;
unsigned short port;
if(argc != 3){
printf("usage: progname serv_ip serv_port\n");
exit(EXIT_FAILURE);
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_func("socket");
serv.sin_family = PF_INET;
port = (unsigned short)atoi(argv[2]);
serv.sin_port = htons(port);
inet_aton(argv[1], &(serv.sin_addr));
if(connect(sockfd, (struct sockaddr *)&serv, sizeof(struct sockaddr_in)) < 0)
err_func("connect");
do{
if(select_func(sockfd) == 0){
len = recv(sockfd, buf, BUFSIZ, 0);
buf[len] = '\0';
printf("-> %s\n", buf);
}else{
len = read(STDIN_FD, buf, BUFSIZ);
len = send(sockfd, buf, len, 0);
}
}while(strncmp(buf, "EXIT\r\n", 6) != 0 && strncmp(buf, "EXIT\n", 5) != 0);
close(sockfd);
return 0;
}
int select_func(int sockfd){
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sockfd, &rfds);
FD_SET(STDIN_FD, &rfds);
if(select(sockfd+1, &rfds, NULL, NULL, NULL) < 0)
err_func("select");
if(FD_ISSET(STDIN_FD, &rfds))
return STDIN_FD;
return sockfd;
}
A 回答 (2件)
- 最新から表示
- 回答順に表示
No.2
- 回答日時:
tsuduki123 さんによる指摘の他に気になったこととして、recvでのデータ受信を適切に行っていないように思われます。
おそらく、1回のsendで送信されたデータは必ず1回のrecvで受信できる、という仮定のもとにプログラムを作成したのではないでしょうか。このプログラムで使用しているTCPプロトコルでは、データがどこで切れるのかは保証されません。1回のsendで送信したデータが複数回のrecvに分割されて受信することもあれば、複数回のsendで送信されたものがまとめて1回のrecvで受信されることもあり得ます。
極端な例では、送信側で200バイトのデータをsendしたら受信側では1バイトのデータを200回recvすることになった、ということも可能性としてはゼロではないわけで、プログラムもそれを想定して作成する必要があります。
詳細については以下のURLを参照してください。
Windows Socket向けの解説ですが、LinuxでTCPソケットを使う場合も同じことが当てはまります。
Winsock 中級者向けの議論
3.4 - TCPのようなストリームプロトコルで、パケット単位の処理を強制するための正しい方法は?
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/intermedi …
TCP を有効に使うために
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/ …
ザ・間違いリスト
20.ストリームソケットで、メッセージフレームの区 切りが保持されると仮定すること
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/articles/ …
No.1
- 回答日時:
うまく動かない内容がわからないので
ロジックじゃなくてぱっと見て、直した方がいいところの指摘だけ。。
server側
1. socket作成の時はgetadrinfo(3)を利用しましょう。こっちの方が簡単だし、都合いいこと多いです。
2. setsockopt()で SO_REUSEADDR を設定してあげた方がいいんじゃないかと思います。
3. select(2)に指定する最初の引数は待ち受けるfdの最大+1です。
acceptのところで最大を保存して利用するようにしましょう
4. acceptに指定する最初の引数はlistenしてbindで待ち受けているソケットです。
5. fd系は-1が帰ってきたとき、errnoがEINTRとEWOULDBLOCKかチェックしていずれかなら正常系でcontinueさせます。
6. チャットなら、クライアントからrecvしたバイト数を保持してsendするときに利用しないとおかしな文字列を送信することになると思います。
7. static char ip[7]; 桁がたりないんじゃないかな
8. サーバーからクライアントへ送信するとき、送信元のクライアントは除外してあげた方がいい気がする
client側
1. こちらもgetadrinfo(3)を利用しましょう。
2. サーバーから受信するまでサーバー送信できない気がする
selectの部分をもっと丁寧に
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# C言語で再起関数とポインタを用いて文字列反転をする方法がわかりません。 4 2023/04/29 20:32
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# プログラミング c言語 4 2023/03/07 01:05
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
fgetsなどのときのstdinのバッ...
-
配列をnビットシフトする
-
CStringをwchar_tに変換したい
-
double型の値をchar配列に変換...
-
コンパイルエラー invalid ope...
-
variant型ってどのような仕組み...
-
strchr() の第2引数はなぜ int ...
-
C++ の FileCopy の設定が解り...
-
int main()の・・・
-
_TCHAR*での引数の読み込み
-
c++ 文字列を入力して、一文字...
-
-'0'の意味について
-
コマンドラインに入力されてい...
-
数字文字の出現回数を表示する...
-
文字列がNULLか空文字列かの判定
-
new
-
sprintfに同じ変数は使えるか
-
間接参照のレベルが異なっています
-
charからLPTSTRへの変換方法
-
c言語
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
fgetsなどのときのstdinのバッ...
-
charでの計算?
-
C言語のfor文です。 繰り返しの...
-
charからLPTSTRへの変換方法
-
文字列から空白を取り除きたい...
-
C言語の入力した文字を反転させ...
-
'const char *' 型は 'char *' ...
-
配列をnビットシフトする
-
str系関数を使わずに二つの文字...
-
int main()の・・・
-
atoi( ) の反対をやりたい
-
CStringをwchar_tに変換したい
-
c++ 文字列を入力して、一文字...
-
switch文で文字を比較すること...
-
干支のプログラム
-
3桁区切(コンマ)記号をつけ...
-
絶対パスからのファイル名の切...
-
間接操作のレベルとは
-
間接参照のレベルが異なっています
-
型変換
おすすめ情報