/********************************************************/
/* */
/*対話エージェントを組み込んだチャットサーバその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);
/*準備失敗*/}
}