标签:
socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
socket 与 file 的却别:
简单实例
服务端将客户端发送的字符串转成大写后再返回给客户端
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘, 8001)) sk.listen(5) connect, address = sk.accept() while True: receive_data = connect.recv(1024) if not receive_data: break connect.sendall(receive_data.upper()) connect.close()
import socket sk = socket.socket() sk.connect((‘127.0.0.1‘, 8001)) while True: msg = input(">>>").strip() if not msg: continue sk.sendall(bytes(msg, encoding=‘utf8‘)) receive_data = sk.recv(1024) print(receive_data.decode()) sk.close()
服务器与客户端交互时不能发送空字符串
sk = socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
family:
type:
proto: 默认值 0 ,一般不填
粘包问题:
sk.recv(bufsize) 在接收 socket 数据时,是有大小限制的,如果 send 的数据过大,就会出现一次性接收不完全的情况,导致后面的交互数据混乱
解决方法:
在发送数据之前,先将需要发送的 bytes 大小发送给对方,等待对方准备完成后,在将数据发送。而接收方会根据 bytes 的大小一直接收,并进行数据组合
import socket import subprocess s = socket.socket() s.bind(("127.0.0.1",9999)) s.listen(5) while True: print("Wating New Connection ... ") conn, addr = s.accept() print("\tWating IP: {0[0]} Port: {0[1]}".format(addr)) while True: # 捕获客户端发送过来的数据,最大接收 1024 字节 recv_data = conn.recv(1024) # 如果接收到的自己为空,则跳出循环 if not recv_data:break # 捕获苦短发送的命令 cmd = str(recv_data, encoding=‘utf8‘) res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = str(res.stdout.read(), encoding=‘utf8‘) error = str(res.stderr.read(), encoding=‘utf8‘) if error: if output: send_data = "%s\n%s" % (output, error) else: send_data = error else: send_data = output print(len(send_data)) # 如果需要发送的字节大约 1024,则先发送一个 Ready|{字节大小},作为标记,然后再紧接着发送数据 if len(send_data) > 1024: ready_tag = "Ready|%s" % len(send_data) conn.send(bytes(ready_tag, encoding=‘utf8‘)) start_tag = conn.recv(1024) if start_tag.deconde() == ‘Start‘: conn.send(bytes(send_data, encoding=‘utf8‘)) else: conn.send(bytes(send_data, encoding=‘utf8‘)) conn.close() print("\tIP:{0[0]} Port:{0[1]} Close Connection".format(addr))
import re import socket s = socket.socket() s.connect((‘127.0.0.1‘, 9999)) while True: try: # 捕获输入 send_data = input(">> ").strip() # 如果捕获数据为空,则继续提示输入 if not send_data:continue # 如果输入的数据为 exit,则跳出循环 if send_data == ‘exit‘:break # 发送捕获到的数据,python 3.5 之后发送的数据必须为 bytes 类型 s.send(bytes(send_data, encoding=‘utf8‘)) # 接收最大 1024 字节的数据 recv_data = s.recv(1024) # 将数据转换为 str recv_data = str(recv_data, encoding=‘utf-8‘) # 如果接收到的数据为匹配正则: Ready\|\d+$, 说明后面会有一个大约 1024的数据进行发送 # 获取到需要接收的字节大小,然后进行多次接收,并进行组合,然后再输出 if re.match(‘Ready\|\d+$‘, recv_data): # 获取数据的字节大小 msg_size = recv_data.split("|")[-1] msg_size = int(msg_size) recv_data = b"" # 接受的数据 recv_size = 0 # 已接收数据大小 s.send(bytes("Start", encoding=‘utf-8‘)) while recv_size < msg_size: recv_msg = s.recv(1024) # 多次接收 recv_data += recv_msg # 组合数据 recv_size += len(recv_msg) # 修改已接收数据大小 recv_data = str(recv_data, encoding=‘utf-8‘) # 打印数据 print(recv_data) except KeyboardInterrupt: break s.close()
通过一种机制监视多个文件描述符,一但某个描述符就绪(一般是读就绪或写就绪)能够通知程序做相应的读写操作
Linux中的 select,poll,epoll 都是IO多路复用的机制。
Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。
网络操作、文件操作、终端操作等均属于IO操作,对于windows只支持Socket操作,其他系统支持其他IO操作,但是无法检测 普通文件操作 自动上次读取是否已经变化。
select 方法
readlist, writelist, errorlist =select.
select
(rlist, wlist, xlist[, timeout])
rlist 必选参数,监听读事件
wlist 必选参数,监听写事件
xlist 必选参数,监听异常事件
timeout 可选参数,select 阻塞的超时事件,如指定时间内监听的所有时间均无变化,则返回 3 个空list,如果 timeout 未指定则一直阻塞,知道监听到变化
通过 socket 创建的 socker 服务器默认只支持一个连接的操作,当已经存在一个连接时,新来的连接会处于等待状态(sk.listen(5),最多支持 5 个客户端等待),当已有连接断开后才能连接和下一个客户端进行连接。
socketserver 模块内部使用 “IO多路复用” 和 “多线程”、“多进程” 方式实现多并发的 socket
import socketserver class Server(socketserver.BaseRequestHandler): # 这里的方法必须是 handle def handle(self): # 定义与客户端的所有交互内容 while True: receive_data = self.request.recv(1024) if not receive_data: break send_data = receive_data.upper() self.request.send(send_data) if __name__ == ‘__main__‘: # 创建 TreadingTCPServer 对象,并执行 serve_forever 方法 server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,9998), Server) server.serve_forever()
import socket s = socket.socket() s.connect((‘127.0.0.1‘,9998)) while True: inp = input(">>>") if not inp: continue if inp == ‘quit‘: break s.send(bytes(inp, encoding=‘utf-8‘)) receive_data = s.recv(1024) print(receive_data.decode()) s.close()
import socket import select import threading def process(request, client_ip): print("HOST[{0[0]}] Port[{0[1]}] Connect".format(client_ip)) request.sendall(bytes(‘欢迎连接到服务器。。。‘, encoding=‘utf-8‘)) while True: receive_data = request.recv(1024) if not receive_data: break request.sendall(receive_data.upper()) sk = socket.socket() sk.bind((‘127.0.0.1‘,8001)) sk.listen(5) while True: # 监听 socket 是否有 IO 变化 r, w, e = select.select([sk], [], [], 1) if sk in r: request, client_ip = sk.accept() # 为 socket 创建多线程 t = threading.Thread(target=process, args=(request, client_ip)) t.start() sk.close()
标签:
原文地址:http://www.cnblogs.com/wenchong/p/5906394.html