---
# System prepended metadata

title: net w12
tags: [network]

---

# 網路程式設計第十二周

## 觀念題
### 第一題: Threading優點
- 請說明多緒程式優點
    - 將多核心CPU發揮得淋漓盡致
    - 不同執行序能資料共享
        - 有點像孫悟空的分身 + 情報共享
    - 防止部分執行序卡住
### 第二題: Threading問題
- 資料被修改問題
    - 以老師的程式碼來說
        - 當這裡沒註解時
            ![image](https://hackmd.io/_uploads/HyUzrybXR.png)
            - 執行解果長這樣
                ![image](https://hackmd.io/_uploads/rJqDrkWQ0.png)
            - 是不正常的
        - 但註解時呢
            ![image](https://hackmd.io/_uploads/Bk0nSJb70.png)
            - 執行結果長這樣
                ![image](https://hackmd.io/_uploads/Sy_JIk-QC.png)
            - 是這樣的
    - 原因是這樣的
        - 這裡的程式長這樣
            ![image](https://hackmd.io/_uploads/H1dHIybQR.png)
        - 也因此這邊使用的參數是共用同一個referance
        - 因此若是使用Sleep時，在ThreadMain1數值將會修改
            - ~~講白點就是別名的壞處拉哈哈~~
- 但我粗淺的覺得，這樣是一個變數汙染的行為
    - 如果要達到變數共用，但避免變數汙染我認為可以用以下方式解決
        - 變數汙染問題: 宣告不同的變數，傳入參數
        - 變數共用問題: 使用static，可以做到
        - 程式碼如下
            :::spoiler C
            ```C=
            #include <stdio.h>  
            #include <winsock.h>  

            void *ThreadMain1(void *arg);             /* Main program of a thread */
            void *ThreadMain2(void *arg);             /* Main program of a thread */

            /* Structure of arguments to pass to client thread */
            struct ThreadArgs
            {
                static int a;
                static int b;
                int c;
            };

            int ThreadArgs::a = 5;
            int ThreadArgs::b = 3;

            int main()
            {
                    struct ThreadArgs *threadArgs1, *threadArgs2;   /* Pointer to argument structure for thread */
                    int  threadID1,threadID2;                 /* Thread ID from CreateThread() */    

                    threadArgs1 = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs));
                    threadArgs2 = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs));		
                    printf("Hello World from main()\n");

                    if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadMain1, threadArgs1, 0, (LPDWORD) &threadID1) == NULL)
                        printf("CreateThread() failed");

                    printf("Thread %d created.\n", threadID1);

                    Sleep(1000);

                    if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadMain2, threadArgs2, 0, (LPDWORD) &threadID2) == NULL)
                        printf("CreateThread() failed");

                    printf("Thread %d created.\n", threadID2);		
                    Sleep(2000);

                    free(threadArgs1);              /* Deallocate memory for argument */	
                    free(threadArgs2);              /* Deallocate memory for argument */
                    return 0;
            }

            void *ThreadMain1(void *threadArgs)
            {
                    int i,j,k;

                    i = ((struct ThreadArgs *) threadArgs) -> a;
                    j = ((struct ThreadArgs *) threadArgs) -> b;
                    k = i + j;   
                    ((struct ThreadArgs *) threadArgs) -> c = k; 
                    Sleep(2000);
                    printf("%d + %d = %d \n",i,j,((struct ThreadArgs *) threadArgs) -> c);	
            }

            void *ThreadMain2(void *threadArgs)
            {
                    int i,j,k;

                    i = ((struct ThreadArgs *) threadArgs) -> a;
                    j = ((struct ThreadArgs *) threadArgs) -> b;
                    k = i - j;
                    ((struct ThreadArgs *) threadArgs) -> c = k; 
                    printf("%d - %d = %d \n",i,j,k);
            }
            ```
            :::
## 實作題: 傳送文字檔案的client/server程式，加入多緒功能
- client與server之間，會建立兩條TCP連線，建立兩個thread，分別傳送文字檔的奇數行與偶數行
- 接收端會將收到的內容整合為一個檔案。比對傳送前後的文字檔要相同。
:::info
- 測試文檔
    [Alice.txt](https://gist.githubusercontent.com/phillipj/4944029/raw/75ba2243dd5ec2875f629bf5d79f6c1e4b5a8b46/alice_in_wonderland.txt)
- 文檔比對script
    :::spoiler Python
    ```Python=
    sendFile   = open("in.txt", "r")
    ReceveFile = open("out.txt", "r")

    if sendFile.readlines() == ReceveFile.readlines():
        print("Files are same")
    else:
        print("Files are different")
    ```
    :::
:::
- 程式碼
    - Server
        :::spoiler C++
        ```C++=
        #include <iostream>
        #include <fstream>
        #include <winsock.h>

        using namespace std;
        void *ThreadMain(void *arg);
        struct ThreadArgs{
            SOCKET oddSocket;
            SOCKET evenSocket;
            bool isEven;
        };
        void* ThreadMain(void*);

        int main(){
            /*
            *   Initialize Winsock
            */
            WSADATA wsaData;
            WSAStartup(0x101,(LPWSADATA) &wsaData);

            /*
                Server Information
                    * IP: 127.0.0.1
                    * Port: 1234
            */
            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 serverSocket = socket(AF_INET, SOCK_STREAM, 0);
            bind(serverSocket, (sockaddr*)&server, sizeof(server));
            listen(serverSocket, 5);

            while (TRUE){
                SOCKET oddSocket = accept(serverSocket, NULL, NULL);
                SOCKET evenSocket = accept(serverSocket, NULL, NULL);

                ThreadArgs* args = (ThreadArgs*) malloc(sizeof(ThreadArgs));
                args->oddSocket  = oddSocket;
                args->evenSocket = evenSocket;

                HANDLE Thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadMain, args, 0, NULL);
                Sleep(1);        
            }
            return 0;
        }

        void* ThreadMain(void* arg){
            ThreadArgs *args = (ThreadArgs*) arg;
            SOCKET oddSocket  = args->oddSocket;
            SOCKET evenSocket = args->evenSocket;
            ofstream out("out.txt");

            bool oddEnd  = false;
            bool evenEnd = false;
            while (true){
                if (oddEnd && evenEnd){
                    break;
                }
                char buffer[1024];
                if (recv(oddSocket, buffer, sizeof(buffer), 0) <= 0){
                    oddEnd = true;
                } 
                else{
                    out << buffer;
                }

                if (recv(evenSocket, buffer, sizeof(buffer), 0) <= 0){
                    evenEnd = true;
                }
                else{
                    out << buffer;
                }


                if (oddEnd && evenEnd){
                    break;
                }
            }
            closesocket(oddSocket);
            closesocket(evenSocket);
            return 0;
        }
        ```
        :::
        :::spoiler Python
        ```python=
        import socket
        import threading

        def receive_message(odd: socket.socket , even:socket.socket) -> None:
            file = open("out.txt","w")
            oddEnd, evenEnd = False, False
            while True:
                print(oddEnd, evenEnd)
                if oddEnd and evenEnd:
                    break
                try:
                    data = odd.recv(1024)
                    if not data: raise ConnectionResetError
                    file.write(data.decode())
                except ConnectionResetError:
                    oddEnd = True

                try:
                    data = even.recv(1024)
                    if not data: raise ConnectionResetError
                    file.write(data.decode())
                except ConnectionResetError:
                    evenEnd = True
            odd.close()
            even.close()
            file.close()



        server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        server.bind(("127.0.0.1",1234))
        server.listen(5)
        while True:
            odd ,addr = server.accept()
            even,addr = server.accept()

            recive = threading.Thread(target= receive_message, args=(odd,even))
            recive.start()
        ```
        :::
    - Client
        :::spoiler C++
        ```C++=
        #include <iostream>
        #include <fstream>
        #include <winsock.h>
        #include <string>

        using namespace std;

        void *ThreadMain(void *arg);

        struct ThreadArgs{
            SOCKET serverSocket;
            bool isEven;
        };

        int main(){
            /*
            *   Initialize Winsock
            */
            WSADATA wsaData;
            WSAStartup(0x101,(LPWSADATA) &wsaData);

            /*
                Server Information
                    * IP: 127.0.0.1
                    * Port: 1234
            */
            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 oddSocket  = socket(AF_INET, SOCK_STREAM, 0);
            SOCKET evenSocket = socket(AF_INET, SOCK_STREAM, 0);

            /*
                Connect to the server
            */
            connect(oddSocket, (sockaddr*)&server,  sizeof(server));
            connect(evenSocket, (sockaddr*)&server, sizeof(server));

            /*
                Create a thread for each socket
            */
            ThreadArgs* oddArgss = (ThreadArgs*) malloc(sizeof(ThreadArgs));
            oddArgss->serverSocket = oddSocket;
            oddArgss->isEven = false;

            ThreadArgs* evenArgs = (ThreadArgs*) malloc(sizeof(ThreadArgs));
            evenArgs->serverSocket = evenSocket;
            evenArgs->isEven = true;

            HANDLE oddThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadMain, oddArgss, 0, NULL);
            Sleep(1);
            HANDLE evenThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadMain, evenArgs, 0, NULL);
            Sleep(1);

            Sleep(1000 * 10000);
            return 0;
        }

        void *ThreadMain(void *arg){
            ThreadArgs *args = (ThreadArgs*) arg;
            SOCKET serverSocket = args->serverSocket;
            bool isEven = args->isEven;
            ifstream inFile;

            inFile.open("in.txt", ios::in);
            if (inFile.fail()) exit(1);

            string buffer;
            int line = 0;

            while (!inFile.eof()){
                getline(inFile, buffer);
                if (inFile.peek() != EOF){
                    buffer += '\n';
                }
                line ++ ;
                if (line % 2 == 0 && isEven){
                    send(serverSocket, buffer.c_str(), buffer.size()+1, 0);
                    cout << "Sent2: " << buffer << endl;
                } 
                else if (line % 2 != 0 && !isEven){
                    send(serverSocket, buffer.c_str(), buffer.size()+1, 0);
                    cout << "Sent1: " << buffer << endl;
                }
                Sleep(100);
            }
            inFile.close();
            closesocket(serverSocket);
            free(arg);
            return 0; 
        }
        ```
        :::
        :::spoiler Python
        ```python=
        import socket
        import threading
        import time

        def send_message(socket: socket.socket, isEven: bool) -> None:
            file = open("in","r")
            read = file.readlines()
            for i, line in enumerate(read, start= 1):
                if isEven:
                    if i%2 == 0:
                        socket.send(line.encode())
                        print(f"sent0: {line}")
                else:
                    if i%2 != 0:
                        socket.send(line.encode())
                        print(f"sent1: {line}")
                time.sleep(0.1)
            socket.close()
            file.close()
            return None
        """
            Server Information:
                IP: 127.0.0.1
                PORT: 1234
        """
        IP,PORT = "127.0.0.1",1234

        """
            Create socket 
                odd: send file in odd
                even: send file in even
        """
        odd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        odd.connect((IP,PORT))
        even = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        even.connect((IP,PORT))

        first  = threading.Thread(target=send_message,args=(odd,False))
        first.start()
        time.sleep(0.001)
        second = threading.Thread(target=send_message,args=(even,True))
        second.start()

        first.join()
        second.join()
        ```
        :::
- 執行畫面
    - 教你七分鐘看完愛麗絲夢遊仙境
        <iframe width="560" height="315" src="https://www.youtube.com/embed/y5dqbJA_ybM?si=h44eoWEE3H3g3Ssa" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
    - 比對結果
        ![image](https://hackmd.io/_uploads/S1pUHlbXC.png)
## 心得
所以我是頭香嗎(X。

這周教了多序，他感覺是可以同時做到多程序並行的程式，感覺十分實用，而且程式碼也不會像之前寫得那麼噁心。

然後有關Thread可能會產生資料被修改問題，我個人粗淺的覺得如果向heap allocate多個變數不是就可以避免了嗎? 若是要做到資料共享，我只要將想要共享的參數使用static感覺上可以避免修改的行為@@

最後想問一下教授，Sleep(1)的用意似乎還是有點在賭電腦的效能，我在其他同學的電腦上發現他Sleep(1)似乎不會成功，想問一下教授，這是有方法解決的嗎?