Python入門教程:socket編程 socket編程思路: TCP編程 UDP編程: 全套配套視頻:

socket是基於C/S架構的,也就是說進行socket網絡編程,通常需要編寫兩個py文件,一個服務端,一個客戶端。

首先,導入Python中的socket模塊: import socket

Python中的socket通信邏輯如下圖所示(圖片來自網絡):

這張邏輯圖,是整個socket編程中的重點的重點,你必須將它理解、喫透,然後刻在腦海裏,真正成爲自己記憶的一部分!很多人說怎麼都學不會socket編程,歸根到底的原因就是沒有“死記硬背”知識點。

在Python中,import socket後,用socket.socket()方法來創建套接字,語法格式如下:

sk = socket.socket([family[, type[, proto]]])

參數說明:

  • family: 套接字家族,可以使AF_UNIX或者AF_INET。
  • type: 套接字類型,根據是面向連接的還是非連接分爲SOCK_STREAM或SOCK_DGRAM,也就是TCP和UDP的區別。
  • protocol: 一般不填默認爲0。
    直接socket.socket(),則全部使用默認值。

下面是具體的參數定義:

通過s = socket.socket()方法,我們可以獲得一個socket對象s,也就是通常說的獲取了一個“套接字”,該對象具有一下方法:

注意事項:

1.Python3以後,socket傳遞的都是bytes類型的數據,字符串需要先轉換一下,string.encode()即可;另一端接收到的bytes數據想轉換成字符串,只要bytes.decode()一下就可以。

2.在正常通信時,accept()和recv()方法都是阻塞的。所謂的阻塞,指的是程序會暫停在那,一直等到有數據過來。

socket編程思路:

服務端:

1.創建套接字,綁定套接字到本地IP與端口:socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.bind()
2.開始監聽連接:s.listen()
3.進入循環,不斷接受客戶端的連接請求:s.accept()
4.接收傳來的數據,或者發送數據給對方:s.recv() , s.sendall()
5.傳輸完畢後,關閉套接字:s.close()

客戶端:

1.創建套接字,連接服務器地址:socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.connect()
2.連接後發送數據和接收數據:s.sendall(), s.recv()
3.傳輸完畢後,關閉套接字:s.close()

Python的socket編程,通常可分爲TCP和UDP編程兩種,前者是帶連接的可靠傳輸服務,每次通信都要握手,結束傳輸也要揮手,數據會被檢驗,是使用最廣的通用模式;後者是不帶連接的傳輸服務,簡單粗暴,不加控制和檢查的一股腦將數據發送出去的方式,但是傳輸速度快,通常用於安全和可靠等級不高的業務場景,比如文件下載。

TCP編程

服務器端:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

ip_port = ('127.0.0.1', 9999)

sk = socket.socket()            # 創建套接字
sk.bind(ip_port)                # 綁定服務地址
sk.listen(5)                    # 監聽連接請求
print('啓動socket服務,等待客戶端連接...')
conn, address = sk.accept()     # 等待連接,此處自動阻塞
while True:     # 一個死循環,直到客戶端發送‘exit’的信號,才關閉連接
    client_data = conn.recv(1024).decode()      # 接收信息
    if client_data == "exit":       # 判斷是否退出連接
        exit("通信結束")
    print("來自%s的客戶端向你發來信息:%s" % (address, client_data))
    conn.sendall('服務器已經收到你的信息'.encode())    # 回饋信息給客戶端
conn.close()    # 關閉連接

客戶端:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

ip_port = ('127.0.0.1', 9999)

s = socket.socket()     # 創建套接字

s.connect(ip_port)      # 連接服務器

while True:     # 通過一個死循環不斷接收用戶輸入,併發送給服務器
    inp = input("請輸入要發送的信息: ").strip()
    if not inp:     # 防止輸入空信息,導致異常退出
        continue
    s.sendall(inp.encode())

    if inp == "exit":   # 如果輸入的是‘exit’,表示斷開連接
        print("結束通信!")
        break

    server_reply = s.recv(1024).decode()
    print(server_reply)

s.close()       # 關閉連接

上面這個例子,基本能夠展示出socket通信的機制。套接字的創建和關閉,服務器的綁定和監聽,客戶端的連接,這些都是固定套路,沒什麼難點。關鍵之處在於循環內部的收發邏輯,這裏纔是重點,需要根據你自己的業務需求,正確編寫。這個過程中,一定要注意,收發是一一對應的,有發就要有收,並且recv()方法默認是阻塞的。

大家可以在自己的環境中測試上面的例子,並加入更多的內容,不斷地進行嘗試。然而試過之後,你們會發現,雖然服務器和客戶端在一對一的情況下,工作良好,但是,如果有多個客戶端同時連接同一個服務器呢?結果可能不太令人滿意,因爲服務器無法同時對多個客戶端提供服務。爲什麼會這樣呢?因爲Python的socket模塊,默認情況下創建的是單進程單線程,同時只能處理一個連接請求,如果要實現多用戶服務,那麼需要使用多線程機制。

下面我們使用Python內置的threading模塊,配合socket模塊創建多線程服務器。客戶端的代碼不需要修改,可以繼續使用。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket
import threading        # 導入線程模塊


def link_handler(link, client):     
    """
    該函數爲線程需要執行的函數,負責具體的服務器和客戶端之間的通信工作
    :param link: 當前線程處理的連接
    :param client: 客戶端ip和端口信息,一個二元元組
    :return: None
    """
    print("服務器開始接收來自[%s:%s]的請求...." % (client[0], client[1]))
    while True:     # 利用一個死循環,保持和客戶端的通信狀態
        client_data = link.recv(1024).decode()
        if client_data == "exit":
            print("結束與[%s:%s]的通信..." % (client[0], client[1]))
            break
        print("來自[%s:%s]的客戶端向你發來信息:%s" % (client[0], client[1], client_data))
        link.sendall('服務器已經收到你的信息'.encode())
    link.close()


ip_port = ('127.0.0.1', 9999)
sk = socket.socket()            # 創建套接字
sk.bind(ip_port)                # 綁定服務地址
sk.listen(5)                    # 監聽連接請求

print('啓動socket服務,等待客戶端連接...')

while True:     # 一個死循環,不斷的接受客戶端發來的連接請求
    conn, address = sk.accept()  # 等待連接,此處自動阻塞
    # 每當有新的連接過來,自動創建一個新的線程,
    # 並將連接對象和訪問者的ip信息作爲參數傳遞給線程的執行函數
    t = threading.Thread(target=link_handler, args=(conn, address))
    t.start()

啓動這個多線程服務器,然後多運行幾個客戶端,可以很明顯地看到,服務器能夠同時與多個客戶端通信,基本達到我們的目的。

UDP編程:

相對TCP編程,UDP編程就簡單多了,當然可靠性和安全性也差很多。由於UDP沒有握手和揮手的過程,因此accept()和connect()方法都不需要。下面是一個簡單的例子:

# 服務端
import socket
ip_port = ('127.0.0.1', 9999)
sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
sk.bind(ip_port)

while True:
    data = sk.recv(1024).strip().decode()
    print(data)
    if data == "exit":
        print("客戶端主動斷開連接!")
        break

sk.close()

# 客戶端
import socket
ip_port = ('127.0.0.1', 9999)

sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
while True:
    inp = input('發送的消息:').strip()
    sk.sendto(inp.encode(), ip_port)
    if inp == 'exit':
        break

sk.close()

全套配套視頻:

Python全套精講視頻教程,自學一樣月薪破2W,從入門到項目實戰

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章