# 網路程式設計第十一周
## 觀念題
### 第一題: 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
```
:::
- 執行結果

### 第五題: 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
```
:::
- 執行結果

## 心得
這周學了如何使用多工,我當初看標題還以為是數位邏輯的那個多工,原來是可以同時做多個任務,在影片中老師用警報器形容我覺得比喻的很好。
在這周作業中,我發現一個好好用的工具(好其實是寫程式語言學用yacc時發現的),給大家推薦一下,他叫make,這樣我就不用一直打gcc -o sever server.c -lwsock32,超級方便。
然後最後,建議老師跟助教能在作業有修改時可以寫信提醒,上周有更改要求,我看到已經是禮拜四晚上了orz...
## 附錄
[我全部的程式碼](https://github.com/kevin0920911/NetworkGIF/tree/main/w11)