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

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é)議是無連接的,不需要先建立連接挣饥,只需要知道主機(jī)地址就可以直接將數(shù)據(jù)傳過去除师,并不保證數(shù)據(jù)一定送到目的地,但是因此傳輸速度比較快扔枫。

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

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

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

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

1.3 環(huán)境

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

2. TCP連接

2.1 客戶端

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

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

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

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

需要注意的是花履,發(fā)起連接請求的時(shí)候傳入的(hostname, port)的一個(gè)元組,所以需要兩重括號(hào)挚赊。這里的hostname可以是像上面的網(wǎng)址诡壁,DNS協(xié)議會(huì)自動(dòng)把網(wǎng)址解析成對應(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)連接請求,就會(huì)和客戶端建立連接嚎朽。之后就可以向服務(wù)器發(fā)起請求進(jìn)行通信铺纽。可以使用send函數(shù)發(fā)送請求哟忍。比如在廖雪峰 Python網(wǎng)絡(luò)編程的教程中發(fā)送的請求為:

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

這個(gè)請求是按照http協(xié)議的格式發(fā)出狡门,得到的響應(yīng)是帶有http首部的新浪首頁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ù)器端與客戶端類似叛复,創(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)聽該端口是否有客戶端發(fā)送請求過來。如果接收到請求抖僵,則創(chuàng)建一個(gè)新的線程處理這個(gè)連接請求鲤看。

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)聽端口,指定最大連接數(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ù)器端程序相對應(yīng)的客戶端程序見完整代碼

3. UDP連接

3.1 客戶端

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

客戶端不再需要connect()發(fā)起連接,而是通過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)聽,只需要通過recvfrom()獲取客戶端發(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. 遇到的問題

在編程實(shí)現(xiàn)的過程中遇到不少問題堰酿,排除拼寫錯(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)聽触创。這時(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. 菜鳥教程 Python網(wǎng)絡(luò)編程
  3. 計(jì)算機(jī)網(wǎng)絡(luò)基礎(chǔ)知識(shí)總結(jié)

完整代碼
Github/zc12345

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哼绑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碉咆,更是在濱河造成了極大的恐慌抖韩,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疫铜,死亡現(xiàn)場離奇詭異茂浮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)壳咕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門励稳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人囱井,你說我怎么就攤上這事驹尼。” “怎么了庞呕?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵新翎,是天一觀的道長程帕。 經(jīng)常有香客問我,道長地啰,這世上最難降的妖魔是什么愁拭? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮亏吝,結(jié)果婚禮上岭埠,老公的妹妹穿的比我還像新娘。我一直安慰自己蔚鸥,他們只是感情好惜论,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布纸镊。 她就那樣靜靜地躺著季惯,像睡著了一般娜汁。 火紅的嫁衣襯著肌膚如雪拟淮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天漾稀,我揣著相機(jī)與錄音躲庄,去河邊找鬼涧郊。 笑死预愤,一個(gè)胖子當(dāng)著我的面吹牛沟于,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播植康,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼社裆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了向图?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤标沪,失蹤者是張志新(化名)和其女友劉穎榄攀,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體金句,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡檩赢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了违寞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贞瞒。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖趁曼,靈堂內(nèi)的尸體忽然破棺而出军浆,到底是詐尸還是另有隱情,我是刑警寧澤挡闰,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布乒融,位于F島的核電站掰盘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏赞季。R本人自食惡果不足惜愧捕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望申钩。 院中可真熱鬧次绘,春花似錦、人聲如沸撒遣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽愉舔。三九已至钢猛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間轩缤,已是汗流浹背命迈。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留火的,地道東北人壶愤。 一個(gè)月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像馏鹤,于是被迫代替她去往敵國和親征椒。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350

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