# 網路程式設計 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端的部分,當這兩端和在一起我就覺得有點小困難,因為要一次寫兩個程式,而且有時候一些小錯誤就會整個程式不能跑,但我還是很樂在其中,因為這就是程式的美妙之處,這次的課堂重點我覺得是中繼程式的開發,中繼程式是一個扮演很重要的角色,因為有了它才可以讓兩端連相通,當然實作的部分我覺得中繼程式是最難的,也是需要處理最多事情的角色。
### 作者: 廖經翰