python3中網路程式設計的編碼問題
在學習python3網路程式設計的時候,總是出現“a bytes-like object is required,not ‘str’ ”這種提示,很苦惱,網上也百度了一波,可是還是沒有得到解決,便看了看有關編碼的知識,可是看了之後還是報同樣的問題,感覺應該是send() sendto(),recv() recvfrom()
這幾個函式的返回值和引數值的原因,於是便查了關於這幾個函式的官方文件,果然我犯的是低階錯誤啊,看類以後程式設計還是得把函式的用法弄清楚再幹啊!
下面的關於編碼的介紹是我個人的理解,如果有誤請諒解!
>
Unicode編碼使得在一個文字中,各個國家的文字都可以清楚的表示出來
這點ASCII是無法做到的)。(記憶體中表示)
utf-8 是秉承一種節約磁碟空間或者網路頻寬,它常用與在傳輸中,
或者是儲存於磁碟中的一種編碼。(硬碟或者需要傳輸的時候表示)
用記事本編輯的時候,從檔案讀取的UTF-8字元被轉換為Unicode字元到記憶體裡,編輯完成後,儲存的時候再把Unicode轉換為UTF-8儲存到檔案:
瀏覽網頁的時候,伺服器會把動態生成的Unicode內容轉換為UTF-8再傳輸到瀏覽器:
python的字串
在最新的Python 3版本中,字串是以Unicode編碼的,也就是說,Python的字串支援多語言。
>>> print('包含中文的string')
包含中文的string
對於單個字元的編碼,Python提供了ord()函式獲取字元的整數表示,chr()函式把編碼轉換為對應的字元:
>>> ord('a')
97
>>> ord('中')
20013
>>> chr(97)
'a'
>>> chr(20013)
'中'
由於Python的字串型別是str,在記憶體中以Unicode表示,一個字元對應若干個位元組。如果要在網路上傳輸,或者儲存到磁碟上,就需要把str變為以位元組為單位的bytes。
Python對bytes型別的資料用帶b字首的單引號或雙引號表示:b'abc'
以Unicode表示的str通過encode()方法可以編碼為指定的bytes
>>> 'abc'.encode('utf-8')
b'abc'
>>> 'abc'.encode('ascii')
b'abc'
純英文的str可以用ASCII編碼為bytes,內容是一樣的,
含有中文的str可以用UTF-8編碼為bytes。
含有中文的str無法用ASCII編碼,因為中文編碼的範圍超過了ASCII編碼的範圍,
Python會報錯。
在bytes中,無法顯示為ASCII字元的位元組,用\x##顯示。
>>> '中'.encode('utf8')
b'\xe4\xb8\xad'
>>> '中'.encode() #python3 預設就是utf-8
b'\xe4\xb8\xad'
>>> '中'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\u4e2d' in
position 0: ordinal not in range(128)
反過來,如果我們從網路或磁碟上讀取了位元組流,那麼讀到的資料就是bytes。要把bytes變為str,就需要用decode()方法:
>>> b'\xe4\xb8\xad'.decode('utf-8')
'中'
>>> b'\xe4\xb8\xad'.decode() #python3 預設就是utf-8
'中'
如果bytes中包含無法解碼的位元組,decode()方法會報錯:
>>> b'\xe4\xb8\xad\xff'.decode()
File "<stdin>", line 1
b'\xe4\xb8\xad\xff'.decode()
^
IndentationError: unexpected indent
如果bytes中只有一小部分無效的位元組,可以傳入errors='ignore'忽略錯誤的位元組:
>>>b'\xe4\xb8\xad\xff'.decode('utf8',error='ignore')
總的來說,我們需要注意bytes,str各自在什麼情況下出現,以及bytes與str的轉換。
在操作字串時,我們經常遇到str和bytes的互相轉換。為了避免亂碼問題,應當始終堅持使用UTF-8編碼對str和bytes進行轉換。
由於Python原始碼也是一個文字檔案,所以,當你的原始碼中包含中文的時候,在儲存原始碼時,就需要務必指定儲存為UTF-8編碼。當Python直譯器讀取原始碼時,為了讓它按UTF-8編碼讀取,我們通常在檔案開頭寫上這兩行:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行註釋是為了告訴Linux/OS X系統,這是一個Python可執行程式,Windows系統會忽略這個註釋;
第二行註釋是為了告訴Python直譯器,按照UTF-8編碼讀取原始碼,否則,你在原始碼中寫的中文輸出可能會有亂碼。
申明瞭UTF-8編碼並不意味著你的.py檔案就是UTF-8編碼的,必須並且要確保文字編輯器正在使用UTF-8 。
以我正在使用的sublime text3為例:
單擊Preferences->Settings
Socket的幾個函式
終於把編碼問題搞清楚了,下面就看看網路程式設計中用到的這幾個函式吧
recv()
---------------------------------------------
socket.recv(bufsize[, flags])
Receive data from the socket.
The return value is a bytes object representing the data received.
The maximum amount of data to be received at once is specified by bufsize.
返回的是bytes型別資料
bufsize是一次能接受的最大資料大小
recvfrom()
---------------------------------------------
socket.recvfrom(bufsize[, flags])
Receive data from the socket.
The return value is a pair (bytes, address)
where bytes is a bytes object representing the data received
and address is the address of the socket sending the data.
返回的是一個二元元組,第一個元素是bytes的資料,
第二個元素是傳送方的地址
send()
----------------------------------------------
socket.send(bytes[, flags])
Send data to the socket.
The socket must be connected to a remote socket.
Returns the number of bytes sent
引數是bytes型別,返回值是位元組數。
sendto()
---------------------------------------------
socket.sendto(bytes, address)
Send data to the socket.
The socket should not be connected to a remote socket,
since the destination socket is specified by address
引數是bytes型別和一個地址
弄清楚這些之後,再寫實現tcp/udp網路程式設計時就沒有出現問題了
我在電腦上開了兩臺虛擬機器,Ubuntu17(192.168.217.132)和centos7
其中Ubuntu作為服務端,centos作為客戶端,下面是具體的實現程式碼。程式碼就不講了吧!
tcp服務端(Ubuntu)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from socket import *
from time import ctime
host = ''
port = 12345
addr = (host,port)
serverSocket = socket(AF_INET,SOCK_STREAM)
serverSocket.bind(addr)
serverSocket.listen(5)
while True:
print('waiting for connect...')
clientSocket,addr1 = serverSocket.accept()
print(addr1,'connected')
while True:
data = clientSocket.recv(1024)
if not data:
break
replyMsg = data.decode().upper() + ' [' + ctime() + ']'
clientSocket.send(replyMsg.encode())
clientSocket.close()
serverSocket.close()
tcp客戶端(centos)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from socket import *
from time import ctime
host = '192.168.217.132'
port = 12345
addr = (host,port)
clientSocket = socket(AF_INET,SOCK_STREAM)
clientSocket.connect(addr)
while True:
data = input('enter messsage:')
if not data:
break
clientSocket.send(data.encode())
data = clientSocket.recv(1024)
if not data:
break
print('the message of server send: ',data.decode())
clientSocket.close()
udp服務端
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from socket import *
from time import ctime
host = ''
port = 12345
addr = (host,port)
serverSocket = socket(AF_INET,SOCK_DGRAM)
serverSocket.bind(addr)
while True:
print('waiting for connect...')
data,addr = serverSocket.recvfrom(1024)
if not data:
break
replyMsg = data.decode().upper() + ' [' + ctime() + ']'
serverSocket.sendto(replyMsg.encode(),addr)
serverSocket.close()
udp客戶端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from socket import *
from time import ctime
host = '192.168.217.132'
port = 12345
addr = (host,port)
clientSocket = socket(AF_INET,SOCK_DGRAM)
while True:
data = input('>>')
if not data:
break
clientSocket.sendto(data.encode(),addr)
data,addr = clientSocket.recvfrom(1024)
if not data:
break
print('the message of server send: ',data.decode())
clientSocket.close()