Python 網絡編程操做TCP/UDP 初探(二)

在上一章中咱們遺留了下面幾個問題:linux

1.異步通訊,也是在不斷的輪詢排隊處理中,若是採用服務器端多線程處理呢?算法

2.多線程與異步操做的異同編程

3.若是A - 服務器 -B該如何實現?(包含如下內容:windows

    a.半雙工通訊服務器

    b.全雙工通訊多線程

    c.多用戶全雙工通訊併發

    d.使用多線程實現多用戶全雙工通訊異步

    e.多用戶、多房間、全雙工通訊socket

    )tcp

針對上面的幾個問題:我以爲是頗有必要好好探索一下的。那麼首先從多線程與異步操做的異同:

一.多線程與異步操做的異同

1.進程:

        每一個進程都擁有本身的地址空間、內存、數據棧以及其它用於跟蹤執行的輔助數據。須要採用ipc的方式共享信息。

2.線程:

        輕量級進程,在同一個進程下執行的,共享相同的上下文。能夠將它們認爲是在一個主進程或者「主線程」中並行運行的一些迷你進程,須要操做系統投入CPU資源來運行和調度。線程包含:開始、執行順序和結束三部分。有一個指令指針,用於紀錄當前運行的上下文,當其餘線程運行時,它能夠被搶佔(中斷)和臨時掛起(睡眠),這種作法叫作讓步。最重要的是一個進程中的各個線程與主線程共享同一片數據空間,所以相比於獨立的進程而言,線程間的信息共享和通訊更加容易。這種共享風險就是,若是多個線程訪問同一片數據,因爲訪問的順序不一樣,結果可能不一致。這種狀況一般稱爲競態條件(race condition)。有些線程可能還有阻塞狀態,因此必須處理來避免cpu的時間過多的分配到這些貪婪線程。

3.多線程:

        以前有看到過知乎上的一個比喻,以爲特別好,在這裏引用下:

單進程單線程:一我的在一個桌子上吃飯

單進程多線程:一羣人在一個桌子上吃飯

多進程單線程:多我的在各自的桌子上吃飯

多線程容易引起的問題就是,一羣人在一個桌子上吃飯的時候,若是同時有多我的要夾同一個菜,就容易一我的剛伸筷子,菜已經被別人夾走。因此必須一人夾一筷子。這就是資源衝突。

在windows上,建立進程會消耗會很大,因此鼓勵你們在windows平臺下進行單進程多線程操做。

在linux 上,建立進程的消耗很小,因此鼓勵你們在各自的桌子上吃飯,可是這樣就存在不一樣桌子互相講話很困難。因此須要你們學習處理進程間的通訊。

當在多核cpu上,每一個cpu運行一個進程,有各自的進程資源,因此在cpu核心上切換無需考慮上下文。可是若是每一個核心運行一個線程,每一個線程須要共享資源,因此必須將一個核心上的資源拷貝到另外一個核心才能繼續運行,因此會耗費大量的開銷。所以在多核服務器端編程,要習慣多進程而非多線程。

4.異步操做

        所謂異步,打個比方,就是若是一大羣人都想你聽他說話,那麼你就給他們每人一分鐘的時間說,你們輪流說,沒說完的待會兒輪到時再繼續說。也就是一個時間片的方法。異步處理基於兩個前提。第一個前提是支持併發,固然這是基本前提。這裏的併發並不必定要是並行,也就是說容許邏輯上異步,實現上串行;第二個前提是 支持回調(callback),由於併發的、異步的處理不會阻塞當前正在被執行的流程,因此「任務完成後」要執行的步驟應該寫在回調中,絕大多數回調是經過函數來實現。

異步操做的優缺點:

  由於異步操做無須額外的線程負擔,而且使用回調的方式進行處理,在設計良好的狀況下,處理函數能夠沒必要使用共享變量(即便沒法徹底不用,最起碼能夠減小共享變量的數量),減小了死鎖的可能。固然異步操做也並不是完美無暇。編寫異步操做的複雜程度較高,程序主要使用回調方式進行處理,與普通人的思惟方式有些初入,並且難以調試。

多線程的優缺點:
  多線程的優勢很明顯,線程中的處理程序依然是順序執行,符合普通人的思惟習慣,因此編程簡單。可是多線程的缺點也一樣明顯,線程的使用(濫用)會給系統帶來上下文切換的額外負擔。而且線程間的共享變量可能形成死鎖的出現。

針對他們是適用範圍,若是針對大數據量的算法及圖形處理,建議使用多線程進行操做。

2、實戰,下面咱們來實現下如下幾個功能:

1.半雙工通訊

        半雙工通訊,就是隻有一我的能打字,而另外一個參與者在獲得輸入消息提示以前必須等待消息。而且,一旦發送者發送一了一條消息,在它可以再次發送消息以前,必須等待對方的回覆。再上一章中,其實咱們已經實現了,修改下部分代碼,來更加完善:

服務器端:

# coding:utf-8
from socket import *
from time import ctime

HOST = ''
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)

tcpSerSock = socket(AF_INET, SOCK_STREAM)  # 分配TCP服務器套接字
tcpSerSock.bind(ADDR)  # 將套接字綁定到服務器地址
tcpSerSock.listen(5)  # 開啓TCP監聽器,接受鏈接數6個,超過鏈接數拒絕鏈接!只與第一個鏈接客戶端通訊.

try:
    while True:
        print 'waiting for connection...'   # 無限循環中,等待客戶端的鏈接
        tcpCliSock, addr = tcpSerSock.accept()
        print '...connected from:', addr
        try:
            while True:
                data = tcpCliSock.recv(BUFSIZE)  # 接收客戶端數據
                if not data:  # 若是接收的消息是空白數據,這覺得着客戶端已經退出,跳出循環,關閉當前的客戶端鏈接,繼續等待另外一個客戶端鏈接
                    break
                else:
                    print ' Client To Ser [%s] %s' % (ctime(), data)
                    while True:
                        serRecData = raw_input('> ')
                        if not serRecData:
                            continue
                        else:
                            tcpCliSock.send(serRecData)  # 接受到客戶端消息不爲空,服務端輸入信息返回給客戶端。
                            print 'waiting for Client...'
                            break
        finally:
            tcpCliSock.close()
finally:
    tcpSerSock.close()  # 永遠不會執行,只是提醒你們,能夠用這種方式,關閉服務器套接字,退出服務

客戶端:

# coding:utf-8
from socket import *
from time import ctime

HOST = 'localhost'
PORT = 21567
BUFSIZE = 1024
ADDR = (HOST, PORT)

tcpCliSock = socket(AF_INET, SOCK_STREAM)  # 分配TCP套接字
tcpCliSock.connect(ADDR)  # 經過主機信息鏈接服務器
try:
    while True:
        data = raw_input('> ')   # 輸入信息
        if not data:  # 若是輸入爲空,則跳出循環體,關閉客戶端鏈接
            break
        tcpCliSock.send(data)  # 輸入不爲空,發送信息。
        print 'waiting for Ser...'
        data = tcpCliSock.recv(BUFSIZE)
        if not data:
            break
        print ' Ser To Client [%s] %s' % (ctime(), data)
finally:
    tcpCliSock.close()

 

服務器端顯示:

客戶端顯示:

2.全雙工通訊

3.多用戶全雙工通訊

4.使用多線程實現多用戶全雙工通訊

5.多用戶、多房間、全雙工通訊

相關文章
相關標籤/搜索