scoket模块 粘包问题 tcp协议特点
Posted lakei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了scoket模块 粘包问题 tcp协议特点相关的知识,希望对你有一定的参考价值。
scoket()模块函数用法
import socket socket.socket(socket_family,socket_type,protocal=0) 获取tcp/ip套接字 tcpsock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 获取udp/ip套接字 udpsock =socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始tcp监听
s.accept() 被动接受tcp客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化tcp服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收tcp数据
s.send() 发送tcp数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.recvfrom() 接收udp数据
s.sendto() 发送udp数据
s.close() 关闭套接字
1 用打电话的流程快速描述socket通信 2 服务端和客户端加上基于一次连接的循环通信 3 客户端发送发空,卡主,证明是从那一个位置卡的 服务端: from socket import * phone =socket(AF_INET,SOCK_STREAM) phone.bind((‘127.0.0.1‘,8081)) phone.listen(5) conn,addr =phone.accept() while True: data =conn.recv(1024) print(‘server===>‘) print(data) conn.send(data.upper()) conn.close() phone.close() 客户端: from socket import * phone=socket(AF_INET,SOCK_STREAM) phone.connect((‘127.0.0.1‘,8081)) while True: msg=input(‘>>: ‘).strip() phone.send(msg.encode(‘utf-8‘)) print(‘client===>‘) data=phone.recv(1024) print(data) 说明卡的原有:缓冲区为空recv就卡主,引出原理图 简单演示udp 服务端 from socket import * phone =socket(AF_INET,SOCK_DGRAM) phone.bind((‘127.0.0.1‘,8082)) while True: msg,addr =phone.recvfrom(1024) phone.sendto(msg.upper(),addr) # 客户端 from socket import * phone =socket(AF_INET,SOCK_DGRAM) while True: msg=input(‘>>: ‘) phone.sendto(msg.encode(‘utf-8‘),(‘127.0.0.1‘,8082)) msg,addr =phone.recvfrom(1024) print(msg) udp客户端可以并发演示 udp客户端可以输入为空演示
基于TCP的套接字
tcp是基于连接的,必须先启动服务端,然后再启动客户端去连接服务端
tcp服务端
from socket import * ss=socket() #创建服务器套接字 ss.bind() #把地址绑定到套接字 ss.listen() #监听连接 inf_loop: #服务器无线循环 cs=ss.accept() #接收客户端连接 comm_loop: #通信循环 cs.recv()/cs.send() #对话(接收与发送) cs.close() #关闭客户端套接字 ss.close() #关闭服务器套接字(可选)
tcp客户端
cs=socket() #创建客户套接字 cs.connect() #尝试连接服务器 comm_loop: #通讯循环 cs.send()/cs.recv() #对话(发送/接收) cs.close() #关闭客户套接字
socket 通信流程与打电话流程类似
基于tcp协议通信的套接字
import socket server = socket.socket() #买手机 server.bind((‘127.0.0.1‘,8080)) #插手机卡 bind((ip,port)) server.listen(5) #开机 半整数池 conn , addr =server.accept() #待机等待接电话 print(conn) #双向通道 print(addr) #地址 (‘127.0.0.1‘, 52942) data = conn.recv(1024) #接听别人说话 只接收1024个字节 bytes print(data) #b‘hello how much?‘ conn.send(b‘hello big baby!‘) #跟别人说话 conn.close() #关闭通信连接 server.close() #关闭服务端
import socket #t套接字对象 client = socket.socket() client.connect((‘127.0.0.1‘,8080)) #找服务器 拨电话 client.send(b‘hello how much?‘) #给服务器发信息 data = client.recv(1024) #接收服务器发来的信息 print(data) client.close() #关闭客户端
加上连接循环与通信循环
import socket ip_port=(‘127.0.0.1‘,8081)#电话卡 BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 s.bind(ip_port) #手机插卡 s.listen(5) #手机待机 while True: #新增接收链接循环,可以不停的接电话 conn,addr=s.accept() #手机接电话 # print(conn) # print(addr) print(‘接到来自%s的电话‘ %addr[0]) while True: #新增通信循环,可以不断的通信,收发消息 msg=conn.recv(BUFSIZE) #听消息,听话 # if len(msg) == 0:break #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生 print(msg,type(msg)) conn.send(msg.upper()) #发消息,说话 conn.close() #挂电话 s.close() #手机关机
import socket ip_port=(‘127.0.0.1‘,8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #拨电话 while True: #新增通信循环,客户端可以不断发收消息 msg=input(‘>>: ‘).strip() if len(msg) == 0:continue s.send(msg.encode(‘utf-8‘)) #发消息,说话(只能发送字节类型) feedback=s.recv(BUFSIZE) #收消息,听话 print(feedback.decode(‘utf-8‘)) s.close() #挂电话
tcp协议特点
import socket server = socket.socket() server.bind((‘127.0.0.1‘,8808)) server.listen(5) #半连接池 等待连接的最大的客户端数 conn,addr = server.accept() # data = conn.recv(1024) # print(data) #b‘hellohellohello‘ 接收的小于1024的 所有的客户端发来的信息 data = conn.recv(4) print(data) #b‘hell‘ data = conn.recv(5) print(data) #b‘ohell‘ #会先接收上次没接收完的信息 data = conn.recv(5) print(data) #b‘ohell‘ #会先接收上次没接收完的信息 conn.close() server.close()
import socket client = socket.socket() client.connect((‘127.0.0.1‘,8808)) client.send(b‘hello‘) client.send(b‘hello‘) client.send(b‘hello‘) #tcp协议的优化特点 发送数据量小的间隔时间短 会一起发送 #会将数据量比较小的并且时间间隔比较短的数据一次性打包发送给接收端 client.close()
struct模块
import struct data=‘sdkjfksdfjsdkfjksdjfkjdsk‘ #服务端 res = struct.pack(‘i‘,len(data)) print(len(res)) #4 会把多个字符的字符串 打包成只有4个的对象 print(res) #b‘\x19\x00\x00\x00‘ #客户端 ret=struct.unpack(‘i‘,res) #解包成原来的 字符串个数 i 代表int 整形 print(ret) #(25,) print(ret[0]) #25
模拟ssh实现远程执行命令
import socket import subprocess import struct import json server =socket.socket() server.bind((‘127.0.0.1‘,8081)) server.listen(5) #半连接池 while True: conn ,addr =server.accept() #等待电话 while True: try: data = conn.recv(1024).decode(‘utf-8‘) if len(data) == 0:break obj = subprocess.Popen(data,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() print(len(stderr+stdout)) header_dic = { ‘filename‘:‘cls.av‘, ‘len‘:len(stdout+stderr) } header_bytes = json.dumps(header_dic).encode(‘utf-8‘) #制作报头 header = struct.pack(‘i‘,len(header_bytes)) conn.send(header) conn.send(header_bytes) conn.send(stdout) conn.send(stderr) #conn.send(stdout+stderr) 字符串拼接 从新拷贝一份占内存 # 和单独发也是一样的 间隔时间短的接收到的也是一起的 except ConnectionResetError: break conn.close() server.close()
import socket,struct,json client = socket.socket() client.connect((‘127.0.0.1‘,8081)) while True: msg = input(‘>>>: ‘).encode(‘utf-8‘) if len(msg)==0:continue client.send(msg) header = client.recv(4) #接收字典打包后的4个长度的字节 精确接收 print(header,‘这是报头的长度‘) head_len = struct.unpack(‘i‘,header)[0] #对这个头进行解包,获取原来的字典的字节数长度 print(head_len,‘这是字典的字节数‘) head_dic =json.loads(client.recv(head_len).decode(‘utf-8‘)) # 反序列化 接收字典的长度(精确接收)解码 #得到字典 print(head_dic,‘这是字典‘) #对需要接受的数据 进行循环接收 total_size = head_dic[‘len‘] recv_size=0 res=b‘‘ while recv_size < total_size: #字符 data = client.recv(1024) #每次接收到的信息 res +=data recv_size +=len(data) print(res.decode(‘gbk‘)) client.close()
udp套接字
udp是无连接的,先启动那一端都不会报错
udp服务端
ss=socket() #创建一个服务器的套接字 ss.bind() #绑定服务器套接字 inf_loop: #服务器无限循环 cs=ss.recvfrom()/ss.sendto() #对话(接收与发送) ss.close() #关闭服务器套接字
udp客户端
cs=socket() #创建客户端套接字 comm_loop: #通信循环 cs.sendto()/cs.recvfrom() #对话(发送/接收) cs.close() #关闭客户套接字
udp套接字简单实例
服务端 import socket server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server.bind((‘127.0.0.1‘,8080)) while True: data ,addr =server.recvfrom(1024) print(data) server.sendto(data.upper(),addr)
客户端 import socket client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server_addr=(‘127.0.0.1‘,8080) #服务端地址,通常写在配置文件中 client.sendto(b‘hello‘,server_addr)
udp 实现简易版本的qq
import socket server=socket.socket(type=socket.SOCK_DGRAM) server.bind((‘127.0.0.1‘,8080)) while True: msg,addr =server.recvfrom(1024) print(addr,msg.decode(‘utf-8‘)) data=input(‘服务端回复:‘).encode(‘utf-8‘) server.sendto(data,addr)
import socket client =socket.socket(type=socket.SOCK_DGRAM) server_addr =(‘127.0.0.1‘,8080) while True: msg = input(‘输入消息:‘) msg = ‘客服端1的消息:%s‘ % msg client.sendto(msg.encode(‘utf-8‘),server_addr) data,addr =client.recvfrom(1024) print(data.decode(‘utf-8‘))
import socket client =socket.socket(type=socket.SOCK_DGRAM) server_addr =(‘127.0.0.1‘,8080) while True: msg = input(‘输入消息:‘) msg = ‘客服端2的消息:%s‘ % msg client.sendto(msg.encode(‘utf-8‘),server_addr) data,addr =client.recvfrom(1024) print(data.decode(‘utf-8‘))
import socket client =socket.socket(type=socket.SOCK_DGRAM) server_addr =(‘127.0.0.1‘,8080) while True: msg=input(‘输入消息:‘) msg=‘客服端3的消息:%s‘%msg client.sendto(msg.encode(‘utf-8‘),server_addr) data,addr =client.recvfrom(1024) print(data.decode(‘utf-8‘))
subprocess模块
import socket import subprocess import struct ‘‘‘ 服务端必备三要素: 1 要有固定的ip和port 2 要能24小时提供服务 3 能承受高并发 并发:看上去像同时进行 ‘‘‘ server=socket.socket() server.bind((‘127.0.0.1‘,8080)) server.listen(5) while True: conn,addr =server.accept() while True: try: cmd =conn.recv(1024).decode(‘utf-8‘) if len(cmd)==0:break #针对linux mac系统 obj=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout=obj.stdout.read() stderr=obj.stderr.read() #制作固定报头 header=struct.pack(‘i‘,len(stderr+stdout)) #发送固定长度的报头 conn.send(header) #发送真是数据 conn.send(stdout) conn.send(stderr) except ConnectionResetError: break conn.close()
import socket import struct client =socket.socket() client.connect((‘127.0.0.1‘,8080)) while True: cmd =input(‘>>:‘).encode(‘utf-8‘) if len(cmd)==0:continue client.send(cmd) header=client.recv(4) #解析报头获取真是数据的长度 total_size=struct.unpack(‘i‘,header)[0] recv_size=0 data=b"" while recv_size<total_size: res=client.recv(1024) recv_size+=len(res) data+=res print(data.decode(‘gbk‘))
socketserver模块 实现并发 基于tcp的套接字,关键字就是两个循环,一个是连接循环,一个通信循环
socketserver模块中分两大类:server类(解决连接问题)和request类(解决通信问题)
基于tcp的socketserver自定义的类中的
1 self.server即套接字对象
2 self.request即一个连接
3 self.client_address即客户端地址
基于udp的socketserver自定义的类中的
1 self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象),
如(b‘adsf‘, <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=(‘127.0.0.1‘, 8080)>)
2 self.client_address即客户端地址
import socket client =socket.socket() client.connect((‘127.0.0.1‘,8080)) while True: client.send(b‘hello world‘) data= client.recv(1024) print(data)
import socketserver class MyBaby(socketserver.BaseRequestHandler): def handle(self): #通信循环 while True: #self.request相当于你的conn通信对象 data= self.request.recv(1024) #收消息 print(data) self.request.send(data.upper()) if __name__ == ‘__main__‘: #通道循环 server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,8080),MyBaby) server.serve_forever()
import socket import time client =socket.socket(type=socket.SOCK_DGRAM) server_addr=(‘127.0.0.1‘,8080) while True: client.sendto(b‘hello world‘,server_addr) data,addr =client.recvfrom(1024) print(data,addr) time.sleep(1)
import socketserver class MyBaby(socketserver.BaseRequestHandler): def handle(self): #通讯循环 while True: #self.request 相当于conn通讯对象 data,sock =self.request #收消息 print(data) print(sock) sock.sendto(data.upper(),self.client_address) #self.client_address 客户端地址 if __name__ == ‘__main__‘: server=socketserver.ThreadingUDPServer((‘127.0.0.1‘,8080),MyBaby) server.serve_forever()
基于tcp文件上传
import socket import struct import json server = socket.socket() server.bind((‘127.0.0.1‘, 8080)) server.listen(5) while True: conn, addr = server.accept() while True: try: # 接收报头 header = conn.recv(4) # 解析报头获取字典长度 header_len = struct.unpack(‘i‘, header)[0] # 接收字典 header_bytes = conn.recv(header_len) header_dic = json.loads(header_bytes.decode(‘utf-8‘)) print(header_dic) # 循环接收文件存储到本地 file_size = header_dic.get(‘file_size‘) file_name = header_dic.get(‘file_name‘) recv_size = 0 # 文件操作 with open(file_name, ‘wb‘)as f: # 循环接收文件 while recv_size < file_size: data = conn.recv(1024) f.write(data) recv_size += len(data) print(header_dic.get(‘msg‘)) except ConnectionResetError: break conn.close()
import socket import os import json import struct client = socket.socket() client.connect((‘127.0.0.1‘, 8080)) # 文件大小 file_size = os.path.getsize(r‘E:\代码存放位置\第八周\莫文蔚-偷情.mp3‘) # 文件名字 file_name = ‘歌曲名:偷情- 歌手:莫文蔚.mp3‘ # 定义一个字典 d = { "file_name": file_name, "file_size": file_size, "msg": ‘注意身体哦‘ } data_bytes = json.dumps(d).encode(‘utf-8‘) # 制作字典的报头 header = struct.pack(‘i‘, len(data_bytes)) # 发送报头 client.send(header) # 发送字典 client.send(data_bytes) # 发送真实数据 with open(r‘E:\代码存放位置\第八周\莫文蔚-偷情.mp3‘, ‘rb‘)as f: for line in f: client.send(line)
import json res = {‘name‘: ‘他说大帅比‘} # print(json.dumps(res,ensure_ascii=False)) #{"name": "他说大帅比"} print(json.dumps(res)) # {"name": "\u4ed6\u8bf4\u5927\u5e05\u6bd4"} ‘‘‘ TCP 三次握手 可靠协议,流式协议 粘包问题的产生: 1.接收方: 我不知道我要接受的数据的总长度 2.发送方: 由于TCP协议的内部优化算法negle 1.会将数据量比较的小的并且时间间隔比较短的数据一次性打包发送 UDP 数据报协议 没有双向通道 1.UDP协议不存在粘包问题 2.客户端可以发空 3.udp可以实现并发的效果 4.服务端不存在,也不影响客户端朝服务端发送数据 TCP/UDP TCP:打电话 UDP:发短信 SocketServer模块 1.能够实现并发效果 并发:看起来像同时运行就能称之位并发 2.udp在使用的时候,多个客户端要有一些io操作 不然容易卡死 并发编程 操作系统发展史 多道技术: - 空间上的复用(多个程序共一套硬件设备,它是多道技术实现时间上的复用的基础, 不然还要去硬盘读数据) - 时间上的复用(单个cpu的电脑上,起多个应用程序。cpu快速切换,给人的感觉是同时运行) cpu两种情况下才会切换:(先保存当前程序的运行状态) - 一个任务占用cpu时间过长或被操作系统强行剥夺走cpu的执行权限(比起串行效率反而降低) - 一个任务执行过程中遇到io操作,也会被操作系统强行剥夺走cpu的执行权限(比起串行效率提高) 并发:看上去像同时进行的 并行:同时运行 补充:单核的计算机不可能实现并行! ‘‘‘
以上是关于scoket模块 粘包问题 tcp协议特点的主要内容,如果未能解决你的问题,请参考以下文章