---
# System prepended metadata

title: 網路管理、Socket
tags: [計算機概論]

---

# Lab10: 網路管理、Socket
## 1. 計算機網路發展史
- 1960s - 美國國防部ARPANET專案問世，奠定了網路的基礎。



- 1980s - 國際標準化組織（ISO）釋出[OSI模型](https://zh.wikipedia.org/zh-tw/OSI%E6%A8%A1%E5%9E%8B)，奠定了網路技術標準化的基礎。
![osimodel](https://hackmd.io/_uploads/HJgWC1LWyl.png)



- 1990s - 英國人[Timothy John Berners-Lee](https://zh.wikipedia.org/zh-tw/%E8%92%82%E5%A7%86%C2%B7%E4%BC%AF%E7%BA%B3%E6%96%AF-%E6%9D%8E)發明了圖形化的瀏覽器，瀏覽器的簡單易用性使得計算機網路迅速被普及。

在沒有瀏覽器的年代，上網是這樣的。
![before-browser](https://hackmd.io/_uploads/ryqGR1I-yg.jpg)



有了瀏覽器以後，上網是這樣的。
![after-browser](https://hackmd.io/_uploads/HkWXAJUWJx.png)


## 2.TCP/IP 通訊協定
![image](https://hackmd.io/_uploads/B14h12uXye.png)

1. **應用層( Application Layer )**
HTTP(HTTPS) / SMTP / FTP …
整合OSI的會議層（Session Layer）、展示層（Presentation Layer)以及應用層（Application Layer）。
2. **傳輸層( Transport Layer )** (如何傳送資料)
TCP / UDP
對應於 OSI 的傳輸層（Transport Layer）。
3. **網路層( Internet Layer )** (加上收發地址)
IP (Internet Protocol)。
4. **鏈結層( Link Layer )** (實際傳送資料)
路由器 / 海底電纜
整合OSI的資料連結層（Data Link Layer）、實體層（Physical Layer）。


## TCP( Transmission Control Protocol )
TCP是傳輸層( Transport Layer )的一種協定，可靠的傳輸是它的特色。
而TCP是利用三次握手（Three-way handshake）來確保雙方的接收功能、發送功能是好的。
進而保證訊息能傳到對方手上。
<div style='text-align:center;'>
<img src='https://miro.medium.com/v2/resize:fit:720/format:webp/0*nj7oJaWNUZzDSKD0.gif'></img>
</div>




## UDP ( User Datagram Protocol )
TCP傳輸可靠性高，確保一定送的到
但每次傳輸都必須經過三次握手對於某些服務效率實在次太低了
所以誕生了UDP，UDP就是拿掉三次握手，直接發送訊息
但代價就是訊息不保證能送達
所以像是現場直播、視訊需要低延遲的應用就會使用UDP。



## IP
IP 旨在將網路封包傳送到預期的目標電腦。 Port則導向電腦上的某個服務。

常見的預設連接埠( port ):
**80:** http
**443:** https(安全版的http)
**22:** ssh


IP 位址有兩種形式：IPv4 和 IPv6。 IPv4 位址的格式為 X.X.X.X，其中每個 X 都是 0-255 範圍內的值。 由於擔心使用可用 IPv4 位址集區耗盡，因此建立了 IPv6 通訊協定。
## 3.Socket
![圖片2](https://hackmd.io/_uploads/SJVuWh_7Jg.png)


- 伺服器端
```=python
from socket import socket, SOCK_STREAM, AF_INET

# 1. 創建Socket物件並指定使用哪種IP格式、傳輸協議
# family=AF_INET - IPv4位址
# family=AF_INET6 - IPv6位址
# type=SOCK_STREAM - TCP套接字
# type=SOCK_DGRAM - UDP套接字
server = socket(family=AF_INET, type=SOCK_STREAM)
# 2. 綁定IP位址和port(port用於區分不同的服務)
server.bind(('127.0.0.1', 5678))
# 3. 開啟監聽 - 監聽客戶端連接到伺服器
# 參數來限制最多能有幾個客戶端排隊連接伺服器
server.listen(5)
print('伺服器啟動，開始監聽...')
while True:
    # 4. 通過迴圈接收客戶端的連接並作出相應的處理(提供服務)
    # accept方法是一個阻塞方法，如果沒有客戶端連接到伺服器，程式碼不會繼續向下執行
    # accept方法返回一個元組，其中的第一個元素是客戶端物件
    # 第二個元素是連接到伺服器的客戶端地址(由IP和port兩部分構成)
    client, addr = server.accept()
    print(str(addr) + ' 連接到了伺服器。')
    # 5. 發送資料
    msg = input('請輸入訊息:\n')
    client.send(msg.encode('utf-8'))
    # 6. 斷開連接
    client.close()

```
- 客戶端
```=python
from socket import socket, SOCK_STREAM, AF_INET

client = socket(family=AF_INET, type=SOCK_STREAM)
# 2. 連接到伺服器(需要指定IP位址和port)
client.connect(('127.0.0.1', 5678))
print('連線成功')
# 3. 從伺服器接收資料
print(client.recv(1024).decode('utf-8'))
client.close()

```

## socket + thread
目前的伺服器與某一客戶端通訊時，其他客戶端只能排隊等待處理。這樣的設計顯然無法滿足我們的需求。那怎樣才能設計一個能夠同時處理多個用戶請求的伺服器呢。接下來，我們將設計一個採用多執行緒技術的伺服器，該伺服器將能夠同時接收多個用戶的連線請求並向客戶端傳送圖片。

## 同步與異步
- 同步:
![圖片3](https://hackmd.io/_uploads/BkAlEhu7yg.png)
- 異步
![圖片4](https://hackmd.io/_uploads/Sy1G43uQJx.png)

## 多執行緒Multithreading
![圖片7](https://hackmd.io/_uploads/S1t3DLtmye.png)
<br><br><br><br>
![圖片9](https://hackmd.io/_uploads/H1GMj8YQJg.png)

### python threading
- 以下是模擬下載兩份pdf的情況
```=python
ffrom time import time, sleep


def download_pdf(filename):
    print(f'開始下載"{filename}"...')
    t = 5
    sleep(t)
    print(f'{filename}下載完成 耗費時間{t}秒')

start = time()
download_pdf('Python從入門到放棄.pdf')
download_pdf('計算機概論.pdf')
end = time()
print(f'總共花費了{end - start}秒')

```
輸出:
```=python
開始下載"Python從入門到入土.pdf"...
Python從入門到入土.pdf下載完成 耗費時間5秒
開始下載"計算機概論.pdf"...
計算機概論.pdf下載完成 耗費時間5秒
總共花費了10.00235104560852秒
```
從上述例子可以看出，如果程式中的程式碼只能按順序逐步執行，那麼即使是執行兩個彼此無關的下載任務，也需要先等一個檔案下載完成後才能開始下一個下載任務。很顯然，這樣的方式既不合理也缺乏效率。

- 以下使用python threading來多執行緒執行下載任務
```=python
from threading import Thread
from time import time, sleep


def download(filename):
    print(f'開始下載"{filename}"...')
    t = 5
    sleep(t)
    print(f'{filename}下載完成 耗費時間{t}秒')


start = time()
# target=目標韓式, args=(目標韓式的參數1, 目標韓式的參數2, ....)
t1 = Thread(target=download, args=('Python從入門到放棄.pdf',))
# 啟動t1執行緒
t1.start()
t2 = Thread(target=download, args=('計算機概論.pdf',))
# 啟動t2執行緒
t2.start()
# 賭塞，等待t1執行緒執行完
t1.join()
# 賭塞，等待t2執行緒執行完
t2.join()
end = time()
print(f'總共花費了{end - start}秒')

```
輸出:
```=python
開始下載"Python從入門到入土.pdf"...
開始下載"計算機概論.pdf"...
計算機概論.pdf下載完成 耗費時間5秒
Python從入門到入土.pdf下載完成 耗費時間5秒
總共花費了5.001640558242798秒
```
### 多執行緒的socket伺服器
- socket + thread: 伺服器端
```=python
from socket import socket, SOCK_STREAM, AF_INET
from threading import Thread


def handle_client(client, addr):
    # 處理與客戶端的互動
    print(f'{addr} 連接到了伺服器。')
    msg = input('請輸入訊息:\n')
    client.send(msg.encode('utf-8'))
    client.close()
    print(f'{addr} 的連接已關閉。')

# 1. 創建Socket物件並指定使用哪種IP格式、傳輸協議
# family=AF_INET - IPv4位址
# family=AF_INET6 - IPv6位址
# type=SOCK_STREAM - TCP套接字
# type=SOCK_DGRAM - UDP套接字
server = socket(family=AF_INET, type=SOCK_STREAM)
# 2. 綁定IP位址和port(port用於區分不同的服務)
server.bind(('127.0.0.1', 5678))
# 3. 開啟監聽 - 監聽客戶端連接到伺服器
# 參數來限制最多能有幾個客戶端排隊連接伺服器
server.listen(5)
print('伺服器啟動，開始監聽...')
    
while True:
    # 4. 通過迴圈接收客戶端的連接並作出相應的處理(提供服務)
    # accept方法是一個阻塞方法，如果沒有客戶端連接到伺服器，程式碼不會繼續向下執行
    # accept方法返回一個元組，其中的第一個元素是客戶端物件
    # 第二個元素是連接到伺服器的客戶端地址(由IP和port兩部分構成)
    client, addr = server.accept()
    # 5. 為每個客戶端新建一個執行緒
    client_thread = Thread(target=handle_client, args=(client, addr))
    client_thread.start()
```

- socket + thread: 客戶端
```=python
from socket import socket, SOCK_STREAM, AF_INET

client = socket(family=AF_INET, type=SOCK_STREAM)
# 2. 連接到伺服器(需要指定IP位址和port)
client.connect(('127.0.0.1', 5678))
print('連線成功')
# 3. 從伺服器接收資料
print(client.recv(1024).decode('utf-8'))
client.close()

```



# Lab題目作業
- 創建一個資料夾，名稱: ==學號_Lab10==，放入作業後，上傳至GitHub
## 作業1 : 簡易通訊軟體1
- 伺服器端要求: 
    - 在樹梅派上執行 
    - 使用多執行緒來服務最少2個客戶端，並且能接收客戶端發送的訊息
    - 檔案名稱: ==Lab10_server.py==
- 客戶端要求:
    - 在筆電或桌電上執行
    - 能夠對伺服器端發送訊息
    - 檔案名稱: ==Lab10_client.py==
- 擷取成功畫面，圖片檔案名稱: ==Lab10.png==
## 加分題 : 簡易通訊軟體2
- 伺服器端要求: 
    - 在樹梅派上執行
    - 使用多執行緒來服務最少2個客戶端，並且能接收客戶端發送的訊息
    - 檔案名稱: ==Lab10_server_plus.py==
- 客戶端要求:
    - 在筆電或桌電上執行
    - 能夠對伺服器端發送訊息，並且一直保持連線
    - 檔案名稱: ==Lab10_client_plus.py==
- 擷取成功畫面，圖片檔案名稱: ==Lab10_plus.png==

