共计 3059 个字符,预计需要花费 8 分钟才能阅读完成。
一单线程 socket
TCP 套接字就是应用 TCP 协定提供的传输服务来实现网络通信的编程接口。在 Python 中能够通过创立 socket 对象并指定 type 属性为 SOCK\_STREAM 来应用 TCP 套接字。因为一台主机可能领有多个 IP 地址,而且很有可能会配置多个不同的服务,所以作为服务器端的程序,须要在创立套接字对象后将其绑定到指定的 IP 地址和端口上。这里的端口并不是物理设施而是对 IP 地址的扩大,用于辨别不同的服务,例如咱们通常将 HTTP 服务跟 80 端口绑定,而 MySQL 数据库服务默认绑定在 3306 端口,这样当服务器收到用户申请时就能够依据端口号来确定到底用户申请的是 HTTP 服务器还是数据库服务器提供的服务。端口的取值范畴是 0~65535,而 1024 以下的端口咱们通常称之为“驰名端口”(留给像 FTP、HTTP、SMTP 等“驰名服务”应用的端口,有的中央也称之为“周知端口”),自定义的服务通常不应用这些端口,除非自定义的是 HTTP 或 FTP 这样的驰名服务。
上面的代码实现了一个提供工夫日期的服务器。
from socket import socket, SOCK_STREAM, AF_INET
from datetime import datetime
def main():
# 1. 创立套接字对象并指定应用哪种传输服务
# family=AF_INET - IPv4 地址
# family=AF_INET6 - IPv6 地址
# type=SOCK_STREAM - TCP 套接字
# type=SOCK_DGRAM - UDP 套接字
# type=SOCK_RAW - 原始套接字
server = socket(family=AF_INET, type=SOCK_STREAM)
# 2. 绑定 IP 地址和端口 (端口用于辨别不同的服务)
# 同一时间在同一个端口上只能绑定一个服务否则报错
server.bind(('192.168.1.2', 6789))
# 3. 开启监听 - 监听客户端连贯到服务器
# 参数 512 能够了解为连贯队列的大小
server.listen(512)
print('服务器启动开始监听...')
while True:
# 4. 通过循环接管客户端的连贯并作出相应的解决 (提供服务)
# accept 办法是一个阻塞办法如果没有客户端连贯到服务器代码不会向下执行
# accept 办法返回一个元组其中的第一个元素是客户端对象
# 第二个元素是连贯到服务器的客户端的地址 (由 IP 和端口两局部形成)
client, addr = server.accept()
print(str(addr) + '连贯到了服务器.')
# 5. 发送数据
client.send(str(datetime.now()).encode('utf-8'))
# 6. 断开连接
client.close()
if __name__ == '__main__':
main()
运行服务器程序后咱们能够通过 Windows 零碎的 telnet 来拜访该服务器,后果如下图所示。
telnet 192.168.1.2 6789
也能够通过 Python 的程序来实现 TCP 客户端的性能,相较于实现服务器程序,实现客户端程序就简略多了,代码如下所示。
from socket import socket
def main():
# 1. 创立套接字对象默认应用 IPv4 和 TCP 协定
client = socket()
# 2. 连贯到服务器 (须要指定 IP 地址和端口)
client.connect(('192.168.1.2', 6789))
# 3. 从服务器接收数据
print(client.recv(1024).decode('utf-8'))
client.close()
if __name__ == '__main__':
main()
二. 应用多线程或异步 I /O
没有应用多线程或者异步 I / O 的解决形式,即当服务器与一个客户端处于通信状态时,其余的客户端只能排队期待。这样的服务器并不能满足咱们的需要,咱们须要的服务器是可能同时接收和解决多个用户申请的。上面应用多线程技术解决多个用户申请的服务器,该服务器会向连贯到服务器的客户端发送一张图片。
服务器端代码:
from socket import socket, SOCK_STREAM, AF_INET
from base64 import b64encode
from json import dumps
from threading import Thread
def main():
# 自定义线程类
class FileTransferHandler(Thread):
def __init__(self, cclient):
super().__init__()
self.cclient = cclient
def run(self):
my_dict = {}
my_dict['filename'] = 'guido.jpg'
# JSON 是纯文本不能携带二进制数据
# 所以图片的二进制数据要解决成 base64 编码
my_dict['filedata'] = data
# 通过 dumps 函数将字典解决成 JSON 字符串
json_str = dumps(my_dict)
# 发送 JSON 字符串
self.cclient.send(json_str.encode('utf-8'))
self.cclient.close()
# 1. 创立套接字对象并指定应用哪种传输服务
server = socket()
# 2. 绑定 IP 地址和端口 (辨别不同的服务)
server.bind(('192.168.1.2', 5566))
# 3. 开启监听 - 监听客户端连贯到服务器
server.listen(512)
print('服务器启动开始监听...')
with open('guido.jpg', 'rb') as f:
# 将二进制数据处理成 base64 再解码成字符串
data = b64encode(f.read()).decode('utf-8')
while True:
client, addr = server.accept()
# 启动一个线程来解决客户端的申请
FileTransferHandler(client).start()
if __name__ == '__main__':
main()
客户端代码:
from socket import socket
from json import loads
from base64 import b64decode
def main():
client = socket()
client.connect(('192.168.1.2', 5566))
# 定义一个保留二进制数据的对象
in_data = bytes()
# 因为不晓得服务器发送的数据有多大每次接管 1024 字节
data = client.recv(1024)
while data:
# 将收到的数据拼接起来
in_data += data
data = client.recv(1024)
# 将收到的二进制数据解码成 JSON 字符串并转换成字典
# loads 函数的作用就是将 JSON 字符串转成字典对象
my_dict = loads(in_data.decode('utf-8'))
filename = my_dict['filename']
filedata = my_dict['filedata'].encode('utf-8')
with open('/Users/Hao/' + filename, 'wb') as f:
# 将 base64 格局的数据解码成二进制数据并写入文件
f.write(b64decode(filedata))
print('图片已保留.')
if __name__ == '__main__':
main()
正文完