# 網路程式設計第十一周 ## 觀念題 ### 第一題: 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)