/********************************************************/ /* */ /*対話エージェントを組み込んだチャットサーバその1 */ /*起動すると80番ポートで接続を待ち受けます */ /*使い方 */ /*ctrl-cキーの入力でプログラム停止 */ /* */ /* */ /********************************************************/ /*ヘッダファイルのインクルード */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") //#include <sys/socket.h> //#include <arpa/inet.h> //#include <unistd.h> #pragma warning(disable:4996) #pragma warning(disable:4244) /*記号定数の定義*/ #define SERVERPORTNUM 81 /*サーバのポート番号*/ #define MAXNUM 10/*最大接続数*/ #define MSGLENGTH 256 /*メッセージの最大長*/ #define SEED 32767/*乱数の種*/ #define LINES 15/*チャットの保存行数*/ #define MAXS 65535/*法要例文の最大数*/ #define CHARS 4/*例文検索キーのバイト数*/ #define OUTPUTFILE "2gram.txt"/*学習結果の出力ファイル*/ /*関数のプロトタイプの宣言*/ void startserver(int *serversocket);/*サーバを起動します*/ void sendtext(int cs,char *msg);/*返答メッセージの送信*/ void sendmes(int cs,char msgs[LINES][MSGLENGTH]);/*返答メッセージ作成*/ void sendhead(int cs);/*ヘッダ等の送付*/ void sendtail(int cs);/*htmlを完結させる*/ void sendbox(int cs);/*テキストボックスの送信*/ void getmes(int cs,char msgs[LINES][MSGLENGTH]); /*ブラウザから入力処理*/ void decoder(char msg[],char sjismsg[]);/*漢字コードを取り出す*/ void agent(char megs[LINES][MSGLENGTH]); /*対話エージェントの処理*/ int getreplydata();/*応答例文の初期化*/ void learn(char msgs[LINES][MSGLENGTH]);/*2-gramの学習*/ void searchword(int *i,char word[]);/*2-gram集から単語を探す*/ int cont(char msg[MSGLENGTH],int start);/*字種による分割*/ int iskanji(char chr);/*sjis漢字1byte目の判定*/ int rnd(int n);/*n以下の乱数を返す*/ /*外部変数*/ char reply[MAXS][MSGLENGTH];/*2-gram集*/ int replyno;/*単語数*/ /******************/ /* main()関数 */ /******************/ int main() { int serversocket;/*サーバ用ソケットディスクリプタ*/ int clientsocket;/*クライアント用ソケットディスクリプタ*/ struct sockaddr_in client;/*クライアントのアドレス*/ unsigned int cl;/*クライアント処理に必要な変数*/ char msgs[LINES][MSGLENGTH]={""};/*チャットのメッセージ*/ WSADATA wsaData; SOCKET sock; WSAStartup(MAKEWORD(2,0), &wsaData); /*応答例文の初期化*/ replyno=getreplydata(); /*乱数の初期化*/ srand(SEED); /*サーバ側のソケット準備*/ startserver(&serversocket); /*クライアントからの呼び出しへの対応*/ while(1){ cl=sizeof(client); if((clientsocket = accept(serversocket,(struct sockaddr *) &client,(int *)&cl))<0){ fprintf(stderr,"accept()呼び出しエラー\n"); exit(1);/*失敗*/} /*ブラウザからの入力処理*/ getmes(clientsocket,msgs); /*対話エージェントの処理*/ agent(msgs); /*返答メッセージの処理*/ sendmes(clientsocket,msgs); /*接続終了*/ closesocket(clientsocket); /*学習結果の出力*/ if(strlen(msgs[1])>0)/*直前のメッセージが空ではない*/ learn(msgs);/*学習処理*/ } WSACleanup(); } /***********************/ /* learn()関数 */ /*2-garmの学習 */ /***********************/ void learn(char msgs[LINES][MSGLENGTH]) { FILE *fp;/*出力ファイルのファイルポインタ*/ int start,end,i;/*配列の処理位置*/ char word[MSGLENGTH];/*処理用文字列*/ /*ファイル出力の準備*/ if((fp=fopen(OUTPUTFILE,"a"))==NULL){ fprintf(stderr,"fopen()呼び出しのエラー\n"); exit(1);/*ファイル書き込みに失敗*/ } /*2-gramを作成して学習する*/ start=0; printf("学習単語\n"); while(start<strlen(msgs[1])){ end=cont(msgs[1],start); for(i=0;i<end-start;++i)/*単語のコピー*/ word[i]=msgs[1][start+i]; word[i]='';/*単語の終わり*/ fprintf(fp,"%s\n",word);/*ファイル出力*/ printf(" word:%s\n",word); /*内部状態の更新*/ if(replyno<MAXS-1){ ++replyno; strcpy(reply[replyno],word); } start=end;/*次の単語*/ } /*区切り記号を出力*/ fprintf(fp,"\n"); strcpy(reply[++replyno],""); /*出力終了*/ fclose(fp); } /**********************/ /* cont()関数 */ /*字種による分割 */ /**********************/ int cont(char msg[MSGLENGTH],int start) { int i; int state; state=iskanji(msg[start]);/*字種の設定*/ for(i=start+2;i<strlen(msg);i+=2) if(state!=iskanji(msg[i]))break; return i; } /*************************/ /* iskanji()関数 */ /*sjis漢字1byte目の判定 */ /*************************/ int iskanji(char chr) { if((unsigned char)chr>=0x88)return 1; else return 0; } /******************************/ /* getreplydata()関数 */ /*応答例文の初期化 */ /******************************/ int getreplydata() { int lineno=0;/*読み込んだ行数*/ int chrno=0;/*書き込んだ文字の位置*/ int chr;/*読み込んだ文字*/ while(((chr=getchar())!=EOF)&&(lineno<MAXS)){ if(chr=='\n')/*改行*/ chr='';/*文字列の終わりとする*/ reply[lineno][chrno]=chr;/*文字をセット*/ ++chrno;/*次の文字*/ if(chrno>(MSGLENGTH-10)){/*長さを超過*/ reply[lineno][chrno]=''; chrno=0; ++lineno; } else if(chr==''){/*文字列が終端*/ chrno=0; ++lineno; } } reply[lineno][chrno]=''; if(lineno==0){/*2-gram集がない*/ fprintf(stderr,"2-gram集がありません。\n"); exit(1); } printf("単語集%d\n",lineno); return lineno;/*2-gram集の例文数を返す*/ } /**************************/ /* setreply()関数 */ /*応答処理 */ /**************************/ void setreply(char msgs[LINES][MSGLENGTH]) { static int i=0;/*例文を選択するための変数*/ int ptr;/*文字の位置*/ int length;/*直前の発話の長さ*/ char word[MSGLENGTH];/*直前の発話から切り出した単語*/ int counter;/*例文検索の回数を数える変数*/ /*直前の発話から適当な部分を切り出す*/ ptr=rnd(strlen(msgs[1]))/2*2;/*切り出し位置決定*/ strncpy(word,&(msgs[1][ptr]),CHARS);/*単語の切り出し*/ word[CHARS]='';/*文字列の終わり*/ /*2-gram検索のためのキーワードを表示*/ printf("keyword:%s\n",word); /*2-gram集から、上記を含む文を探す*/ strcpy(msgs[0],">"); searchword(&i,word); strcat(msgs[0],word);/*出力文字列の単語を連結*/ if(strlen(reply[i])<=0)return;/*空の単語*/ /*2-gramの連鎖処理*/ while(strlen(msgs[0])<MSGLENGTH){ printf("連鎖処理 msgs[0]:%s reply[]:%s\n",msgs[0],reply[i]); strcat(msgs[0],reply[i]);/*出力文字列に連鎖する単語を連結*/ strcpy(word,reply[i]); ++i;if(i>replyno)i=0; if(strlen(reply[i])<=0)break;/*空の単語*/ searchword(&i,word);/*次の連鎖を検索*/ } } /**************************/ /* searchword関数 */ /*2-gram集から単語を探す */ /**************************/ void searchword(int *i,char word[]) { int counter;/*例文検索の回数を数える変数*/ /*2-gram集から、上記を含む分を探す*/ counter=0; while(counter<=replyno){ if(strstr(reply[*i],word)!=NULL){ strcpy(word,reply[*i]); ++(*i);/*次の検索に備える*/ return;/*単語が見つかった*/ } ++(*i); if(*i>replyno)*i=0; ++counter; } /*なければ、適当に単語を返す*/ *i=rnd(replyno-1); strcpy(word,reply[*i]); ++(*i);/*次の検索に備える*/ } /*************************/ /* agent()関数 */ /*対話エージェントの処理 */ /*************************/ void agent(char msgs[LINES][MSGLENGTH]) { int i; int select; /*msgs[][]の更新*/ for(i=LINES-1;i>0;--i)strcpy(msgs[i],msgs[i-1]); /*発話*/ if(strlen(msgs[1])>0){/*直前のメッセージが空でない*/ setreply(msgs); printf("%s\n",msgs[0]); } } /****************************/ /* getmes()関数 */ /*ブラウザから入力処理 */ /****************************/ void getmes(int cs,char msgs[LINES][MSGLENGTH]) { char prev1,prev2,prev3;/*リクエスト終了判定用変数*/ char msg[MSGLENGTH];/*メッセージのバッファ*/ int i; prev1=prev2=''; i=0; while(((recv(cs,&msg[i],1,0))>0)&&(i<MSGLENGTH-1)){/*1文字ずつ読み取る*/ if((msg[i]=='\n')&&(prev1='\r'))break; /*改行が送られてきたら読み取り終了*/ prev2=prev1;prev1=msg[i];/*判定の準備*/ ++i; } msg[i]='';/*文字列の終わり*/ if(strchr(msg,'=')!=NULL){/*入力文字列が送られてきた*/ /*msgs[][]の更新*/ for(i=LINES-1;i>0;--i)strcpy(msgs[i],msgs[i-1]); decoder(strchr(msg,'='),msgs[0]); printf("%s\n",msgs[0]);/*サーバ画面に文字列を出力*/ } prev1=prev2=prev3=''; while(((recv(cs,msg,1,0))>0)&&(i<MSGLENGTH-1)){ /*1文字ずつ読み取る*/ if((msg[0]=='\n')&&(prev1=='\r')&&(prev2=='\n'))break; /*空行が送られてきたら読み飛ばし終了*/ prev3=prev2;prev2=prev1;prev1=msg[0];/*判定の準備*/ } } /*************************/ /* decoder()関数 */ /*漢字コードを取り出す */ /*************************/ void decoder(char msg[],char sjismsg[]) { int i=1,j=0; char hexch[3];/*2行の16進数(文字表現)*/ while(msg[i]!=' '){/*エンコードされたsjisコードを取り出す*/ if(i>MSGLENGTH-2)break;/*文字列の長さの上限*/ else if(msg[i]=='+'){ sjismsg[j]=' ';/*半角空白*/ ++i; } else if(msg[i]=='%'){/*エンコードされた全角文字*/ ++i; hexch[0]=msg[i]; hexch[1]=msg[i+1]; hexch[2]=''; sjismsg[j]=strtol(hexch,NULL,16); ++i;++i; } else {/*エンコードされていない文字*/ sjismsg[j]=msg[i]; ++i; } ++j; } sjismsg[j]='';/*文字列の終わり*/ } /*************************/ /* sendmes()関数 */ /*返答メッセージ作成 */ /*************************/ void sendmes(int cs,char msgs[LINES][MSGLENGTH]) { int i; /*htmlのヘッダ等の送付*/ sendhead(cs); /*テキストボックスの送信*/ sendbox(cs); /*これまでのデータ送信*/ for(i=0;i<LINES;++i){ sendtext(cs,msgs[i]); sendtext(cs,"<br>\r\n"); } /*htmlを結合させる*/ sendtail(cs); } /*************************/ /* sendbox()関数 */ /*テキストボックスの送信 */ /*************************/ void sendbox(int cs) { sendtext(cs, "<form action=\"cgi\"method=\"get\">"); sendtext(cs, "<input type=\"submit\"value=\"send\">\r\n"); sendtext(cs, "<input type=\"text\"name=\"text\"size=\"80\"maxlength=\"60\">\r\n"); sendtext(cs,"</form>\r\n"); } /**************************/ /* sendhead()関数 */ /*ヘッダ等の送付 */ /**************************/ void sendhead(int cs) { sendtext(cs, "<html><head><title>chat server</title></head><body>\r\n"); } /**************************/ /* sendtail()関数 */ /*htmlを完結させる */ /**************************/ void sendtail(int cs) { sendtext(cs,"</body></html>\r\n\r\n"); } /***************************/ /* sendtext()関数 */ /*返答メッセージの送信 */ /*および画面表示 */ /***************************/ void sendtext(int cs,char *msg) { send(cs,msg,strlen(msg),0);/*クライアントへの送信*/ } /***************************/ /* rnd()関数 */ /*n未満の乱数を返す */ /***************************/ int rnd(int n) { int rndno;/*生成する乱数*/ while((rndno=(double)rand()/RAND_MAX*n)==n); return rndno; } /***************************/ /* startserver()関数 */ /*サーバを起動します */ /***************************/ void startserver(int *serversocket) { struct sockaddr_in serveradd;/*サーバのアドレス*/ /*サーバ側のソケットの準備*/ if((*serversocket= socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0){ fprintf(stderr,"socket()呼び出しのエラー\n");exit(1); /*準備失敗*/} memset(&serveradd,0,sizeof(serveradd)); serveradd.sin_family=AF_INET; serveradd.sin_addr.s_addr=htonl(INADDR_ANY); serveradd.sin_port=htons(SERVERPORTNUM); if(bind(*serversocket,(struct sockaddr *)&serveradd, sizeof(serveradd))<0){ fprintf(stderr,"bind()呼び出しのエラー\n");exit(1); /*準備失敗*/} /*接続を待ち受ける*/ if(listen(*serversocket,MAXNUM)<0){ fprintf(stderr,"listen()呼び出しのエラー\n");exit(1); /*準備失敗*/} }