# 網路程式設計 HW05 學生: 1084837廖經翰 ## 1. 連至2個Server 第一個作業要將client連到chargen server並且將chargen server的字元每秒送給discard server,和觀察每一秒的流量。 執行畫面: <center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/HloZgu8.png" /></center> <br> 程式碼: ```C= #include<stdio.h> #include<winsock.h> #include<string.h> #include<time.h> #define MAXLINE 1024 int main() { SOCKET discard_sd , chargen_sd; struct sockaddr_in serv; char str[MAXLINE]; int n = 0; int port = 0; int total = 0; int start = 0, end = 0; //set WSA and initial WSAStartup and test error WSADATA wsadata; if( WSAStartup( 0x101, (LPWSADATA) &wsadata ) != 0 ) { fprintf( stderr, "WSAStartup() fails!" ); exit(1); } strcpy( str , "127.0.0.1" ); //IP為本機 //Chargen server設定 port = 19; serv.sin_family = AF_INET; //設定IPv4 serv.sin_addr.s_addr = inet_addr( str ); //設定IP serv.sin_port = htons( port ); //port if( (chargen_sd = socket(AF_INET, SOCK_STREAM, 0) ) == SOCKET_ERROR ) //開啟一個socket的錯誤測試 { fprintf( stderr, "Chargen server can't open TCP socket\n" ); exit(1); } if( connect(chargen_sd, (LPSOCKADDR) &serv, sizeof(serv) ) == SOCKET_ERROR ) //socket連線之錯誤測試 { fprintf(stderr, "Chargen server can't connect to server\n"); exit(1); } //Discard server設定 port = 9; serv.sin_family = AF_INET; //設定IPv4 serv.sin_addr.s_addr = inet_addr( str ); //設定IP serv.sin_port = htons( port ); //port if( (discard_sd = socket(AF_INET, SOCK_STREAM, 0) ) == SOCKET_ERROR ) //開啟一個socket的錯誤測試 { fprintf( stderr, "Discard server can't open TCP socket\n" ); exit(1); } if( connect(discard_sd, (LPSOCKADDR) &serv, sizeof(serv) ) == SOCKET_ERROR ) //socket連線之錯誤測試 { fprintf(stderr, "Discard server can't connect to echo server\n"); exit(1); } start = time(NULL); //設定計時開始的時間 while(1) { //接收錯誤檢測 if( ( n = recv( chargen_sd, str, MAXLINE, 0 ) ) == 0 ) { fprintf( stderr, "Chargen server connection closed\n" ); break; } else if ( n == SOCKET_ERROR ) { fprintf( stderr, "Chargen server recv() error!\n" ); break; } //傳送錯誤檢測 if( ( n = send( discard_sd , str , strlen(str) , 0 ) ) == 0 ) { fprintf( stderr, "Discard server connection closed\n" ); break; } else if( n == SOCKET_ERROR ) { fprintf( stderr, "Discard server send() error!\n" ); break; } total = total + n; //送往Discard Server的總Byte數量 end = time(NULL); //設定計時結束的時間 if( difftime( end , start ) > 1.0 ) //每秒送給Discard Server一次 { printf( "send Byte/sec: %d\n" , total ); total = 0; //總數量初始化 start = time(NULL); //開始時間初始化 } } closesocket(chargen_sd); //關閉Chargen server closesocket(discard_sd); //關閉Discard server WSACleanup(); //關閉整個WSA服務 printf("\n"); system("pause"); return 0; } ``` ## 2. 中繼程式轉送 第二個作業要將client連到proxy server並且會有兩個功能,一個為時間查詢,另一個為IP查詢。從client連線到proxy server的port我是使用1001,使用者只要在client一開始輸入1001就可以連線到proxy server。 ### 時間查詢 只要按下1就可以使用時間查詢的服務,這樣會先從client端去問proxy server,proxy server再跑去問Simple TCPIP datetime server的時間,問到時間之後再回傳給client。 執行畫面: <center>Client</center> <center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/PdllLkL.png" /></center> <br> <center>proxy server</center> <center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/408RI3Y.png" /></center> <br> ### IP查詢 只要按下2就可以使用IP查詢的服務,這樣會先從client端去問proxy server,proxy server再去使用gethostbyaddr()的方式查詢hostname,最後回傳結果給client。 執行畫面: <center>Client</center> <center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/JLm7hy8.png" /></center> <br> <center>proxy server</center> <center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/JSiSFBA.png" /></center> <br> client端程式碼: ```C= #include<stdio.h> #include<winsock.h> #include<string.h> #include<time.h> #define MAXLINE 1024 int main() { SOCKET send_proxy_sd , recv_proxy_sd; struct sockaddr_in serv; char hostname[50] = ""; char str[MAXLINE] = ""; char search_ip[100]=""; char txt[20]=""; int n = 0; int port = 0; //set WSA and initial WSAStartup and test error WSADATA wsadata; if( WSAStartup( 0x101, (LPWSADATA) &wsadata ) != 0 ) { fprintf( stderr, "WSAStartup() fails!" ); exit(1); } printf( "Input connect to Port: " ); scanf( "%d", &port ); printf("\n"); send_proxy_sd = socket( AF_INET , SOCK_STREAM , 0 ); //開啟一個TCP socket //sockaddr_in的結構(serv) serv.sin_family = AF_INET; serv.sin_addr.s_addr = inet_addr( "127.0.0.1" ); //server的IP位址(連到本機端) serv.sin_port = htons( port ); connect( send_proxy_sd , (LPSOCKADDR)&serv , sizeof(serv) ); //連接至 printf("connect proxy access!\n"); printf("\n"); while(1) { printf("+++++++++++++++++++++++++++++++++++\n"); printf("| 請輸入服務的代號(1~2)以執行服務 |\n"); printf("| 1. Datetime服務 |\n"); printf("| 2. IP查詢DNS服務 |\n"); printf("|---------------------------------|\n"); printf("| 結束整個程式 (quit) |\n"); printf("+++++++++++++++++++++++++++++++++++\n"); scanf( "%s", txt ); send( send_proxy_sd , txt, strlen(txt)+1, 0); //傳送str至server if( strcmp( txt , "quit" ) == 0 ) { break; } else if( strcmp( txt , "1" ) == 0 ) { n = recv( send_proxy_sd, str, 100, 0); //接收client傳送的內容 printf( "Recv: %s" , str ); } else if( strcmp( txt , "2" ) == 0 ) { char ask[100] = "ask://"; printf("\n"); printf("請輸入要查找的IP: "); scanf( "%s", search_ip ); strcat( ask , search_ip ); send( send_proxy_sd , ask, strlen(ask)+1, 0); //傳送str至server n = recv( send_proxy_sd, hostname, 100, 0); //接收client傳送的內容 printf("Recv: %s" , hostname ); printf("\n"); } printf("\n"); } closesocket( send_proxy_sd ); //關閉TCP socket WSACleanup(); //關閉整個WSA服務 printf("\n"); system("pause"); return 0; } ``` proxy server程式碼: ```C= #include<stdio.h> #include<string.h> #include<winsock.h> #include<conio.h> #define MAXLINE 1024 char time_data[20] = ""; int main() { SOCKET recv_client_sd, send_client_sd , clnt_sd; WSADATA wsadata; struct sockaddr_in serv , clnt; char null[100] = "NULL"; int n; char str[100]; //接收client所傳的內容 WSAStartup( 0x101, &wsadata ); recv_client_sd = socket( AF_INET , SOCK_STREAM , 0 ); //開啟一個TCP socket serv.sin_family = AF_INET; //使用IPv4 serv.sin_port = htons( 1001 ); //設定port number serv.sin_addr.s_addr = inet_addr( "127.0.0.1" ); //設定ip address bind( recv_client_sd, (struct sockaddr *) &serv, sizeof(serv) ); //將socket綁定serv的資訊 listen( recv_client_sd,5); //server開始監聽或等待client while(1) { int clnt_len = sizeof( clnt ); printf( "echo_srv: waiting for client...\n" ); printf("\n"); clnt_sd = accept( recv_client_sd, (struct sockaddr *) &clnt, &clnt_len ); //server收到連線 while(1) { n = recv( clnt_sd, str, 100, 0); //接收client傳送的內容 str[n] = '\0'; //加入停止符號 if( strcmp( str , "quit" ) == 0 ) { break; } else if( strcmp( str , "1" ) == 0 ) { printf("ask time"); service3(); printf( "%s" , time_data ); send(clnt_sd, time_data, strlen( time_data )+1, 0); //把目前的時間回傳給client端 printf("\n"); } else if( strcmp( str , "2" ) == 0 ) { printf("ask IP\n"); n = recv( clnt_sd, str, 100, 0); //接收client傳送的內容 str[n] = '\0'; //加入停止符號 int ip_size = strlen(str); char ip_str1[ip_size+1]; for(int i = 6 ; i <= n ; i++) { ip_str1[i-6]=str[i]; } struct in_addr sAddr; LPHOSTENT hp; sAddr.s_addr = inet_addr( ip_str1 ); hp = gethostbyaddr( (LPSTR) &sAddr , sizeof(sAddr) , AF_INET ); if( hp == NULL ) { printf("NULL"); printf("\n"); send(clnt_sd, null , strlen( null )+1, 0); //把目前的時間回傳給client端 printf("\n"); continue; } printf("host name: %s\n", hp -> h_name ); printf("echo_srv: %s\n", hp -> h_name ); send(clnt_sd, hp -> h_name, strlen( hp -> h_name )+1, 0); //把目前的時間回傳給client端 printf("\n"); } else { break; } } closesocket( clnt_sd ); //關閉server與該client的socket } closesocket( clnt_sd ); //關閉server與該client的socket //關閉server的服務 closesocket( recv_client_sd ); WSACleanup(); system("pause"); return 0; } //Daytime服務 void service3() { printf("\n"); //set WSA and initial WSAStartup WSADATA wsadata; WSAStartup( 0x101, (LPWSADATA) &wsadata ); //呼叫WSAStartup()註冊WinSock DLL的使用 int n = 0; //存接收資料的長度 SOCKET sd; struct sockaddr_in serv; sd = socket( AF_INET , SOCK_STREAM , 0 ); //開啟一個TCP socket //sockaddr_in的結構(serv) serv.sin_family = AF_INET; serv.sin_addr.s_addr = inet_addr( "127.0.0.1" ); //server的IP位址(連到本機端) serv.sin_port = htons( 13 ); //使用服務的port number connect( sd , (LPSOCKADDR)&serv , sizeof(serv) ); //連接至Daytime server //接收的部分 n = recv(sd, time_data, MAXLINE, 0); //由server接收 n = 0; //初始化接收資料的長度 closesocket(sd); //關閉TCP socket WSACleanup(); // 結束WinSock DLL的使用 } ``` ## 3. 本週心得 這次的課程老師講解的是client端與server端的部分,當這兩端和在一起我就覺得有點小困難,因為要一次寫兩個程式,而且有時候一些小錯誤就會整個程式不能跑,但我還是很樂在其中,因為這就是程式的美妙之處,這次的課堂重點我覺得是中繼程式的開發,中繼程式是一個扮演很重要的角色,因為有了它才可以讓兩端連相通,當然實作的部分我覺得中繼程式是最難的,也是需要處理最多事情的角色。 ### 作者: 廖經翰