在学习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()