# 網路程式設計 HW06 學生: 1084837廖經翰 ## 1. 請任選以上二項聊天室延伸功能,由v2或v3延伸,實作一個聊天室系統 這次的作業是實作一個聊天室系統,並且新增一些功能在聊天室系統裡面,我這次的作業是使用C語言來實作聊天室系統,而且我是用v3的版本去改的聊天室系統並且有實作7種功能。 ### 執行流程 1. 一開始先執行server.c,當執行server.c的時候,使用者必須輸入聊天室的人數來設定開放讓幾個人連進聊天室,當輸入完後,server就會等待client端連入。 2. 接著要執行client_view.c,當執行好client_view.c的時候就可以接著執行client_input.c。 3. 當執行client_input.c之後,client_input.c會要使用者輸入自己的使用者名稱,輸入完之後就可以順利的開始聊天了。 4. 如果要3個人聊天的話,就是要先執行client_view.c,接著才是執行client_input.c,然後連續開3次之後就可以開始輸入使用者的名稱,輸入好就可以聊天了。 ### n人交談 一開始執行server的程式,可以讓使用者輸入來設定這個聊天室可以開放幾個人進來聊天,如果輸入10個人的話就可以容納10個人的聊天室。 執行畫面: <center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/KKqaXCk.png" />3個人的聊天室</center> <br> ### 不雅字馬掉 (Ex: FxxK ) 當使用者輸入 FUCK 或是 fuck 的時候,聊天室就會自動的把使用者輸入的不雅字給馬掉,變成 FxxK 或是 fxxk。 執行畫面: <center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/h1EPj8L.png" />不雅字被馬掉</center> <br> ### 當有人說bye,結束此聊天室 當使用者輸入 bye 的時候,聊天室就會自動結束然後終止程式,輸入bye的那位使用者也會自動結束聊天並且終止程式。 執行畫面: <center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/XmWasao.png" />說bye結束聊天室</center> <br> ### 語句翻譯 當使用者輸入特定語句的時候,聊天室就會自動幫該語句從英文翻譯中文,並且會把原語句跟翻譯過後的語句一起輸出。 特定語句有: Thank you 、 Good Morning 、 Good afternoon 、 Good Night 、 See you tomorrow 、 See you later 、 How are you? 、 I'm sorry 、 Great。 特定語句的翻譯為: Thank you(謝謝) 、 Good Morning(早安) 、 Good afternoon(午安) 、 Good Night(晚安) 、 See you tomorrow(明天見) 、 See you later(晚點見) 、 How are you?(你好嗎?) 、 I'm sorry(對不起) 、 Great(太棒了)。 執行畫面: <center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/rqa2VON.png" />翻譯語句</center> <br> ### client設定發言者名後,將來對話顯示發言者 當執行完client_view.c的時候,接著執行client_input.c時,使用者就必須要輸入使用者的名稱來辨識出該使用者的名子是什麼,而且在server端與client_view端也會顯示並且可以知道哪一位使用者在說話。 執行畫面: <center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/F763Iwa.png" />使用者名稱設定</center> <br> ### 傳送接收訊息的時間 當使用者每一次輸入訊息的時候,都可以知道目前的時間為多少,在server端與client_view端也會顯示每一則訊息是在哪一個時間點傳送與接收的。 執行畫面: <center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/eOa76Nc.png" />傳送接收時間</center> <br> ### 說不雅字被禁言 前面有一個功能是當說不雅字的時候就會馬掉(Ex: FxxK ),所以我就自己設定一個機制就是當使用者說3次不雅字的時候,聊天室就會把該使用者禁言10秒,再這10秒內該使用者都不可以輸入任何東西,10秒過後才可以正常的繼續輸入訊息。 執行畫面: <center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/71dz15T.png" />禁言機制</center> <br> ### 程式碼 #### **server.c** ```C= #include<stdio.h> #include<winsock.h> #include<time.h> int main() { int conn_num = 0; //設定連線的人數 printf("please input the people connect number: "); scanf( "%d", &conn_num ); SOCKET sd , clnt_sd0 , clnt_sd[ conn_num+1 ]; WSADATA wsadata; struct sockaddr_in serv , clnt0 , clnt[ conn_num+1 ]; int i , n; char str[100]; WSAStartup( 0x101, &wsadata ); sd = socket( AF_INET , SOCK_STREAM , 0 ); //開啟一個TCP socket serv.sin_family = AF_INET; //使用IPv4 serv.sin_port = htons( 1234 ); //設定port number serv.sin_addr.s_addr = inet_addr( "127.0.0.1" ); //設定ip address bind( sd, (struct sockaddr *) &serv, sizeof(serv) ); //將socket綁定serv的資訊 listen( sd , 5 ); //server開始監聽或等待client printf( "Server waits...\n" ); for( int i = 1 ; i <= conn_num ; i++ ) { int clnt_len = sizeof( clnt[i] ); clnt_sd[i] = accept( sd, (struct sockaddr *) &clnt[i] , &clnt_len ); //server收到連線 printf( "%d people connection...\n" , i ); //顯示有幾個人連入 } int clnt_len0 = sizeof( clnt0 ); while(1) { clnt_sd0 = accept( sd, (struct sockaddr *) &clnt0, &clnt_len0 ); //server收到連線 n = recv( clnt_sd0, str, 100, 0 ); //接收client的訊息 if ( n <= 0 ) break; closesocket( clnt_sd0 ); //目前電腦時間的顯示 time_t now = time(NULL); struct tm *t = localtime( &now ); char time_buffer[80]; strftime( time_buffer , 80 , "%H:%M:%S", t ); printf( "[%s] ", time_buffer ); printf( "%s\n" , str ); //使用者輸入的訊息 //傳送給每一個client_view for( int i = 1 ; i <= conn_num ; i++ ) { send( clnt_sd[i], str, strlen(str)+1 , 0 ); } //當使用者說bye的時候就結束聊天室 char *find = strstr( str , "bye" ); if( find != NULL ) { break; } } //關閉socket的服務 closesocket(sd); closesocket(clnt_sd0); //關閉所有的client_view服務 for( int i = 1 ; i <= conn_num ; i++ ) { closesocket(clnt_sd[i]); } closesocket(clnt_sd0); WSACleanup(); printf("\n"); printf("Room is closed...\n"); system("pause"); return 0; } ``` --- #### **client_view.c** ```C= #include<stdio.h> #include<winsock.h> #include<time.h> int main() { SOCKET sd; WSADATA wsadata; struct sockaddr_in serv; int i , n; char str[100]=""; WSAStartup( 0x101, &wsadata ); sd = socket( AF_INET , SOCK_STREAM , 0 ); //開啟一個TCP socket serv.sin_family = AF_INET; //使用IPv4 serv.sin_port = htons( 1234 ); //設定port number serv.sin_addr.s_addr = inet_addr( "127.0.0.1" ); //設定ip address connect( sd , (struct sockaddr*)&serv , sizeof(serv) ); printf("Connect to server.\n"); while(1) { memset( str , 0 , 100 ); //先初始化str的內容 n = recv( sd , str , 100 , 0 ); //接收server傳的訊息 if ( n <= 0 ) break; //目前電腦時間的顯示 time_t now = time(NULL); struct tm *t = localtime( &now ); char time_buffer[80]; strftime( time_buffer , 80 , "%H:%M:%S", t ); printf( "[%s] ", time_buffer ); printf( "%s\n", str ); //輸出server傳來的訊息 //當使用者說bye的時候就結束該服務 char *find = strstr( str , "bye" ); if( find != NULL ) { break; } } //關閉socket的服務 closesocket(sd); WSACleanup(); printf("\n"); system("pause"); return 0; } ``` --- #### **client_input.c** ```C= #include<stdio.h> #include<winsock.h> #include<time.h> #include<windows.h> //翻譯前的字串 char word[200][400] = { "Thank you","Good Morning", "Good afternoon", "Good Night", "See you tomorrow", "See you later", "How are you?", "I'm sorry", "Great"}; //翻譯後的字串 char trans_word[200][400] = {"Thank you(謝謝)","Good Morning(早安)", "Good afternoon(午安)", "Good Night(晚安)", "See you tomorrow(明天見)", "See you later(晚點見)", "How are you?(你好嗎?)", "I'm sorry(對不起)", "Great(太棒了)"}; //用來替換不雅字的功能 int replace_word( char *str , int counter ) { char *result = NULL; char *temp = str; char new_str[ strlen(str)+1 ]; //新的字串 int index = 0; //new_str的index //使用 strstr() 在字串中搜尋不雅字 while( ( result = strstr( temp , "fuck" ) ) != NULL || ( result = strstr( temp , "FUCK" ) ) != NULL ) { counter = counter + 1; //當有說不雅字的時候,計數器就增加 int len = result - temp; //計算"fuck"前面部分的長度 strncpy( &new_str[index] , temp , len ); //將"fuck"前面的部分複製到新字串中 index = index + len; //更新索引位置 if( ( result = strstr( temp , "FUCK" ) ) != NULL ) { strcpy( &new_str[index] , "FxxK" ); //將"FxxK"複製到新字串中 } else if( ( result = strstr( temp , "fuck" ) ) != NULL ) { strcpy( &new_str[index] , "fxxk" ); //將"fxxk"複製到新字串中 } index = index + 4; //更新索引位置 temp = result + 4; //更新搜尋起點 } strcpy( &new_str[index] , temp ); //將剩下的部分複製到新字串中 strcpy( str , new_str ); //將新字串複製回原始字串中 return counter; } //用來翻譯語句的功能 void trans_fun( char *str ) { char *result2 = NULL; char *temp2 = str; char new_str2[ strlen(str)+1 ]; //新的字串 int index2 = 0; //new_str2的index for( int i = 0 ; i <= 8 ; i++ ) { while( ( result2 = strstr( temp2 , word[i] ) ) != NULL ) { int len2 = result2 - temp2; //計算要翻譯語句的前面部分之長度 strncpy( &new_str2[index2] , temp2 , len2 ); //將要翻譯語句的前面之部分複製到新字串中 index2 = index2 + len2; //更新索引位置 strcpy( &new_str2[index2] , trans_word[i] ); //將要翻譯的語句複製到新字串中 index2 = index2 + strlen( trans_word[i] ); //更新索引位置 temp2 = result2 + strlen( trans_word[i] ); //更新搜尋起點 } } strcpy( &new_str2[index2] , temp2 ); //將剩下的部分複製到新字串中 strcpy( str , new_str2 ); //將新字串複製回原始字串中 } int main() { SOCKET sd; WSADATA wsadata; struct sockaddr_in serv; int i , n; char mix[100]=""; //存字串合併的結果 char user_name[100]=""; //存該使用者的名稱 char str[100]=""; int block_counter = 0; //不雅字的計數器 WSAStartup( 0x101, &wsadata ); serv.sin_family = AF_INET; //使用IPv4 serv.sin_port = htons( 1234 ); //設定port number serv.sin_addr.s_addr = inet_addr( "127.0.0.1" ); //設定ip address //讓使用者輸入名稱 printf( "please input the user name: "); gets( user_name ); printf(" \n"); printf("歡迎 %s 進入這個聊天室!\n" , user_name ); while(1) { printf(" \n"); sd = socket( AF_INET , SOCK_STREAM , 0 ); //開啟一個TCP socket //目前電腦時間的顯示 time_t now = time(NULL); struct tm *t = localtime( &now ); char time_buffer[80]; strftime( time_buffer , 80 , "%H:%M:%S", t ); printf( "[%s] ", time_buffer ); //使用者輸入要傳送的訊息 printf( "Input: " ); gets( str ); block_counter = replace_word( str , block_counter ); //不雅話馬掉的功能 trans_fun( str ); //翻譯字串的功能 strcat( mix , user_name ); //合使用者名稱 strcat( mix , ": " ); //合冒號 strcat( mix , str ); //合使用者要傳送的訊息 connect( sd , (struct sockaddr*)&serv , sizeof(serv) ); send( sd , mix , strlen( mix )+1 , 0 ); //最後把全部合起來的結果送給server端 printf( "[%s] ", time_buffer ); printf( "Send: %s\n", str ); //當使用者說3次不雅字的時候,就會被禁言10秒鐘 if( block_counter >= 3 ) { printf( " \n" ); printf( "------你被禁言了10秒------"); printf( " \n" ); block_counter = 0; Sleep(10000); // 10s } //當使用者說bye的時候就結束該服務 char *find = strstr( str , "bye" ); if( find != NULL ) { break; } //初始化變數 memset( mix , 0 , 100 ); memset( str , 0 , 100 ); //關閉socket closesocket(sd); } //關閉整個連線的服務 WSACleanup(); printf("\n"); system("pause"); return 0; } ``` ## 2. 本週心得 這次老師教的課程為聊天室,因為生活周遭都有聊天室的軟體可以使用,所以本身就很好奇整個聊天室的運作架構,因為這次老師的教學讓我可以自己可以實作出一個簡單的聊天室,並且知道整個的程式流程與架構,而且這次的作業需要增加一些功能到聊天室,我覺得非常有趣,因為可以新增一些非常有趣的功能,而且實作出來也非常的開心,但是就是要寫3個不同的端才可以執行整個聊天室,這確實有點小複雜與辛苦,但最後有完成這次的作業並且加一些很有趣的功能我覺得很不錯,但是以目前教的聊天室架構覺得寫程式有點辛苦,所以我非常期待老師以後教的不同架構,例如: threading 或是 multiprocessing 之類的,也非常謝謝老師可以那麼用心的錄製影片與認真教學,很期待您之後的課程! ### 作者: 廖經翰