python網(wǎng)絡(luò)編程入門(mén)

1. 背景介紹

1.1 TCP與IP協(xié)議

python的socket模塊是網(wǎng)絡(luò)編程的基礎(chǔ)組件主要用于主機(jī)或者進(jìn)程之間的通信。

計(jì)算機(jī)網(wǎng)絡(luò)的TCP/IP五層模型中,傳輸層的TCP協(xié)議和UDP協(xié)議實(shí)現(xiàn)了主機(jī)間的通信。其中TCP協(xié)議需要先建立連接,然后進(jìn)行數(shù)據(jù)傳輸,如果出現(xiàn)丟包情況會(huì)進(jìn)行數(shù)據(jù)重傳,確保數(shù)據(jù)送達(dá)目的地。而UDP協(xié)議是無(wú)連接的,不需要先建立連接,只需要知道主機(jī)地址就可以直接將數(shù)據(jù)傳過(guò)去,并不保證數(shù)據(jù)一定送到目的地,但是因此傳輸速度比較快。

因此在常規(guī)的模型中,如果進(jìn)行大量數(shù)據(jù)的即時(shí)傳輸(比如視頻電話等)通常是先使用TCP建立連接,然后使用UDP進(jìn)行數(shù)據(jù)傳輸。(事實(shí)上隨著技術(shù)進(jìn)步,有些視頻類(lèi)通信是根據(jù)網(wǎng)絡(luò)狀況選擇通信協(xié)議,在網(wǎng)絡(luò)狀況良好時(shí)會(huì)使用完全TCP的通信。)

1.2 客戶(hù)端與服務(wù)器端

在網(wǎng)絡(luò)編程時(shí),主機(jī)間通信時(shí)通常是C/S架構(gòu),即一方做客戶(hù)端,一方做服務(wù)器端。一般來(lái)說(shuō)服務(wù)器端需要能夠同時(shí)處理多個(gè)客戶(hù)端的請(qǐng)求,因此實(shí)現(xiàn)的時(shí)候需要涉及到多線程的知識(shí)。

在TCP連接中,認(rèn)為主動(dòng)發(fā)起通信請(qǐng)求的一方是客戶(hù)端,被動(dòng)響應(yīng)請(qǐng)求的一方是服務(wù)器端。

1.3 環(huán)境

  1. 操作系統(tǒng):CentOS
  2. 編程語(yǔ)言:python 2.7.5
  3. python模塊:標(biāo)準(zhǔn)庫(kù)中的socket,time,threading。

2. TCP連接

2.1 客戶(hù)端

首先在頭部需要導(dǎo)入socket庫(kù)。

然后創(chuàng)建TCP連接的套接字(socket),并且指定服務(wù)器的主機(jī)地址和端口號(hào),發(fā)起連接請(qǐng)求。這里指定的是新浪的服務(wù)器,端口號(hào)為80。

import socket #導(dǎo)入socket庫(kù)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #創(chuàng)建套接字
s.connect(("www.sina.com.cn", 80)) #向新浪服務(wù)器的80端口發(fā)起連接請(qǐng)求

其中創(chuàng)建套接字的時(shí)候AF_INET指定使用IPv4,如果想要使用IPv6可以改為AF_INET6;SOCK_STREAM指定面向流傳輸?shù)腡CP協(xié)議。

需要注意的是,發(fā)起連接請(qǐng)求的時(shí)候傳入的(hostname, port)的一個(gè)元組,所以需要兩重括號(hào)。這里的hostname可以是像上面的網(wǎng)址,DNS協(xié)議會(huì)自動(dòng)把網(wǎng)址解析成對(duì)應(yīng)的主機(jī)IP;也可以是IP地址,比如本地IP"127.0.0.1"。如果是自己平時(shí)做實(shí)驗(yàn)的話,port端口號(hào)需要大于1024,否則可能會(huì)和其他服務(wù)的端口號(hào)沖突。

服務(wù)器如果響應(yīng)連接請(qǐng)求,就會(huì)和客戶(hù)端建立連接。之后就可以向服務(wù)器發(fā)起請(qǐng)求進(jìn)行通信??梢允褂胹end函數(shù)發(fā)送請(qǐng)求。比如在廖雪峰 Python網(wǎng)絡(luò)編程的教程中發(fā)送的請(qǐng)求為:

s.send('GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')

這個(gè)請(qǐng)求是按照http協(xié)議的格式發(fā)出,得到的響應(yīng)是帶有http首部的新浪首頁(yè)html內(nèi)容。之后就可以接受這些內(nèi)容并查看。

buffer = []
while True:
    d = s.recv(1024)#recv()函數(shù)中的參數(shù)表示一次最多接受的字節(jié)數(shù),這里表示一次最多接受1kb
    if d:
        buffer.append(d)
    else:
        break
data = ''.join(buffer)
header, html = data.split('\r\n\r\n',1)#分離http首部和html內(nèi)容
print 'header:\n', header#打印首部

最后結(jié)束通信,調(diào)用close函數(shù)關(guān)閉連接。

s.close()

2.2 服務(wù)器端

服務(wù)器端與客戶(hù)端類(lèi)似,創(chuàng)建一個(gè)基于IPv4和TCP協(xié)議的套接字(socket)之后,需要先綁定服務(wù)器的IP地址和端口號(hào)。這是因?yàn)橐慌_(tái)機(jī)器可能有多塊網(wǎng)卡,具有不同的IP地址。然后使用listen()函數(shù)進(jìn)行監(jiān)聽(tīng)該端口是否有客戶(hù)端發(fā)送請(qǐng)求過(guò)來(lái)。如果接收到請(qǐng)求,則創(chuàng)建一個(gè)新的線程處理這個(gè)連接請(qǐng)求。

def tcp_server(host,port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((host, port))#綁定IP地址和端口號(hào)
    s.listen(5)#監(jiān)聽(tīng)端口,指定最大連接數(shù)
    print 'Waiting for connections......'
    while True:
        sock, addr = s.accept()#接受一個(gè)新連接
        t = threading.Thread(target = tcplink, args = (sock, addr))#創(chuàng)建新線程處理TCP連
接
        t.start()

其中target = tcplink指定的是新線程中調(diào)用的函數(shù),args = (sock, addr)指定的是tcplink()函數(shù)的參數(shù)。

def tcplink(sock, addr):
    print 'Accept new connection from %s :%s......' % addr
    sock.send('Welcome!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if data == 'exit' or not data:
            break
        sock.send('Hello, %s~'%data)
    sock.close()
    print 'Connection from %s :%s closed~' % addr

與這個(gè)服務(wù)器端程序相對(duì)應(yīng)的客戶(hù)端程序見(jiàn)完整代碼。

3. UDP連接

3.1 客戶(hù)端

創(chuàng)建套接字的時(shí)候,SOCK_DGRAM指定是UDP連接。

客戶(hù)端不再需要connect()發(fā)起連接,而是通過(guò)sendto()直接指定服務(wù)器的(hostname, port)元組。但是依然可以用recv()接收服務(wù)器端發(fā)送的數(shù)據(jù)。

def udp_connect(host, port, msg):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    for data in msg:
        s.sendto(data,(host,port))
        print s.recv(1024)
    s.close()

3.2 服務(wù)器端

服務(wù)器端也不再需要使用listen()進(jìn)行監(jiān)聽(tīng),只需要通過(guò)recvfrom()獲取客戶(hù)端發(fā)送的數(shù)據(jù)和(hostname, port)元組。

def udp_server(host,port):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind((host,port))
    print 'Bind UDP on %s:%s......'%(host, port)
    while True:
        data, addr = s.recvfrom(1024)
        print 'Received from %s :%s......' % addr
        s.sendto('Hello,%s~'%data, addr)

4. 遇到的問(wèn)題

在編程實(shí)現(xiàn)的過(guò)程中遇到不少問(wèn)題,排除拼寫(xiě)錯(cuò)誤之外,值得一記的一個(gè)錯(cuò)誤是端口被占用。python報(bào)錯(cuò)為:
Couldn't listen on any:9999: [Errno 98] Address already in use.
這是因?yàn)榉?wù)器端程序終止運(yùn)行之后該進(jìn)程仍在占用那個(gè)端口進(jìn)行監(jiān)聽(tīng)。這時(shí)候可以先查找占用端口的進(jìn)程PID,使用kill命令強(qiáng)行終止進(jìn)程。

$ lsof -i TCP:9999 | grep LISTEN 
$ lsof -i UDP:9999
$ kill -s 9 <PID>

5. 其他

參考資料

  1. 廖雪峰 Python網(wǎng)絡(luò)編程
  2. 菜鳥(niǎo)教程 Python網(wǎng)絡(luò)編程
  3. 計(jì)算機(jī)網(wǎng)絡(luò)基礎(chǔ)知識(shí)總結(jié)

完整代碼
Github/zc12345

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,238評(píng)論 3 428
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,823評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,604評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,339評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,713評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評(píng)論 3 445
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,893評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,448評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,201評(píng)論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,397評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,631評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,033評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,321評(píng)論 1 293
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,128評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,347評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容