socketを使ったプログラムでの文字列操作

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
大3生

socketを使ったプログラムでの文字列操作

#1

投稿記事 by 大3生 » 10年前

いつもお世話になっています。


今回は課題でsocketを使い、クライアントから整数を渡し、サーバーでそれらを加算し、クライアントに返す、という処理を行いたいのですが、全くうまく行きません。

まずソースコードを貼ります。
(サーバー)

コード:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>

#define SOCKET_NAME "my_socket"
#define N 256


int main()
{
	int fd1,fd2,size;

	char string[N];
	char numstock[N];
	int i,j,num,sum;

	struct sockaddr_un server,client;//serverとclientの宣言
	int clientlen=sizeof(struct sockaddr_un);


	if((fd1=socket(PF_UNIX,SOCK_STREAM,0))==-1)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}

	memset(&server,0,sizeof(struct sockaddr_un));
	server.sun_family=AF_UNIX;
	memcpy(server.sun_path,SOCKET_NAME,sizeof(SOCKET_NAME));

	unlink(SOCKET_NAME);

	if(bind(fd1,(struct sockaddr *)&server,sizeof(server))==-1)
	{
		perror("bind");
		exit(EXIT_FAILURE);
	}

	if(listen(fd1,1)==1)
	{
		perror("listen");
		exit(EXIT_FAILURE);
	}

	memset(&client,0,sizeof(struct sockaddr_un));

	if((fd2=accept(fd1,(struct sockaddr *)&client,&clientlen))==-1)
	{
		perror("accept");
		exit(EXIT_FAILURE);
	}




	do
	{
		if((size=read(fd2,string,sizeof(string)))==-1)
		{
			perror("read");
			exit(EXIT_FAILURE);
		}
	
		i=0;
		j=0;
		sum=0;
		
		printf("%s\n",string);
		printf("%d\n",sum);	
		while(string[i]!='\0')
		{

			if(isdigit(string[i]))
     			{
			
				numstock[j]=string[i];
				j++;
	
				if((string[i+1]==' ')||(string[i+1]=='\0')) 
                        	{
			
					num=atoi(numstock);
					printf("@ %d\n",num);
					sum+=num;
					printf("sum %d\n",sum);

					int a;
					for(a=0;a<=N;a++)
					{
						numstock[a]='\0';
					}
					
					j=0;
                                        num=0;				
				}
			}

			i++;
		}
				
	}
	while(size<=256);


	




	close(fd2);
	close(fd1);
	exit(EXIT_SUCCESS);

}
(クライアント)

コード:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>

#define SOCKET_NAME "my_socket"

int main()
{
	int fd1;
	int cint,size;
	int sum,num;
	char c;
	struct sockaddr_un server;

	if((fd1=socket(PF_UNIX,SOCK_STREAM,0))==-1)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}

	memset(&server,0,sizeof(struct sockaddr_un));
	server.sun_family=AF_UNIX;
	memcpy(server.sun_path,SOCKET_NAME,sizeof(SOCKET_NAME));

	if(connect(fd1,(struct sockaddr *)&server,sizeof(struct sockaddr_un))==-1)
	{
		perror("connect");
		exit(EXIT_FAILURE);
	}



	while((cint=getchar())!=EOF)
	{
		c=cint;	

		if((size=write(fd1,&c,sizeof(char)))==-1)
		{
			perror("write");
			exit(EXIT_FAILURE);
		}

	}

	
	
	close(fd1);
	exit(EXIT_SUCCESS);

}


2つをそれぞれ別の端末で起動した後にクライアント側で入力

125 10 19 2

と入力すると、サーバー側でこれらを全て加算し、クライアント側で

156

と表示させたいわけです。

この動作をクライアント側でctrl+dが入力されるまでループさせるのが目標ですが、現在加算自体がうまくいきません。
一応動作としては、サーバー側でstring配列にある文字列が数字であるとき、バッファnumstockに格納し、string[i+1]がスペース、または'\0'であった場合、atoiでnumstockの文字列を数字に変換し、それをsumに足す、というのになっています。

また、どのようにしてサーバーで加算した数をクライアントに表示させるかが思いつきません。


どうか解決策をおねがいします。

大3生

Re: socketを使ったプログラムでの文字列操作

#2

投稿記事 by 大3生 » 10年前

少しだけ改良できたのでサーバーの方のソースを載せます

これで各配列の要素は毎回初期化できたのですが、例えば入力に
1 4 5 6 1
とあった場合

たまに加算が
1+4+5+6
でとまる場合があります


コード:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>

#define SOCKET_NAME "my_socket"
#define N 256


int main()
{
	int fd1,fd2,size;
	int i,l,j,num,sum;

	struct sockaddr_un server,client;//serverとclientの宣言
	int clientlen=sizeof(struct sockaddr_un);


	if((fd1=socket(PF_UNIX,SOCK_STREAM,0))==-1)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}

	memset(&server,0,sizeof(struct sockaddr_un));
	server.sun_family=AF_UNIX;
	memcpy(server.sun_path,SOCKET_NAME,sizeof(SOCKET_NAME));

	unlink(SOCKET_NAME);

	if(bind(fd1,(struct sockaddr *)&server,sizeof(server))==-1)
	{
		perror("bind");
		exit(EXIT_FAILURE);
	}

	if(listen(fd1,1)==1)
	{
		perror("listen");
		exit(EXIT_FAILURE);
	}

	memset(&client,0,sizeof(struct sockaddr_un));

	if((fd2=accept(fd1,(struct sockaddr *)&client,&clientlen))==-1)
	{
		perror("accept");
		exit(EXIT_FAILURE);
	}




	do
	{
		char string[N]={0};
		if((size=read(fd2,string,sizeof(string)))==-1)
		{
			perror("read");
			exit(EXIT_FAILURE);
		}

		printf("%s\n",string);
	
		i=0;
		l=0;
		j=0;
		sum=0;

		char numstock[N][N]={{0},{0}};
			
		while(string[i]!='\0')
		{

			if(isdigit(string[i]))
     			{
			
				numstock[l][j]=string[i];
				j++;
	
				if((string[i+1]==' ')||(string[i+1]=='\0')) 
                        	{
			
					num=atoi(&numstock[l][0]);
					printf("@ %d\n",num);
					sum+=num;
					printf("sum %d\n",sum);
					
					j=0;
                                        num=0;
					l++;				
				}
			}

			i++;
		}
				
	}
	while(size<=256);


	




	close(fd2);
	close(fd1);
	exit(EXIT_SUCCESS);

}
		



アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: socketを使ったプログラムでの文字列操作

#3

投稿記事 by h2so5 » 10年前

ヒント: このコードでは1 4 5 6 1 の最後の1の後にはNULLが続いていると仮定していますが、本当にそうでしょうか。

大3生

Re: socketを使ったプログラムでの文字列操作

#4

投稿記事 by 大3生 » 10年前

文字列の最後が改行記号でした。これで加算はうまく行ったのですが、今度はクライアント側にこれを送ろうとしたのですが、どちらともsumが0になってしまいます。
以下に貼り付ける2つのコードの/**/部分を消すとサーバー側での加算はうまく行くのですが、/**/を取り払った場合、加算自体もおかしなことになります。

またクライアント側でctrl+cで強制終了させようと思ったのですが、ctrl+c入力でサーバー側が無限ループのような挙動になります。

(サーバー)

コード:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>

#define SOCKET_NAME "my_socket"
#define N 256


int main()
{
	int fd1,fd2,size;
	int i,l,j,num,sum;

	struct sockaddr_un server,client;//serverとclientの宣言
	int clientlen=sizeof(struct sockaddr_un);


	if((fd1=socket(PF_UNIX,SOCK_STREAM,0))==-1)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}

	memset(&server,0,sizeof(struct sockaddr_un));
	server.sun_family=AF_UNIX;
	memcpy(server.sun_path,SOCKET_NAME,sizeof(SOCKET_NAME));

	unlink(SOCKET_NAME);

	if(bind(fd1,(struct sockaddr *)&server,sizeof(server))==-1)
	{
		perror("bind");
		exit(EXIT_FAILURE);
	}

	if(listen(fd1,1)==1)
	{
		perror("listen");
		exit(EXIT_FAILURE);
	}

	memset(&client,0,sizeof(struct sockaddr_un));

	if((fd2=accept(fd1,(struct sockaddr *)&client,&clientlen))==-1)
	{
		perror("accept");
		exit(EXIT_FAILURE);
	}




	do
	{
		char string[N]={0};
		if((size=read(fd2,string,sizeof(string)))==-1)
		{
			perror("read");
			exit(EXIT_FAILURE);
		}

		printf("%s\n",string);
	
		i=0;
		l=0;
		j=0;
		sum=0;

		char numstock[N][N]={{0},{0}};
		char sumout[N];		

		while(string[i]!='\0')
		{

			if(isdigit(string[i]))
     			{
			
				numstock[l][j]=string[i];
				j++;
	
				if((string[i+1]==' ')||(string[i+1]=='\n')) 
                        	{
			
					num=atoi(&numstock[l][0]);
					printf("@ %d\n",num);
					sum+=num;
					printf("sum %d\n",sum);
					
					j=0;
                                        num=0;
					l++;				
				}
			}

			i++;
		}

		
		printf("%d sum before sprintf\n",sum);
		sprintf(sumout,"%d",sum);
		printf("%s this is strign\n",sumout);
		
		/*		
		if(write(fd2,sumout,sizeof(sumout))==-1)
                {
                	perror("write");
                	exit(EXIT_FAILURE);
                }
		*/
				
	}
	while(size<=256);


	




	close(fd2);
	close(fd1);
	exit(EXIT_SUCCESS);

}

(クライアント)

コード:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>
#include<signal.h>

#define SOCKET_NAME "my_socket"


void end(void)
{
	exit(EXIT_SUCCESS);
}


int main()
{

	if(signal(SIGINT,(void *)end)==SIG_ERR)//"ctrl+c"が入力された時にsendを呼び出す
        {
                perror("signal");
                exit(EXIT_FAILURE);
        }

	int fd1;
	int cint,size;
	char sumout[256];
	char c;
	struct sockaddr_un server;

	if((fd1=socket(PF_UNIX,SOCK_STREAM,0))==-1)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}

	memset(&server,0,sizeof(struct sockaddr_un));
	server.sun_family=AF_UNIX;
	memcpy(server.sun_path,SOCKET_NAME,sizeof(SOCKET_NAME));

	if(connect(fd1,(struct sockaddr *)&server,sizeof(struct sockaddr_un))==-1)
	{
		perror("connect");
		exit(EXIT_FAILURE);
	}



	while((cint=getchar())!=EOF)
	{
		c=cint;	

		if((size=write(fd1,&c,sizeof(char)))==-1)
		{
			perror("write");
			exit(EXIT_FAILURE);
		}

		/*			
		if(read(fd1,sumout,sizeof(sumout))==-1)
                {
                        perror("size");
                        exit(EXIT_FAILURE);
                }

		printf("%s\n",sumout);
		*/
		
	}

	
	
	close(fd1);
	exit(EXIT_SUCCESS);

}

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: socketを使ったプログラムでの文字列操作

#5

投稿記事 by h2so5 » 10年前

もうちょっと真面目にデバッグしましょう。
sizeof(sumout)じゃなくてstrlen(sumout)ではないですか。

大3生

Re: socketを使ったプログラムでの文字列操作

#6

投稿記事 by 大3生 » 10年前

>strlen(sumout)ではないか

read,writeは指定されたバイト数を読み込むということでsizeofを使っていましたがstrlenに変えてもクライアント、サーバー間でのやり取りはうまくいきませんでした


正直もうどこをどうデバッグしたらいいかわかりません。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: socketを使ったプログラムでの文字列操作

#7

投稿記事 by h2so5 » 10年前

クライアント終了時にサーバーがおかしくなる問題は、空の文字列をreadしたときにサーバーを終了するようにすれば解決すると思います。

大3生

Re: socketを使ったプログラムでの文字列操作

#8

投稿記事 by 大3生 » 10年前

空の文字列とは何でしょうか?
クライアントに入力cを出力させるようにしてみましたが、ctrl+cの時の入力が確認できませんでした。

そもそも自分がクライアント/サーバーのreadとwriteのタイミングを理解していないことがわかったので、確認させて下さい。


上のプログラムでは、

clientで入力がEOFになるまでwrite、ということは、入力が、123 3 3、の時は

'1','2','3',' ','3',' ','3','\n'のそれぞれに対してwriteが行われているという認識です
ここでも不明なのですが、最後の改行記号はどのタイミングでcに入るのでしょうか?これも教えていただけたらと思います。

ファイル記述子の動きとしては、
現在位置が先頭の時に'1'の書き込みでひとつズレ、以下同様との動きだと思っています。

ここでどうしても理解できないのが、上のクライアントの動きに対する、サーバーの動きです

サーバーのプログラムではreadを文字列で読みだしており、エラーでない時に読み込んだ文字列stringを文字列型として出力
stringの現在参照している要素が'\0'でない限り、文字列から数字列の変換を行う。
上記の動きをreadの帰り値が256バイト以下の時に繰り返す。

このサーバとクライアントの動きがどのようにして関係するのが全くわかっていません。

クライアントで書き込みが行われるたびにサーバーの動きに移るのだとしたら、出力するstringは
1
12
123
123
123 3
123 3
123 3
123 3 3
というふうになるはずです

どなたかクライアントとサーバーの動きの関係を詳しく説明してもらえないでしょうか?できれば自分のプログラムを例にとってもらえるとありがたいです。よろしくおねがいします

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: socketを使ったプログラムでの文字列操作

#9

投稿記事 by h2so5 » 10年前

大3生 さんが書きました:空の文字列とは何でしょうか?
クライアントに入力cを出力させるようにしてみましたが、ctrl+cの時の入力が確認できませんでした。
クライアント側のことではなく、サーバー側でreadしたときにクライアント側のソケットが終了していた場合、readが0を返すということです。
大3生 さんが書きました: '1','2','3',' ','3',' ','3','\n'のそれぞれに対してwriteが行われているという認識です
ここでも不明なのですが、最後の改行記号はどのタイミングでcに入るのでしょうか?これも教えていただけたらと思います。
改行するときに何のキーを押しましたか?

大3生

Re: socketを使ったプログラムでの文字列操作

#10

投稿記事 by 大3生 » 10年前

クライアント終了時にサーバーがreadを行うと0を返すのですね。ありがとうございます、クライアント終了時にサーバーも上手く終了させることが出来ました。

文字列入力の後のエンターが改行記号の入力に相当するということでしょうか
ということは、エンターは、getcharの開始のトリガーと、標準入力に改行記号を入力するという2つの役割を持っているということですか?

大3生

Re: socketを使ったプログラムでの文字列操作

#11

投稿記事 by 大3生 » 10年前

クライアントである条件1を満たした時にAをファイルに書き込みをして、
サーバーある条件2を満たした時、ファイルからAを読み込み、
サーバーがある条3件を満たした時にBをファイルに書き込み、
クライアントがある条件4を満たした時にファイルからBを読み込む

という制御がしたいのです。
どなたか教えて下さい。おねがいします。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: socketを使ったプログラムでの文字列操作

#12

投稿記事 by h2so5 » 10年前

大3生 さんが書きました: 文字列入力の後のエンターが改行記号の入力に相当するということでしょうか
ということは、エンターは、getcharの開始のトリガーと、標準入力に改行記号を入力するという2つの役割を持っているということですか?
そうです。

大3生

Re: socketを使ったプログラムでの文字列操作

#13

投稿記事 by 大3生 » 10年前

改良していった結果、クライアント側の入力を文字列にすることで目的の動作をするようになりました。
数字への変換と加算のプログラムはできていたので、自分の勘違いが招いた問題だったと思います。
問題解決の手助けに感謝します。

h2so5 さん、ありがとうございました。
またお世話になることがあると思いますが、よろしくおねがいします。

クライアント

コード:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>
#include<signal.h>
 
#define SOCKET_NAME "my_socket"
#define N 256
 
void end(void)
{
    exit(EXIT_SUCCESS);
}
 
 
int main()
{
 
    	if(signal(SIGINT,(void *)end)==SIG_ERR)//"ctrl+c"が入力された時にsendを呼び出す
        {
                perror("signal");
                exit(EXIT_FAILURE);
        }
 
    	int fd1;
    	int cint,size;
    	char sumout[256];
    	char c;
    	struct sockaddr_un server;
 
    	if((fd1=socket(PF_UNIX,SOCK_STREAM,0))==-1)
    	{
        	perror("socket");
        	exit(EXIT_FAILURE);
    	}
 
    	memset(&server,0,sizeof(struct sockaddr_un));
    	server.sun_family=AF_UNIX;
    	memcpy(server.sun_path,SOCKET_NAME,sizeof(SOCKET_NAME));
 
    	if(connect(fd1,(struct sockaddr *)&server,sizeof(struct sockaddr_un))==-1)
    	{
        	perror("connect");
        	exit(EXIT_FAILURE);
    	}
 
 
 
    	while(1)
    	{
		char sumout[N]={0};
		char sumin[N]={0};
	
        	gets(sumin);
 

        	if((size=write(fd1,sumin,sizeof(sumin)))==-1)
        	{
            		perror("write");
            		exit(EXIT_FAILURE);
        	}
 
        	else
		{          
        		if(read(fd1,sumout,sizeof(sumout))==-1)
                	{
                        	perror("size");
                        	exit(EXIT_FAILURE);
                	}
 	
			printf("sum is %s\n",sumout);
		}
        
    	}
 
    
    
    close(fd1);
    exit(EXIT_SUCCESS);
 
}
サーバー

コード:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>
 
#define SOCKET_NAME "my_socket"
#define N 256
 
 
int main()
{
    int fd1,fd2,size;
    int i,l,j,num,sum;
 
    struct sockaddr_un server,client;//serverとclientの宣言
    int clientlen=sizeof(struct sockaddr_un);
 
 
    if((fd1=socket(PF_UNIX,SOCK_STREAM,0))==-1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
 
    memset(&server,0,sizeof(struct sockaddr_un));
    server.sun_family=AF_UNIX;
    memcpy(server.sun_path,SOCKET_NAME,sizeof(SOCKET_NAME));
 
    unlink(SOCKET_NAME);
 
    if(bind(fd1,(struct sockaddr *)&server,sizeof(server))==-1)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
 
    if(listen(fd1,1)==1)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }
 
    memset(&client,0,sizeof(struct sockaddr_un));
 
    if((fd2=accept(fd1,(struct sockaddr *)&client,&clientlen))==-1)
    {
        perror("accept");
        exit(EXIT_FAILURE);
    }
 
 
 
 
    do
    {
        char string[N]={0};
        if((size=read(fd2,string,sizeof(string)))==-1)
        {
            perror("read");
            exit(EXIT_FAILURE);
        }
	else if(size==0)
	{
		perror("ctrl+d");
		exit(EXIT_FAILURE);
	}
 
        printf("%s\n",string);

        i=0;
        l=0;
        j=0;
        sum=0;
 
        char numstock[N][N]={{0},{0}};
        char sumout[N]={0};     
 
        while(string[i]!='\0')
        {
	
	    printf("now=[%c]\n",string[i]);
            if(isdigit(string[i]))
            {
            
                    numstock[l][j]=string[i];
                    j++;
    
                    if((string[i+1]==' ')||(string[i+1]=='\0')) 
                    {
            
                    num=atoi(&numstock[l][0]);
                    printf("num %d\n",num);
                    sum+=num;
                    printf("sum %d\n",sum);
                    
                    j=0;
                    num=0;
                    l++;                
                    }
            }
            i++;
        }
 
        
        printf("%d sum before sprintf\n",sum);
        sprintf(sumout,"%d",sum);
        printf("%s this is string\n",sumout);
        
              
        if(write(fd2,sumout,sizeof(sumout))==-1)
                {
                    perror("write");
                    exit(EXIT_FAILURE);
                }
	printf("\n");	
                
    }
    while(1);
 
 
    
 
 
 
 
    close(fd2);
    close(fd1);
    exit(EXIT_SUCCESS);
 
}

閉鎖

“C言語何でも質問掲示板” へ戻る