socket内置方法
socket模块中为socket对象定义了一些内置方法,通过这些内置方法,可以实现socket通信,常用的方法如表1所示。
表1 socket内置方法
方法名称 | 说明 |
---|---|
bind(address) | 将套接字与通信地址address绑定 |
listen(backlog) | 将套接字变为监听套接字,并设置允许等待接受的最大连接数 |
setblocking(bool) | 设置套接字的阻塞状态,默认为True表示阻塞,False表示非阻塞 |
connect(address) | 向地址为address的进程发起连接请求 |
accept() | 接受连接请求 |
send()/sendto(adress) | 发送数据/向adress发送数据 |
recv()/recvfrom() | 接收数据 |
close() | 关闭套接字 |
下面将以套接字通信的流程为顺序来逐一介绍上述这些方法。
1. bind()——绑定地址到套接字
为确保客户端能准确地找到服务器进程,服务器进程的地址(hostname:port)通常是固定的。服务器程序在创建socket后,需调用bind()方法将服务器socket与服务器地址绑定,bind()方法的语法格式如下:
bind(address)
bind()方法的参数address是一个形如(hostname,port)的元组,address元组中的第一个元素hostname是一个字符串,表示主机地址;第二个元素port是一个整数,表示进程端口号。当元组中的hostname为空字符串时,进程使用本机的任意IP地址作为目标地址。
假设当前服务器端的socket对象为socket_server,主机名为“192.168.43.31”,端口号为“3456”,那么bind()的用法如下:
socket_server.bind(('192.168.43.31',3546))
客户端的socket也可以调用bind()方法绑定一个地址,但因为客户端进程存活时间相对较短,且客户端套接字为主动套接字,服务器在接收数据时动态地获取客户端地址已能满足需求,所以客户端一般不进行此项操作。客户端进程的端口号通常由系统随机分配。
2. listen()——服务器监听客户端请求
服务器程序的套接字应被动地监听客户端的连接请求,但默认情况下套接字工作在主动状态,可主动发送数据。调用listen()方法将使一个套接字由主动状态变为被动状态,以等待接收其它程序的连接请求。listen()方法的语法格式如下:
listen([backlog])
listen()方法中的参数backlog用于指定在拒绝新连接之前,系统允许的未完成连接数。内核为监听套接字维护了两个队列:已连接队列和未连接队列。完成三次握手过程的客户端对应的套接字将被添加到已连接队列中,处于半连接状态的客户端对应的套接字将被添加到未连接队列中。若未连接队列存满,再有新的客户端发起连接请求,该连接请求将被直接拒绝。
需要说明的是,在3.x版本的Python中,backlog是一个可选参数,若指定该参数,则其值至少为0;若缺省该参数,系统会选择一个合理的默认值为其赋值。
listen()方法的用法如下所示:
socket_server.listen(5)
3. connect()——建立与服务器的连接
connect()方法由客户端socket调用,其功能为向服务器发起连接请求。connect()方法的语法格式如下:
connect(address)
connect()方法的参数address是一个形如“(hostname,port)”的元组,用于指定服务器的地址。若连接出错,connect()方法将返回socket.error错误。
假设客户端的套接字对象为client_socket,则connect()方法的用法如下所示:
client_socket.connect(('192.168.43.31',3546))
以上示例的功能为:客户端套接字client_socket向主机名为192.168.43.31,端口号为3456的进程发起连接请求。
4. accept()——接受客户端的连接
accept()方法由服务器端的socket调用,其功能为处理客户端发起的连接请求。accept()方法的语法格式如下:
accept()
若调用accept()方法前没有客户端连接请求到达,accept()方法将会使服务器进程阻塞,直到有客户端请求连接到达时才会返回;否则accept()方法立即返回一个形如(conn,address)的元组,其中conn是新的套接字对象,用于与相应客户端进行数据交互, address是客户端进程地址,其本质为一个形如(hostname,port)的元组。
accept()方法的用法如下所示:
client_socket, address = socket_server.accept()
5. send()/sendto()——发送数据
send()、sendto()方法用于向目标进程发送数据,它们的语法格式分别如下:
send(string)
sendto(string, address)
send()方法由流式套接字调用;sendto()方法一般由数据报式套接字调用,参数string用于设置要发送的数据。需要注意的是,Python3中要求string为字节码格式,因此用户需使用如下方式传入数据:
(1)以“b'string'”的形式传入参数,例如传递的字符串为“hello itheima”,则send()方法的用法为send(b'hello itheima'),这种方式只能转换ASCII字符。
(2)通过encode()方法对字符串进行转码,encode()方法中需传入一个表示字节码格式的参数,如将字符串转为gb2312格式(针对的是Window系统)的字节码,则应使用语句send('hello itheima'.encode('gb2312'))。
sendto()方法中的参数address本质为一个形如(hostname,port)的元组,用于指定目标进程的地址。
send()、sendto()方法调用成功都会返回所发送字符串的字节数,它们的用法如下所示:
send('hello world')
sendto('hello world', ('192.168.12.32', 4567))
6. recv()、recvfrom()——接收数据
recv()、recvfrom()方法用于接收数据,它们的语法格式分别如下:
recv(bufsize)
recvfrom(bufsize)
recv()方法的参数bufsize用于设置可接收的最大数据量。若方法调用成功,返回接收到的数据。
recvfrom()方法的参数bufsize同样用于设置可接收的最大数据量。若方法调用成功,recvfrom()方法将返回一个形如(data,address)的元组,其中data是接收到的数据,address是发送数据的套接字地址。
需要注意的是,这两个方法在Python3中返回的数据都是字节码,若要以字符串形式打印,需先使用decode()方法对其进行解码,decode()方法的参数与encode()方法的参数相同,都表示数据的格式。
假设接收的数据存储在recv_info中,则使用decode()方法将recv_info转为gb2312格式的语句如下:
recv_info.decode('gb2312')
7. close()——关闭套接字
close()方法用于关闭套接字。类似于文件,套接字也是系统中的一种资源,使用完毕的套接字应及时关闭。此外,一台主机中端口的数量是有限的,系统同样应关闭空闲的套接字,避免端口浪费。
close()方法的用法如下所示:
client_socket.close()
server_socket.close()
当客户端终止后,服务器中与此客户端交互的套接字也应关闭。一般情况下,若套接字接收到的数据为空,则说明客户端进程已经关闭连接,因此可通过判断接收到的数据长度判断是否关闭与客户端交互的套接字。