标签:关闭 拼接 puts 发送 lse 文件 多个 多线程 coding
Socket有一个缓冲区,缓冲区是一个流,先进先出,发送和取出的可自定义大小的,如果取出的数据未取完缓冲区,则可能存在数据怠慢。造成粘包的问题
Socket发送两条连续数据时,可能最终会拼接成一条进行发送
解决方法一:
两条数据间进行延时发送,如【tiem.sleep(0.5) #延时0.5s】
解决方法二:
每次发送后等待对方确认接收信息数据,发送一条后就立即接收等待
解决方法三:
设定接收数据大小,发送端每次发送需要发送的数据的数据大小,接收端通过设置【recv(xx)】只接收确定的大小
(在client发送文件大小后面加一个recv,直到server端回复,才开始发送文件)
简单的服务器:
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,8898)) #把地址绑定到套接字 sk.listen() #监听链接 conn,addr = sk.accept() #接受客户端链接 ret = conn.recv(1024) #接收客户端信息 print(ret) #打印客户端信息 conn.send(b‘hi‘) #向客户端发送信息 conn.close() #关闭客户端套接字 sk.close() #关闭服务器套接字(可选)
简单的客户端:
import socket sk = socket.socket() # 创建客户套接字 sk.connect((‘127.0.0.1‘,8898)) # 尝试连接服务器 sk.send(b‘hello!‘) ret = sk.recv(1024) # 对话(发送/接收) print(ret) sk.close() # 关闭客户套接字
服务器进阶:实现客户循环连接及数据循环收发
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘, 9999,)) sk.listen(5) while True: conn, address = sk.accept() conn.sendall(bytes(‘欢迎上传文件‘, encoding=‘utf-8‘)) file_size = str(conn.recv(1024), encoding=‘utf-8‘) conn.sendall(bytes(‘服务端:已经接收到文件大小,开始接收文件。。。‘, encoding=‘utf-8‘)) print(file_size) totle_size = int(file_size) has_recv = 0 f = open(‘1.pdf‘,‘wb‘) while True: if totle_size == has_recv: break data = conn.recv(1024) f.write(data) has_recv += len(data) f.close()
客户端进阶:
import os import socket obj = socket.socket() obj.connect((‘127.0.0.1‘,9999)) ret_bytes = obj.recv(1024) ret_str = str(ret_bytes,encoding=‘utf-8‘) print(ret_str) size = os.stat(‘《疯狂Python讲义》.pdf‘).st_size obj.sendall(bytes(str(size),encoding=‘utf-8‘)) # 收到服务器确认信息,才开始发送信息 print(str(obj.recv(1024),encoding=‘utf-8‘)) with open(‘《疯狂Python讲义》.pdf‘,‘rb‘) as f: for line in f: obj.sendall(line)
可以监听多个文件描述符(文件句柄)(Socket对象),一旦文件句柄出现变化,即可感知
send和sendall的区别
import socket sk1 = socket.socket() sk1.bind((‘127.0.0.1‘, 8001,)) sk1.listen() sk2 = socket.socket() sk2.bind((‘127.0.0.1‘, 8002,)) sk2.listen() sk3 = socket.socket() sk3.bind((‘127.0.0.1‘, 8003,)) sk3.listen() inputs = [sk1, sk2, sk3] import select while True: # select自动监听sk1-sk3三个对象,一旦某个句柄发生变化 # 如果有人链接,就会发生变化 # 最后一个参数每次循环,最多等待一秒 # r_list表示发生变化链接,e_list表示发生错误的链接,w_list有什么值,存储什么值 r_list, w_list, e_list = select.select(inputs, [],inputs, 1) # print(r_list, w_list, e_list) # 链接上就发送一个hello for sk in r_list: conn,address = sk.accept() conn.sendall(bytes(str(‘hello world!‘),encoding=‘utf-8‘)) conn.close() for sk in e_list: inputs.remove(sk)
import socket obj = socket.socket() obj.connect((‘127.0.0.1‘,8001)) content = str(obj.recv(1024),encoding=‘utf-8‘) print(content) obj.close()
import socket # 本质还是一个一个处理 sk1 = socket.socket() sk1.bind((‘127.0.0.1‘, 8001,)) sk1.listen() inputs = [sk1] import select while True: # select自动监听sk1-sk3三个对象,一旦某个句柄发生变化 # 如果有人链接,就会发生变化 # 最后一个参数每次循环,最多等待一秒 # r_list表示发生变化链接,e_list表示发生错误的链接,w_list有什么值,存储什么值 r_list, w_list, e_list = select.select(inputs, [], inputs, 1) # print(r_list, w_list, e_list) # 链接上就发送一个hello print("正在监听的socket对象%d,发生变化的%s" % (len(inputs), r_list)) for sk in r_list: # 有新用户链接 if sk == sk1: conn, address = sk.accept() inputs.append(conn) else: try: # 有老用户来链接 data = sk.recv(1024) # 表示确实客户端确实发送数据了 data_str = str(data, encoding=‘utf-8‘) sk.sendall(bytes(data_str, encoding=‘utf-8‘)) # 表示客户端发送为空,客户端终止的时候 except Exception as ex: inputs.remove(sk) for sk in e_list: inputs.remove(sk)
import socket obj = socket.socket() obj.connect((‘127.0.0.1‘,8001)) while True: inp = input(">>>") obj.sendall(bytes(inp,encoding=‘utf-8‘)) ret = str(obj.recv(1024),encoding=‘utf-8‘) print(ret) obj.close()
import socket # 本质还是一个一个处理 sk1 = socket.socket() sk1.bind((‘127.0.0.1‘, 8001,)) sk1.listen() inputs = [sk1, ] outputs = [] message_dict = {} import select while True: # select自动监听sk1-sk3三个对象,一旦某个句柄发生变化 # 如果有人链接,就会发生变化 # 最后一个参数每次循环,最多等待一秒 # r_list表示发生变化链接,e_list表示发生错误的链接,w_list有什么值,存储什么值 r_list, w_list, e_list = select.select(inputs, outputs, inputs, 1) # print(r_list, w_list, e_list) # 链接上就发送一个hello print("正在监听的socket对象%d,发生变化的%s" % (len(inputs), r_list)) # 通过r_list和w_list实现读写分离 # r_list只用来读取信息 for sk in r_list: # 有新用户链接 if sk == sk1: conn, address = sk.accept() inputs.append(conn) # 保存用户{‘小周‘:[]} message_dict[conn] = [] else: try: # 有老用户来链接 data = sk.recv(1024) # 表示确实客户端确实发送数据了 data_str = str(data, encoding=‘utf-8‘) # 保存收到的消息 message_dict[sk].append(data_str) # sk.sendall(bytes(data_str, encoding=‘utf-8‘)) outputs.append(sk) # 表示客户端发送为空,客户端终止的时候 except Exception as ex: inputs.remove(sk) # w_list保存谁给我发送过消息, # w_list只用来回复信息 for conn in w_list: # 拿到接受的消息 recv_str = message_dict[conn][0] # 每一次取出数据后,在删除,等待下一次信息 del message_dict[conn][0] conn.sendall(bytes(recv_str+‘好‘, encoding=‘utf-8‘)) outputs.remove(conn) for sk in e_list: inputs.remove(sk)
socket三阶段
socket,服务端只能处理一个请求
select + socket,伪并发
r_list:既读又写
r_list,w_list:读写分离
每次客户端链接,都创建一个线程去处理 = 同时处理所有请求,并发实现
类图关系:
源码流程
# -*- coding:utf-8 -*- import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): # print self.request,self.client_address,self.server conn = self.request conn.sendall(‘欢迎致电 10086,请输入1xxx,0转人工服务.‘) Flag = True while Flag: data = conn.recv(1024) if data == ‘exit‘: Flag = False elif data == ‘0‘: conn.sendall(‘通过可能会被录音.balabala一大推‘) else: conn.sendall(‘请重新输入.‘) if __name__ == ‘__main__‘: # socket + select + 多线程 # 创建IP,端口,类名 # ThreadingTCPServer.init() => TCPServer.init() => BaseServer.init() # server对象 # self.server_address (‘127.0.0.1‘,8009) # self.RequestHandlerClass MyServer # self.socket socket.socket() # self.server_bind() self.socket.bind() # self.server_activate() self.socket.listen() server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,8009),MyServer) # server对象的serve_forever方法 # 同样,在BaseServer中找到此方法,使用方法不一样,但是可以对应 # while not self.__shutdown_request while Ture # ready = selector.select(poll_interval) # _ServerSelector()--->selectors.SelectSelector--->select() # r, w, _ = self._select(self._readers, self._writers, [], timeout) # self._handle_request_noblock() # request, client_address = self.get_request() conn, address = sk.accept() # self.process_request t = threading.Thread() # self.finish_request()=> self.RequestHandlerClass(request, client_address, self) MyServer() # self.request = request # self.client_address = client_address # self.server = server # self.setup() # try: # self.handle() ----》 MyServer.handle() 最终目的达到 server.serve_forever()
标签:关闭 拼接 puts 发送 lse 文件 多个 多线程 coding
原文地址:https://www.cnblogs.com/shuimohei/p/13393329.html