OSI七层模型
每层运行常见物理设备
七层协议数据传输的封包与解包过程
TCP三握四挥
socket层
数据传输动图如下:
socket
工作流程
socker()
模块用法简单版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import socketserver_test = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_test.bind(('127.0.0.1' , 8080 )) server_test.listen(5 ) conn, client_address = server_test.accept() print (client_address)data = conn.recv(1024 ) print (data)conn.send(data.upper()) conn.close() server_test.close()
1 2 3 4 5 6 7 8 9 10 11 12 import socketclient_test = socket.socket(sockrt.AF_INET, socket.SOCK_STREAM) client_test.connect(("127.0.0.1" , 8080 )) client_test.send('hello' .encode('utf8' )) data = client_test.recv(1024 ) print (data.decode('utf8' ))client_test.close()
处理服务端连接不断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import socketserver_test = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_test.bind(("127.0.0.1" , 8080 )) server_test.listen(5 ) while True : conn, client_address = server_test.accept() print (client_address) while True : try : data = conn.recv(1024 ) if len (data) == 0 : break print (data) conn.send(data.upper()) except Exception: break conn.close() server_test.close()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import socketclient_test = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_test.connect(('127.0.0.1' , 8080 )) while True : msg = input ('>>>:' ).strip() if len (msg) == 0 : continue client_test.send(msg.encode('utf8' )) data = client_test.recv(1024 ) print (data.decode('utf8' )) client_test.close()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 s.bind() 绑定(主机,端口号)到套接字 s.listen() 开始TCP监听 s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来 s.connect() 主动初始化TCP服务器连接 s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 s.recv() 接收TCP数据 s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完) s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完) s.recvfrom() 接收UDP数据 s.sendto() 发送UDP数据 s.getpeername() 连接到当前套接字的远端的地址 s.getsockname() 当前套接字的地址 s.getsockopt() 返回指定套接字的参数 s.setsockopt() 设置指定套接字的参数 s.close() 关闭套接字 s.setblocking() 设置套接字的阻塞与非阻塞模式 s.settimeout() 设置阻塞套接字操作的超时时间 s.gettimeout() 得到阻塞套接字操作的超时时间 s.fileno() 套接字的文件描述符 s.makefile() 创建一个与该套接字相关的文件
粘包问题 远程执行shell命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import socketimport subprocessserver_test = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_test.bind(("127.0.0.1" , 8080 )) server_test.listen(5 ) while True : conn, client_address = server_test.accept() print (client_address) while True : try : cmd = conn.recv(1024 ) if len (cmd) == 0 : break res = subprocess.Popen(cmd, shell=True , stdout=subprocess.PIPE, stderr=subprocess.PIPE) res1 = res.stdout.read() res2 = res.stderr.read() conn.send(res1 + res2) except Exception: break conn.close() server_test.close()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import socketclient_test = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_test.connect(('127.0.0.1' , 8080 )) while True : msg = input ('>>>:' ).strip() if len (msg) == 0 : continue client_test.send(msg.encode('utf8' )) data = client_test.recv(1024 ) print (data.decode('utf8' )) client_test.close()
客户端执行命令
粘包现象
只有TCP有粘包现象,UDP永远不会粘包 粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
两种粘包情况
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
解决粘包
为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据
struct
模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import struct, jsonheader_dic = {'total_size' : 10241321431312 } header_json = json.dumps(header_dic) print (header_json) print (len (header_json)) header_total_size = struct.pack('i' , len (header_json)) print (len (header_total_size)) header_upk = struct.unpack('i' , header_total_size) print (header_upk)
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import socket, struct, jsonimport subprocesss = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('127.0.0.1' , 8080 )) s.listen(5 ) while True : conn, address = s.accept() print (address) while True : try : cmd = conn.recv(1024 ) if len (cmd) == 0 : break res = subprocess.Popen(cmd, shell=True , stdout=subprocess.PIPE, stderr=subprocess.PIPE) res1 = res.stdout.read() res2 = res.stderr.read() header_dic = {'total_size' : len (res1) + len (res2)} header_json = json.dumps(header_dic) header_bytes = header_json.encode('utf8' ) header = struct.pack('i' , len (header_bytes)) conn.send(header) conn.send(header_bytes) conn.send(res1) conn.send(res2) except Exception: break conn.close()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import socket, struct, jsonc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) c.connect(('127.0.0.1' , 8080 )) while True : msg = input ('>>>: ' ).strip() if len (msg) == 0 : continue c.send(msg.encode('utf8' )) header_bytes_len = struct.unpack('i' , c.recv(4 ))[0 ] header_bytes = c.recv(header_bytes_len) header_json = header_bytes.decode('utf8' ) header_dic = json.loads(header_json) total_size = header_dic['total_size' ] recv_size = 0 res = b'' while recv_size < total_size: data = c.recv(1024 ) recv_size += len (data) res += data print (res.decode('utf8' )) c.close()
UDP协议套接字 1 2 3 4 5 6 7 8 9 10 11 12 import sockets = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('127.0.0.1' , 9999 )) while True : data, address = s.recvfrom(1024 ) print (data, address) s.sendto(data.upper(), address)
1 2 3 4 5 6 7 8 9 10 import socketc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) while True : msg = input ('>>>: ' ).strip() c.sendto(msg.encode('utf8' ), ('127.0.0.1' , 9999 )) res, s_address = c.recvfrom(1024 ) print (res.decode('utf8' ), s_address)
socketserver模块
实现并发
基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环
socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题)
server类:
request类:
TCP并发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import socketserverclass MyRequestHandler (socketserver.BaseRequestHandler ): def handle (self ): while True : try : data = self.request.recv(1024 ) if len (data) == 0 : break print (data) self.request.send(data.upper()) except Exception: break self.request.close() server = socketserver.ThreadingTCPServer(('127.0.0.1' , 8080 ), MyRequestHandler, bind_and_activate=True ) server.serve_forever()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import socketc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) c.connect(('127.0.0.1' , 8080 )) while True : msg = input ('>>>: ' ).strip() if len (msg) == 0 : continue c.send(msg.encode('utf8' )) data = c.recv(1024 ) print (data.decode('utf8' )) c.close()
UDP并发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import socketserverclass MyRequesthanlder (socketserver.BaseRequestHandler ): def handle (self ): data,server = self.request server.sendto(data.upper(),self.client_address) server = socketserver.ThreadingUDPServer(('127.0.0.1' ,9999 ),MyRequesthanlder) server.serve_forever() from socket import *client = socket(AF_INET,SOCK_DGRAM) while True : msg = input (">>>>:" ).strip() client.sendto(msg.encode('utf-8' ),('127.0.0.1' ,9999 )) res,server_addr = client.recvfrom(1024 ) print (res.decode('utf-8' ))