---
# System prepended metadata

title: 網路程式設計 HW05

---

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

### 作者: 廖經翰