python3中网络编程的编码问题

在学习python3网络编程的时候,总是出现“a bytes-like object is required,not ‘str’ ”这种提示,很苦恼,网上也百度了一波,可是还是没有得到解决,便看了看有关编码的知识,可是看了之后还是报同样的问题,感觉应该是send() sendto(),recv() recvfrom() 这几个函数的返回值和参数值的原因,于是便查了关于这几个函数的官方文档,果然我犯的是低级错误啊,看类以后编程还是得把函数的用法弄清楚再干啊!

先来说说编码问题
关于编码的详细介绍请移步廖雪峰的python教程字符串和编码

下面的关于编码的介绍是我个人的理解,如果有误请谅解!

这里写图片描述
>

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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章