(1)网络服务:
Python 提供了两个级别访问的网络服务:
1. 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
2. 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发
在下面的介绍中,主要针对的是低级别的网络服务支持的基本 socket编程
socket又称"套接字",网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一段称为一个socket。应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
(2)socket编程的连接过程:
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
1. 服务器监听:是服务器端套接字不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
2. 客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
3. 连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
(3)socket()函数:
Python 中,我们用 socket()函数来创建套接字,语法格式如下:
socket.socket([family[, type[, proto]]])
参数如下:
family: 套接字家族可以使AF_UNIX或者AF_INET
type: 套接字类型可以根据是面向连接的还是非连接分为
SOCK_STREAM
或SOCK_DGRAM
protocol: 一般不填默认为0.
(4)socket对象的常见内置方法:
函数 | 描述 |
---|---|
服务器端套接字 | |
s.bind() | 绑定地址(host,port)到套接字,以元组(host,port)的形式表示地址。 |
s.listen() | 开始监听。操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
s.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来 |
客户端套接字 | |
s.connect() | 主动初始化TCP服务器连接。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
服务器,客户端的套接字函数 | |
s.recv() | 接收TCP数据,一般接收的数据为二进制格式的数据 |
s.send() | 发送TCP数据,将string中的数据发送到连接的套接字。一般发送的数据也是二进制的形式。 |
s.sendall() | 完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 |
s.recvfrom() | 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
s.sendto() | 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 |
s.close() | 关闭套接字 |
当创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器。
示例:客户端与服务器建立连接,建立连接的端口为6666,客户机主要是向服务器发送指令,等待应答;
服务器处于监听状态,如果有客户机建立连接,就处理与此客户机之间的信息交流,并将结果发送回客户端;
服务器端:监听(接收数据)——反馈(发数据)
#TCP服务器
#导入socket模块
import socket
import os
from threading import Thread
#创建一个socket
s=socket.socket()
ip_port=('127.0.0.1',6666)
#绑定监听的地址和端口
s.bind(ip_port)
#可以连接的最大客户机的数目
s.listen(5)
def tcplink(con):
while 1:
cmd = con.recv(1024) #接收客户端的信息
print(cmd)
command=cmd.decode() #针对客户端发出的信息进行操作
if command.startswith('cd'):
os.chdir(command[2:].strip())
result="修改目录成功"
elif command == 'exit':
break
else:
result=os.popen(command).read()
if result:
con.send(result.encode())
else:
con.send(b'OK!')
con.close()
if __name__=="__main__":
while 1:
#建立一个新的连接
con,addr=s.accept()
#创建新线程来处理TCP连接
t=Thread(target=tcplink,args=(con,))
t.start()
客户端: (建立连接——发数据——接收数据)
#TCP客户端
#导入socket库
import socket
#创建一个socket对象
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#建立连接,括号内的内容为一个元组,包括地址和端口号
s.connect(('127.0.0.1',6666))
#建立连接后,向服务器发送数据
while 1:
cmd=input("command:")
if cmd=='exit':
s.send(b'exit')
break
else:
#将命令编码后发送给服务器
s.send(cmd.encode())
#接收数据
data=s.recv(65535)
#解码打印数据
print(data.decode(),len(data))
s.close()
结果如下:
客户端:(输入指令,得到结果)
服务器:(打印出客户端的指令)
TCP是建立可靠连接,UDP是面向无连接的协议。
使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。
使用UDP的通信双方分为客户端和服务器。
示例:服务器创建socket对象后,需要绑定ip地址和端口号;
SOCK_DGRAM
指定了这个Socket的类型是UDP;客户端不需要建立连接,(UDP面向无连接的),而是直接通过
sendto()
给服务器发数据
服务器:(监听状态——发数据)
#UDP 服务器
import socket
from threading import Thread
#创建一个socket
s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#绑定监听的地址和端口
ip_port=('127.0.0.1',666)
s.bind(ip_port)
while 1:
con, addr = s.recvfrom(1024)
data = con.decode()
#打印出发送过来的数据,再将ip地址和端口打印出来
print(data, addr)
if data=='exit':
continue
#回复,将消息发送回去
reply=input("Server:")
s.sendto(reply.encode(),addr)
客户端:(发数据——接收数据)
#UDP 客户端
import socket
#创建一个socket
s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ip_port=('127.0.0.1',666)
while 1:
#先发送,在接收
data=input("Client:")
s.sendto(data.encode(),ip_port)
if data=='exit':
break
con, addr = s.recvfrom(1024)
reply = con.decode()
print(reply,addr)
s.close()
结果如下:(左边为客户端,右边为服务器)