socket粘包
原理:服务器端或客户端连续调用2次send时,数据其实并没有立刻被发送出去,而是放到了系统的socket发送缓冲区里,等缓冲区满了、或者数据等待超时了,数据才会被send到客户端,这样就把好几次的小数据拼成一个大数据,统一发送,这么做的目地是为了提高io利用效率,一次性发送总比连发好几次效率高嘛。 但也带来一个问题,就是“粘包”,即2次或多次的数据粘在了一起统一发送。
解决方法:
1. 设置超时时间 time.sleep(0.5)
2. 客户端发送确认 conn.recv(1024)
3. 添加\r\n进行进行数据区分
4. 检测最后一次还剩多少就收多少
socketserver介绍
socketserver模块实际上是socket的封装,简化了编写网络服务程序的任务。同时SocketServer模块也 是Python标准库中很多服务器框架的基础。
socketserver中包含了两种类,一种为服务类(server class),一种为请求处理类(request handle class)。前者提供了许多方法:像绑定,监听,运行…… (也就是建立连接的过程) 后者则专注于如何处理用户所发送的数据(也就是事务逻辑)。
5种类型:BaseServer,TCPServer,UnixStreamServer,UDPServer,UnixDatagramServer。
BaseServer不直接对外服务。
TCPServer针对TCP套接字流(ThreadingTCPServer、ForkingTCPServer为多并发)
UDPServer针对UDP数据报套接字(ThreadingUDPServer、ForkingUDPServer为多并发)
UnixStreamServer和UnixDatagramServer针对UNIX域套接字,不常用。
+------------+ | BaseServer | +------------+ | v +-----------+ +------------------+ | TCPServer |------->| UnixStreamServer | +-----------+ +------------------+ | v +-----------+ +--------------------+ | UDPServer |------->| UnixDatagramServer | +-----------+ +--------------------+
BaseRequestHandler类的实例h可以实现以下方法 h.handle() 调用该方法执行实际的请求操作。调用该函数可以不带任何参数,但是几个实例变量包含有用的值。h.request包含请求,h.client_address包含客户端地址,h.server包含调用处理程序的实例。
对于TCP之类的数据流服务,h.request属性是套接字对象。对于数据报服务,它是包含收到数据的字节字符串。 h.setup() 该方法在handle()之前调用。默认情况下,它不执行任何操作。如果希望服务器实现更多连接设置(如建立SSL连接),可以在这里实现。 h.finish() 调用本方法可以在执行完handle()之后执行清除操作。默认情况下,它不执行任何操作。如果setup()和handle()方法都不生成异常,则无需调用该方法。
1 import socketserver
2 import threading
3 import socket
4
5
6 class MyTCPHandler(socketserver.BaseRequestHandler):
7 def setup(self):
8 ip = self.client_address[0].strip() # 获取客户端的ip
9 port = self.client_address[1] # 获取客户端的port
10 print(ip + ":" + str(port) + " is connect!")
11
12 def handle(self):
13 # while True: # while循环
14 data = str(self.request.recv(1024), ‘ascii‘)
15 if data: # 判断是否接收到数据
16 cur_thread = threading.current_thread()
17 response = bytes("{}: {}".format(cur_thread.name, data), ‘ascii‘)
18 self.request.sendall(response)
19
20 def finish(self):
21 print("client is disconnect!")
22
23
24 def client(ip, port, message):
25 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
26 sock.connect((ip, port))
27 sock.sendall(bytes(message, ‘ascii‘))
28 response = str(sock.recv(1024), ‘ascii‘)
29 print("Received: {}".format(response))
30
31
32 if __name__ == "__main__":
33 # Port 0 means to select an arbitrary unused port
34 HOST, PORT = "localhost", 0
35
36 server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
37 ip, port = server.server_address
38
39 # Start a thread with the server -- that thread will then start one
40 # more thread for each request
41 server_thread = threading.Thread(target=server.serve_forever)
42 # Exit the server thread when the main thread terminates
43 server_thread.daemon = True
44 server_thread.start()
45 print("Server loop running in thread:", server_thread.name)
46
47 client(ip, port, "Hello World 1")
48 # client(ip, port, "Hello World 2")
49 # client(ip, port, "Hello World 3")
50
51 server.shutdown()
52 server.server_close()
server
=
socketserver.TCPServer((HOST, PORT), MyTCPHandler) # 单线程
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) # 多线程,每连接一个新用户多开一个线程
server = socketserver.ForkingTCPServer((HOST, PORT), MyTCPHandler) # 多进程,每连接一个新用户多开一个进程
BaseServer 主要用法
1 class socketserver.BaseServer(server_address, RequestHandlerClass)
2
3 fileno() # 文件描述
4
5 hand_request() # 处理单个请求
6
7 serve_forever(poll_interval=0.5) # 处理多个请求, poll_interval设置每0.5秒轮询是否关闭, 跳过超时属性,能够调service_actions()清理僵尸子进程
8
9
10 service_actions() # 能够让server_forever循环调用
11
12 shutdown() # 告诉server_forever()循环停止
13
14 server_close() # 清除server
15
16 address_family # 地址簇
17
18 RequestHandlerClass # 请求处理类,为每一个请求创建实例
19
20 server_address # (ip,port)
21
22 allow_reuse_address # 地址重用, 默认是false
23
24 socket_type # socket类型,普遍是socket.SOCK_STREAM和socket.SOCK_DGRAM
25
26 finish_request() # 实例化RequestHandlerClass实际处理请求并调用handle()方法
27
28 verify_request(request, client_address) # 返回布尔值, 默认为true, 为true处理请求, false不处理
补充知识点
查文件大小,不用把文件都读取出来:os.stat(filename).st_size