---
# System prepended metadata

title: net w11
tags: [network]

---

# 網路程式設計第十一周

## 觀念題
### 第一題: Select優點
- 使用select()與上週的non-blocking比較，有何優點？
    1. 不用一直查看 => 有資料再收
    2. 降低CPU使用率
### 第二題: select使用方法
- 請說明使用select()的四個步驟。
    - 以現實生活舉例
        1. 設定保全
            - 命令保全要去看那些地方
        2. 啟動保全
            - 沒事 => 那我就繼續休息
            - 有事 => 過去處理
    - 換作是四大步驟
        1. 設定
            1. 清空檢查表
            ```C
            FD_ZERO(&readfd);
            ```
            2. 加入檢查表
            ```C
            FD_SET(sd,&readfd);
            ```
        2. 啟動保全
            3. 監看
            ```C
            select(&read,&write,&except,&timeout)
            ```
            4. 結果報告
                - ISSET + if
                ```C
                if (ISSET(socket1, &read)){...}
                ```
### 第三題: 限制
- 程式碼變得更加臃腫
- Client 端無法同時輸入與接收socket
## 程式題
### 第四題: Daytime Server
- 請將echo server改為daytimes server，server每秒會傳送當前日期與時間給目前所有連入的client。
- 程式碼
    - Server
        :::spoiler C
        ```C=
        #include <stdio.h>
        #include <winsock.h>
        #include <time.h>
        #define MAXCLIENT 5
        #define BUFFER_SIZE 1024
        int main(){
            int client_num = 0;

            // Initialize Winsock
            WSADATA wsadata;
            WSAStartup(0x101, &wsadata);

            /*
                Server Information
                    IP: 127.0.0.1
                    PORT: 1234
            */
            struct sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr("127.0.0.1");
            server.sin_port = htons(1234);

            // Create a socket
            SOCKET server_socket = socket(AF_INET, SOCK_STREAM, 0);
            SOCKET client_socket[MAXCLIENT]={0};

            // Bind the socket
            bind(server_socket, (LPSOCKADDR)&server, sizeof(server));
            listen(server_socket, 5);

            // Check table build
            fd_set writefds,readfds;

            while (TRUE){
                // Clear the socket set
                FD_ZERO(&readfds);
                FD_ZERO(&writefds);

                // Add socket to set
                FD_SET(server_socket, &readfds);

                for (int i = 0; i< MAXCLIENT; i++){
                    if (client_socket[i]>0){
                        FD_SET(client_socket[i],&writefds);
                    }
                }

                // Select the socket
                int activity = select(0, &readfds, &writefds, NULL, NULL);

                // Wake up by select
                if (activity == SOCKET_ERROR){
                    printf("Select Error\n");
                    exit(1);
                }


                // New connection
                if (FD_ISSET(server_socket, &readfds)){
                    struct sockaddr_in client;
                    int client_size = sizeof(client);
                    SOCKET new_socket = accept(server_socket, (LPSOCKADDR)&client, &client_size);
                    printf("New connection: socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(client.sin_addr) , ntohs(client.sin_port));
                    BOOL isFull = FALSE;
                    for (int i = 0; i< MAXCLIENT; i++){
                        if (client_socket[i] == 0){
                            isFull = TRUE;
                            client_socket[i] = new_socket;
                            client_num++;
                            printf("The %d client socket is in cli_sd[%d]\n",client_num,i);
                            break;
                        }
                    }
                    if (!isFull){
                        printf("Server is full\n");
                        send(new_socket, "Server is full", strlen("Server is full")+1, 0);
                        closesocket(new_socket);
                    }
                }
                // if client can be written
                for (int i = 0; i< MAXCLIENT; i++){
                    if (FD_ISSET(client_socket[i],&writefds)){
                        time_t now;
                        struct tm *tm_info;
                        char time_str[1024];
                        time(&now);
                        tm_info = localtime(&now);
                        strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
                        int n = send(client_socket[i], time_str, strlen(time_str)+1, 0);
                        printf("Send client[%d]: %s\n", i, time_str);
                        if (n == SOCKET_ERROR){
                            int error_code = WSAGetLastError();
                            if (error_code == WSAECONNRESET){
                                // Somebody disconnected
                                printf("Host disconnected unexpectedly\n");
                                closesocket(client_socket[i]);
                                client_socket[i] = 0;
                                client_num--;
                            }
                            else{
                                printf("Send failed\n");
                            }
                        }
                        if (n == 0){
                            printf("Host disconnected\n");
                            closesocket(client_socket[i]);
                            client_socket[i] = 0;
                            client_num--;
                        }
                    }
                }
                printf("Client number: %d\n", client_num);
                Sleep(1000);
            }

            return 0;
        }
        ```
        :::
        :::spoiler Python
        ```Python=
        import select
        import select
        import socket
        import datetime
        import time

        """
            Server information
                IP: 127.0.0.1
                PORT: 1234
        """
        IP, PORT = "127.0.0.1",1234

        # Create a socket 
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind((IP, PORT))
        server.listen(5)

        # List of sockets
        Wsockets = [server]
        Rsockets = []
        while True:
            # Get the list of sockets which are ready to be read
            print("Waiting for clients")

            readables, writeables, error = select.select(Wsockets, Rsockets, [])
            if error:
                print(error)
                break
            for sock in readables:
                if sock is server:
                    newClient, addr = server.accept()
                    if len(Rsockets) >= 5:
                        newClient.send(b"Server is full")
                        newClient.close()
                        break
                    print(f"New client connected: {addr}")
                    Rsockets.append(newClient)

            for sock in writeables:
                now = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
                try:
                    sock.send(f"{now}".encode())
                    print(f"Send to clinet{Rsockets.find(sock)}: {now}")
                except Exception as e:
                    print(e)
                    sock.close()
                    Rsockets.remove(sock)
            print(f"Client number: {len(Rsockets)}")
            time.sleep(1)
        ```
        ::: 
    - Client
        :::spoiler C
        ```C=
        #include <stdio.h>
        #include <winsock.h>
        #define MAXCLIENT 5
        #define BUFFER_SIZE 1024

        int main(){
            // Initialize Winsock
            WSADATA wsadata;
            WSAStartup(0x101, &wsadata);

            /*
                Server Information 
                    IP: 127.0.0.1
                    PORT: 1234
            */
            struct sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr("127.0.0.1");
            server.sin_port = htons(1234);

            // Create a socket
            SOCKET server_socket = socket(AF_INET, SOCK_STREAM, 0);

            connect(server_socket, (LPSOCKADDR)&server, sizeof(server));
            printf("Connected to server\n");
            u_short mode = 1;
            ioctlsocket(server_socket, FIONBIO, &mode);
            while (TRUE){
                char buffer[BUFFER_SIZE];
                int n =recv(server_socket, buffer, BUFFER_SIZE, 0);
                if (n>0){
                    printf("%s\n", buffer);
                    if(strcmp(buffer, "Server is full") == 0){
                        break;
                    }
                }

            }
            closesocket(server_socket);
            WSACleanup();
            return 0;
        }
        ```
        :::
        :::spoiler Python
        ```Python=
        import socket

        """
            Server information
                IP: 127.0.0.1
                PORT: 1234
        """
        IP, PORT = "127.0.0.1", 1234
        # Create a socket
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.connect((IP, PORT))

        while True:
            data = server.recv(1024)
            print(f"{data.decode()}")
            if data.decode() == "Server is full":
                server.close()
                break
        ```
        :::
- 執行結果
    ![image](https://github.com/kevin0920911/NetworkGIF/blob/main/w11/GIF/4-1.gif?raw=true)
### 第五題: Client using select
- 請將client的接收，也採用select()方式。
- 程式碼
    - Server
        :::spoiler C
        ```C=
        #include <stdio.h>
        #include <winsock.h>
        #include <time.h>
        #define MAXCLIENT 5
        #define BUFFER_SIZE 1024
        int main(){
            int client_num = 0;

            // Initialize Winsock
            WSADATA wsadata;
            WSAStartup(0x101, &wsadata);

            /*
                Server Information
                    IP: 127.0.0.1
                    PORT: 1234
            */
            struct sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr("127.0.0.1");
            server.sin_port = htons(1234);

            // Create a socket
            SOCKET server_socket = socket(AF_INET, SOCK_STREAM, 0);
            SOCKET client_socket[MAXCLIENT]={0};

            // Bind the socket
            bind(server_socket, (LPSOCKADDR)&server, sizeof(server));
            listen(server_socket, 5);

            // Check table build
            fd_set writefds,readfds;

            while (TRUE){
                // Clear the socket set
                FD_ZERO(&readfds);
                FD_ZERO(&writefds);

                // Add socket to set
                FD_SET(server_socket, &readfds);

                for (int i = 0; i< MAXCLIENT; i++){
                    if (client_socket[i]>0){
                        FD_SET(client_socket[i],&writefds);
                    }
                }

                // Select the socket
                int activity = select(0, &readfds, &writefds, NULL, NULL);

                // Wake up by select
                if (activity == SOCKET_ERROR){
                    printf("Select Error\n");
                    exit(1);
                }


                // New connection
                if (FD_ISSET(server_socket, &readfds)){
                    struct sockaddr_in client;
                    int client_size = sizeof(client);
                    SOCKET new_socket = accept(server_socket, (LPSOCKADDR)&client, &client_size);
                    printf("New connection: socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(client.sin_addr) , ntohs(client.sin_port));
                    BOOL isFull = FALSE;
                    for (int i = 0; i< MAXCLIENT; i++){
                        if (client_socket[i] == 0){
                            isFull = TRUE;
                            client_socket[i] = new_socket;
                            client_num++;
                            printf("The %d client socket is in cli_sd[%d]\n",client_num,i);
                            break;
                        }
                    }
                    if (!isFull){
                        printf("Server is full\n");
                        send(new_socket, "Server is full", strlen("Server is full")+1, 0);
                        closesocket(new_socket);
                    }
                }
                // if client can be written
                for (int i = 0; i< MAXCLIENT; i++){
                    if (FD_ISSET(client_socket[i],&writefds)){
                        time_t now;
                        struct tm *tm_info;
                        char time_str[1024];
                        time(&now);
                        tm_info = localtime(&now);
                        strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
                        int n = send(client_socket[i], time_str, strlen(time_str)+1, 0);
                        printf("Send client[%d]: %s\n", i, time_str);
                        if (n == SOCKET_ERROR){
                            int error_code = WSAGetLastError();
                            if (error_code == WSAECONNRESET){
                                // Somebody disconnected
                                printf("Host disconnected unexpectedly\n");
                                closesocket(client_socket[i]);
                                client_socket[i] = 0;
                                client_num--;
                            }
                            else{
                                printf("Send failed\n");
                            }
                        }
                        if (n == 0){
                            printf("Host disconnected\n");
                            closesocket(client_socket[i]);
                            client_socket[i] = 0;
                            client_num--;
                        }
                    }
                }
                printf("Client number: %d\n", client_num);
                Sleep(1000);
            }

            return 0;
        }
        ```
        :::
        :::spoiler Python
        ```Python=
        import select
        import socket
        import datetime
        import time

        """
            Server information
                IP: 127.0.0.1
                PORT: 1234
        """
        IP, PORT = "127.0.0.1",1234

        # Create a socket 
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind((IP, PORT))
        server.listen(5)

        # List of sockets
        Wsockets = [server]
        Rsockets = []
        while True:
            # Get the list of sockets which are ready to be read
            print("Waiting for clients")

            readables, writeables, error = select.select(Wsockets, Rsockets, [])
            if error:
                print(error)
                break
            for sock in readables:
                if sock is server:
                    newClient, addr = server.accept()
                    if len(Rsockets) >= 5:
                        newClient.send(b"Server is full")
                        newClient.close()
                        break
                    print(f"New client connected: {addr}")
                    Rsockets.append(newClient)

            for sock in writeables:
                now = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
                try:
                    sock.send(f"{now}".encode())
                    print(f"Send: {now}")
                except Exception as e:
                    print(e)
                    sock.close()
                    Rsockets.remove(sock)
            print(f"Client number: {len(Rsockets)}")
            time.sleep(1)
        ```
        :::
    - Client
        :::spoiler C
        ```C=
        #include <stdio.h>
        #include <winsock.h>
        #define MAXCLIENT 5
        #define BUFFER_SIZE 1024

        int main(){
            // Initialize Winsock
            WSADATA wsadata;
            WSAStartup(0x101, &wsadata);

            /*
                Server Information 
                    IP: 127.0.0.1
                    PORT: 1234
            */
            struct sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr("127.0.0.1");
            server.sin_port = htons(1234);

            // Create a socket
            SOCKET server_socket = socket(AF_INET, SOCK_STREAM, 0);

            connect(server_socket, (LPSOCKADDR)&server, sizeof(server));
            printf("Connected to server\n");
            fd_set readfds;
            while (TRUE){
                FD_ZERO(&readfds);
                FD_SET(server_socket, &readfds);
                select(0, &readfds, NULL, NULL, NULL);

                if (FD_ISSET(server_socket, &readfds)){
                    char buffer[BUFFER_SIZE];
                    int n =recv(server_socket, buffer, BUFFER_SIZE, 0);
                    printf("Server: %s\n", buffer);
                    if(strcmp(buffer, "Server is full") == 0){
                        break;
                    }
                }

            }
            closesocket(server_socket);
            WSACleanup();
            return 0;
        }
        ```
        :::
        :::spoiler Python
        ```Python=
        import socket
        import select

        """
            Server information
                IP: 127.0.0.1
                PORT: 1234
        """
        IP, PORT = "127.0.0.1", 1234
        # Create a socket
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.connect((IP, PORT))
        rsock = [server]

        while True:
            readable,_, err= select.select(rsock, [], [])
            if err:
                print(err)
                break
            for sock in readable:
                try:
                    data = sock.recv(1024)
                    print(f"{data.decode()}")
                    if data.decode() == "Server is full":
                        server.close()
                        break
                except Exception as e:
                    print(e)
                    server.close()
                    break
        ```
        :::
- 執行結果
    ![image](https://github.com/kevin0920911/NetworkGIF/blob/main/w11/GIF/4-2.gif?raw=true)
    
## 心得
這周學了如何使用多工，我當初看標題還以為是數位邏輯的那個多工，原來是可以同時做多個任務，在影片中老師用警報器形容我覺得比喻的很好。

在這周作業中，我發現一個好好用的工具(好其實是寫程式語言學用yacc時發現的)，給大家推薦一下，他叫make，這樣我就不用一直打gcc -o sever server.c -lwsock32，超級方便。

然後最後，建議老師跟助教能在作業有修改時可以寫信提醒，上周有更改要求，我看到已經是禮拜四晚上了orz...

## 附錄
[我全部的程式碼](https://github.com/kevin0920911/NetworkGIF/tree/main/w11)