# 網路程式設計 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 之類的,也非常謝謝老師可以那麼用心的錄製影片與認真教學,很期待您之後的課程!
### 作者: 廖經翰