socket模組的基本使用方法
多多關注:
本人的個人部落格網站[https://www.immisso.com](https://www.immisso.com)
本人的漫畫網站[https://www.ibudm.com](https://www.immisso.com)
socket(中文名稱:套接字)是應用層與傳輸層(TCP/UDP協議)的介面。是對TCP/IP的封裝。是作業系統的通訊機制。應用程式通過socket進行網路資料的傳輸。Python中的socket是我們常用的模組,當然還有socketserver模組(對socket模組的進一步封裝)
socket 通訊方式,常用的主要是兩種 + TCP + UDP
下面以一個例子來介紹Socket程式設計。服務端檔案`base_socket_server.py`,客戶端檔案`base_socket_clent.py`。該例子主要介紹了socket的單連線最簡單的用法,要深入使用。看後續文章
在使用socket模組進行編碼之前我們先介紹一個socket的引數
+ family(地址簇): 1. socket.AF_INET IPv4(預設) 2. socket.AF_INET6 IPv6 3. socket.AF_UNIX用於單一的Unix系統程序間通訊
+ type(型別): 1. socket.SOCK_STREAM 流式socket TCP協議(預設) 2. socket.SOCK_DGRAM 資料報式socket UDP協議 3. socket.SOCK_RAW 原始套接字 4. socket.SOCK_RDM 可靠UDP 5. socket.SOCK_SEQPACKET 可靠的連線資料包服務
### 1.socket最基本用法
服務端`base_socket_server.py`
```python # -*- coding: utf-8 -*- # Created by misso at 2017/8/10
# 匯入socket模組 import socket
# 建立例項 # 預設AF_INET,SOCK_STREAM可以不填寫 sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 定義繫結的ip和port ip_port = ("127.0.0.1",8000)
#繫結監聽 sk.bind(ip_port)
# 監聽 sk.listen()
print("等待接受資料..........") # 接受資料 sock,addr = sk.accept()
# 獲取從客戶端發過來的資料 # 一次獲取1k的資料 # python3.x以上的版本。網路資料的傳送接受都是byte型別。 # 如果傳送的資料是str型別則需要進行編解碼 data = sock.recv(1024) str_data = data.decode("utf8") print(str_data)
# 給客戶端返回資料 msg = "服務端返回的資料:"+str_data sock.send(msg.encode())
# 主動關閉連線 sock.close() ``` 這段程式碼的意思是開啟一個socket服務,客戶端傳送過來訊息後。經過服務端的處理後。再返回給客戶端,然後斷開連線。接下來看客戶端的程式碼。
客戶端`base_socket_client.py`
```python
# -*- coding: utf-8 -*- # Created by misso at 2017/8/10
import socket
# 建立例項 # 預設AF_INET,SOCK_STREAM可以不填寫 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 定義繫結的ip和port ip_port = ("127.0.0.1",8000)
#繫結監聽 client.connect(ip_port)
# 給伺服器傳送資料
str = input("輸入資料:")
client.send(str.encode("utf8"))
data = client.recv(1024) print(data.decode()) client.close()
``` 客戶端的程式碼的意思是,開啟連線,連線到指定埠,使用者輸入資料傳送到服務端,然後接受服務端返回的資料。最後再關閉這個連線
執行結果如下: ![](https://imgs.immisso.com/image/img3.png) ![](https://imgs.immisso.com/image/img4.png)
### 2.客服端連續訊息傳送
上面兩個檔案最後都關閉了連線,我們怎麼保持訊息的連續傳送呢?僅僅是不做關閉就可以了嗎?即使我們註釋掉`base_socket_server.py`檔案裡的st.close()。就會發現依舊是不可以的。我們怎麼實現一次連線,就可以持續傳送呢,我們可以在一次連線成功後做一個while true的迴圈,這樣我們就可以持續傳送訊息了。下面是對程式碼的進一步改寫。
服務端`base_socket_server.py`改寫後的程式碼
```python
# -*- coding: utf-8 -*- # Created by misso at 2017/8/12
# 匯入socket模組 import socket
# 建立例項 # 預設AF_INET,SOCK_STREAM可以不填寫 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 定義繫結的ip和port ip_port = ("127.0.0.1", 8000)
# 繫結監聽 sk.bind(ip_port)
# 監聽 sk.listen()
while True: print("等待接受資料..........") # 接受資料 sock, addr = sk.accept() message = "連線成功" sock.send(message.encode("utf8"))
while True: # 獲取從客戶端發過來的資料 # 一次獲取1k的資料 # python3.x以上的版本。網路資料的傳送接受都是byte型別。 # 如果傳送的資料是str型別則需要進行編解碼 data = sock.recv(1024) str_data = data.decode("utf8") print(str_data)
if str_data == "exit": break
# 給客戶端返回資料 msg = "服務端返回的資料:" + str_data sock.send(msg.encode("utf8"))
# 主動關閉連線 sock.close() ``` 客戶端`base_socket_client.py`改寫後的程式碼
```python
# -*- coding: utf-8 -*- # Created by misso at 2017/8/12
import socket
# 建立例項 # 預設AF_INET,SOCK_STREAM可以不填寫 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 定義繫結的ip和port ip_port = ("127.0.0.1", 8000)
# 繫結監聽 client.connect(ip_port)
while True: # 接受訊息 data = client.recv(1024) print(data.decode("utf8")) # 給伺服器傳送資料 input_str = input("輸入資料:")
client.send(input_str.encode("utf8"))
if input_str == "exit": break ```
執行結果如下: ![](https://imgs.immisso.com/image/img5.png) ![](https://imgs.immisso.com/image/img6.png)
這樣便實現了一個使用者連續傳送資訊連線不斷開的要求,即使這樣當一個使用者連線的時候,另一個使用者是不能連線的。我們怎樣才能進行多連線呢?這裡我們就會用到多執行緒了,每一個使用者連線開啟一個執行緒。就能保證多使用者同時連線了。
### 3. 多使用者連線
上面也提到了,在實際應用中,我們需要多個使用者連線的,我們可以通過開啟執行緒的方式進行多使用者連線
服務端`middle_socket_server.py`
```python
# -*- coding: utf-8 -*- # Created by misso at 2017/8/12
# 匯入socket、threading模組 import socket import threading
# 建立例項 # 預設AF_INET,SOCK_STREAM可以不填寫 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 定義繫結的ip和port ip_port = ("127.0.0.1", 8000)
# 繫結監聽 sk.bind(ip_port)
# 監聽 sk.listen()
# 定義執行緒執行函式
def handle_sock(sock,addr): message = "連線成功" sock.send(message.encode("utf8")) while True: # 獲取從客戶端發過來的資料 # 一次獲取1k的資料 # python3.x以上的版本。網路資料的傳送接受都是byte型別。 # 如果傳送的資料是str型別則需要進行編解碼 data = sock.recv(1024) str_data = data.decode("utf8") print(str_data)
if str_data == "exit": break
# 給客戶端返回資料 msg = "服務端返回的資料:" + str_data sock.send(msg.encode("utf8")) # 主動關閉連線 sock.close() while True: print("等待接受資料..........") # 接受資料 sock, addr = sk.accept() client_thread = threading.Thread(target=handle_sock,args=(sock,addr)) client_thread.start()
```
客戶端`middle_socket_client.py`
```python
# -*- coding: utf-8 -*- # Created by misso at 2017/8/12
import socket
# 建立例項 # 預設AF_INET,SOCK_STREAM可以不填寫 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 定義繫結的ip和port ip_port = ("127.0.0.1", 8000)
# 繫結監聽 client.connect(ip_port)
while True: # 接受訊息 data = client.recv(1024) print(data.decode("utf8")) # 給伺服器傳送資料 input_str = input("輸入資料:")
client.send(input_str.encode("utf8"))
if input_str == "exit": break
```
執行結果如下: ![](https://imgs.immisso.com/image/img7.png) ![](https://imgs.immisso.com/image/img8.png) ![](https://imgs.immisso.com/image/img9.png)
提到多連線我們不得不提另一個模組socketserver。
#### 4.socketserver模組的使用
socketserver模組是對socket的封裝。它也可以進行使用者的多連線(其內部實現原始碼也使用了threading模組)。使用起來更加方便。 服務端`socketserver_socket_server.py`
```python
# -*- coding: utf-8 -*- # Created by misso at 2017/8/12
# 匯入模組 from socketserver import BaseRequestHandler,ThreadingTCPServer
# 定義類 class MyServer(BaseRequestHandler): # 重寫handle方法 def handle(self): # 定義連線物件 conn = self.request
message = "連線成功" conn.send(message.encode())
while True: # 接受客戶端訊息 data = conn.recv(1024) # 列印接受的訊息 print(data.decode("utf8"))
#如果接受到exit的訊息,則進行迴圈的退出
if data == b'exit': break
conn.send(data) conn.close()
if __name__ == "__main__":
# 建立多執行緒例項
server = ThreadingTCPServer(("127.0.0.1",8000),MyServer)
# 開啟socketserver非同步多執行緒
server.serve_forever()
```
客戶端`base_socket_client.py`
```python
# -*- coding: utf-8 -*- # Created by misso at 2017/8/12
import socket
# 建立例項 # 預設AF_INET,SOCK_STREAM可以不填寫 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 定義繫結的ip和port ip_port = ("127.0.0.1", 8000)
# 繫結監聽 client.connect(ip_port)
while True: # 接受訊息 data = client.recv(1024) print(data.decode("utf8")) # 給伺服器傳送資料 input_str = input("輸入資料:")
client.send(input_str.encode("utf8"))
if input_str == "exit": break
```
執行結果如下:
![](https://imgs.immisso.com/image/img10.png) ![](https://imgs.immisso.com/image/img11.png) ![](https://imgs.immisso.com/image/img12.png)