本文在Socket套接字编程 (一)的基础上,继续介绍Socket编程,利用Socket建立 TCP和UDP“客户端/服务器”连接。
在 Python程序中创建TCP服务器时,下面是一段创建通用TCP服务器的一般演示代码。这仅仅只是设计服务器的一种方式。一旦熟悉了服务器设计,大家可以按照自己的需求修改下面的代码来操作服务器。
ss = socket() #创建服务器套接字
ss.bind() #套接字与地址绑定
ss.listen() #监听连接
inf_loop: #服务器无限循环
cs = ss.accept() #接受客户端连接
comm_loop: #通信循环
cs.recv()/cs.send() #对话(接收/发送)
cs.close() #关闭客户端套接字
ss.close() #关闭服务器套接字#(可选)
在 Python程序中,所有套接字都是通过socket.socket()函数创建的。因为服务器需要占用一个端口并等待客户端的请求,所以它们必须绑定到一个本地地址。因为TCP是一种面向连接的通信系统,所以在TCP 服务器开始操作之前,必须安装一些基础设施。特别地,TCP服务器必须监听(传入)的连接。一旦这个安装过程完成后,服务器就可以开始它的无限循环。在调用accept()函数之后,就开启了一个简单的(单线程)服务器,它会等待客户端的连接。在默认情况下,accept()函数是阻塞的,这说明执行操作会被暂停,直到一个连接到达为止。一旦服务器接受了一个连接,就会利用 accept()方法返回一个独立的客户端套接字,用来与即将到来的消息进行交换。
例如在下面的实例代码中,演示了socket建立TCP“客户端/服务器连接的过程,这是一个可靠的、相互通信的“客户端/服务器”。
(1)实例文件jiandanfuwu.py的功能是以TCP连接方式建立一个服务器端程序,能够接收信息并发送信息到客户端。文件jiandanfuwu.py的具体实现代码如下所示。
import socket #导入Socket模块
HOST = '' #主机地址为0.0.0.0,表示绑定本机所有网络接口ip地址
PORT= 10000 #定义端口号的初始值
#创建socket对象s,参数分别表示地址和协议类型
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#参数指定使用IP和TCP协议
s.bind((HOST,PORT)) #将套接字与地址绑定
s.listen(1) #监听连接,参数1表示最多接受多少个等待连接的客户端
conn, addr = s.accept() #接受客户端连接,建立连接后会返回一个元组,两个元素
toSend = "你好,客户端,这里是服务端!" # 设置发送数据
print('这里是服务器端',addr) #打印显示客户端地址
while True: #连接成功后
data = conn.recv(1024) #实行对话操作(接收/发送)
print("客户端的信息:",data.decode('utf-8')) #打印显示获取的信息
#data.decode()将目标二进制数据bytes解码为字符串str类型
if not data: #如果没有数据
break #终止循环
conn.sendall(toSend.encode('utf-8'))#发送数据信息
#toSend.encode()将字符串str编码为二进制数据bytes类型
conn.close () #关闭连接
在上述实例代码中,建立TCP连培之后使用 while语句多次与客户端进行数据交换,直到收到数据为空时会终止服冬器的运行。因为这只是一个服务器端程序,所以运行之后程序不会立即返回交互信息,还等待和客户端建立连接,等和客户端建立连接后才能看到具体的交互效果。
(2)实例文件jiandankehu.py的功能是建立客户端程,在此需要创建一个 socket 实例,然后调用这个socket实例的connect()函数来连接服务器端。函数connect()的语法格式如下所示。
connect(address)
参数“address”通常也是一个元组(由一个主机名/IP地址,端口构成),如果要连接本地计算机的话,主机名可直接使用“localhost”,函数connect()能够将socket 连接到远程地址为“address”的计算机。实例文件jiandankehu.py的具体实现代码如下所示。
import socket #导入socket模块
HOST = 'localhost' #定义变量HOST的初始值
PORT= 10000 #定义端口号,必须和服务端一致
#创建socket对象s,参数分别表示地址和协议类型
dataSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
dataSocket.connect((HOST,PORT)) #建立和服务器端的连接
data = "服务端你好,我是客户端! " #设置发送数据
while data:
dataSocket.sendall(data.encode('utf-8')) #发送数据”你好..."
data = dataSocket.recv(512) #实行对话操作(接收/发送)
print("获取服务端的信息:\n",data.decode('utf-8')) #打印显示接收到的服务器信息
data = input('请输入信息:\n ') #信息输入
dataSocket.close() #关闭连接
上述代码使用socket 以 TCP连接方式建立了一个简单的客户端程序,基本功能是从键盘录入的信息发送给服务器,并从服务器接收信息。因为服务器端是建立在本地“localhost"的10000端口上,所以上述代码作为其客户端程序,连接的就是本地“localhost”的10000端口。
当连接成功之后,向服务器端发送了一个默认的信息“服务端你好,我是客户端!”之后,便要从键盘录入信息向服务器端发送,直到录入空信息(敲回车)时退出 while循环,关闭socket 连接。
先运行jiandanfuwu.py服务器端程序,然后运行jiandankehu.py客户端程序,除了发送一个默认的信息外,从键盘中录入的信息都会发送给服务器,服务器收到后显示并再次转发默认信息“你好,客户端,这里是服务端!”客户端进行显示。执行效果如图所示:
在 Python程序中,当便用socket应用传输层的UDP协议建立服务器与客户端程序时,整个实现过程要比使用TCP协议简单一点。基于UDP协议的服务器与客户端在进行数据传送时,不是先建立连接,而是直接进行数据传输。在socket对象中,使用方法recvfrom()接
收数据,具体语法格式如下所示。
recvfrom(Duisize[,flags]) #bufsize用于指定缓冲区大小
方法recvfrom()主要用来从socket 接收数据,可以连接UDP协议。在socket对象中,使用方法 sendto()发送数据,具体语法格式如下所示。
sendto (bytes, address)
参数“bytes”表示要发送的数据,参数“address”表示发送信息的目标地址,由目标IP地址和端口构成的元组。主要用来通过 UDP协议将数据发送到指定的服务器端。
在 Python程序中,UDP 服务器应该不需要 TCP服务器那么多的设置,因为它们不是面向连接的。UDP 服务器除了等待传入的连接之外,几乎不需要做其他工作。例如下面是一段通用的 UDP服务器端代码。
import socket
ss = socket() #创建服务器套接字
ss.bind() #绑定服务器套接字
infloop: #服务器无限循环
cs = ss.recvfrom()/ss.sendto () #实现对话操作(接收/发送)
ss.close()#关闭服务器套接字
从上述演示代码中可以看到,除了普通的创建套接字并将其绑定到本地地址(主机名/端口号对)外,并没有额外的工作。无限循环包含接收客户端消息、打上时间戳并返回消息,然后回到等待另一条消息的状态。再一次close()调用是可选的,并且由于无限循环的缘故,它并不会被调用,但它提醒我们,它应该是优雅或智能退出方案的一部分。
注意:UDP和 TCP服务器之间的另一个显著差异是,因为数据报套接字是无连接的,所以就没有为了成功通信而使一个客户端连接到一个独立的套接字“转换的操作。这些服务器仅仅接收消息并有可能回复数据。
例如在下面的实例代码中,演示了socket建立UDP“客户端/服务器”连接的过程,UDP是一个不可靠的、相互通信的“客户端/服务器”。
实例文件UDPserve.py的功能是使用UDP 连接方式建立一个服务器端程序,将接收到的信息直接发回到客户端。文件UDPserve.py的具体实现代码如下所示。
import socket #导入 socket模块
HOST = '' #定义变量HOST的初始值
PORT= 10000 #定义变量 PORT的初始值
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((HOST, PORT)) #将套接字与地址绑定
data = True #设置变量data的初始值
while data: #如果有数据
data,address = s.recvfrom(1024) #实现对话操作(接收/发送)
if data==b'zaijian': #当接收的数据是zaiian时
break #停止循环
print('UDP服务端接收到的信息是:',data.decode('utf-8')) #显示接收到的信息
s.sendto(data, address) #发送信息
s.close() #关闭连接
在上述实现代码中,建立UDP连接之后使用while语句多次与客户进行数据交换,设置上述服务器程序建立在本机的10000端口,当收到“"zajian”"信息时退出while循环,然后关闭服务器。
实例文件UDPclient.py的具体实现代码如下所示。
import socket #导入socket模块
HOST = 'localhost' #定义变量HOST的初始值
PORT = 10000 #定义变量PORT的初始值
#创建socket对象s,参数分别表示地址和协议类型
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
data="你好,这里是UDP客户端!" #定义变量data的初始值
while data : #如果有data数据
s.sendto(data.encode('utf-8'), (HOST, PORT)) #发送数据信息
if data=='zaijian': #如果data的值是zaijian
break #停止循环
data,addr = s.recvfrom(512) #读取数据信息
print("从服务器接收信息: \n", data.decode('utf-8')) #显示从服务器端接收的信息
data = input('客户端输入信息: \n') #信息输入
s.close ()#关闭连接
上述代码使用socket以UDP连接方式建立了一个简单的客户端程序,当在客户端创建socket后,会直接向服务器端(本机的10000端口)发送数据,而没有进行连接。当用户键入“zaijian”时退出while循环,关闭本程序。执行效果如图所示。