一单线程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_INETfrom datetime import datetimedef 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 socketdef 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_INETfrom base64 import b64encodefrom json import dumpsfrom threading import Threaddef 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 socketfrom json import loadsfrom base64 import b64decodedef 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()