Socket
一:socket介绍
#Socket是 应用层 与 TCP/IP 协议族通信的中间软件的抽象层,Socket的设计模式是门面模式,它将极其复杂的TCP/IP 封装隐藏在Socket
接口的后面。程序员只需通过Socket的接口进行调用即可
import socket # tcp/ip套字连接 soc1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # udp/ip套字连接 soc2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#服务端套字函数: soc1.bind(('ip','端口')) #绑定主机和IP地址 soc1.listen(5) # 开始监听 soc1.accept() #被动式TCP客户端连接,阻塞等待连接的到来 #客户端套字函数: soc1.connect(('ip','端口')) #主动初始化TCP服务器连接 soc1.connect_ex(('ip','端口')) #connect()的拓展,出错时返回出错码,而不是抛出异常 #公共套字连接函数,UDP和TCP soc1.recv(1024) # 接受TCP数据,参数为缓冲区的大小 soc1.send(data='abc') # 发送TCP数据,参数为要发送的数据,另外,send在待发送数据量大于己端缓存区剩余空间时,
数据会造成丢失
soc1.sendall(data='abc') # 发送TCP数据,参数为要发送的数据,send的加强版,在待发送数据量大于己端缓存区剩余空间时, 循环调用send发送数据,直到数据发送完为止,而且不会造成数据丢失 soc1.recvfrom(1024) #接受udp数据,参数为缓存区大小 soc1.sendto(data='abc') #发送udp数据, 参数为要发送的数据 soc1.getpeername() # 连接到当前套接字的远端的地址 soc1.getsockname() #当前套接字的地址 soc1.getsockopt() #返回指定套接字的参数 soc1.setsockopt() #设置指定套接字的参数 soc1.close() #关闭套接字 #面向锁的套接字函数 soc1.setblocking() # 设置套接字的阻塞与非阻塞模式 soc1.settimeout() # 设置阻塞套接字操作的超时时间 soc1.gettimeout() #得到阻塞套接字操作的超时时间 # 面向文件的套接字函数 soc1.fileno() #套接字的文件描述符 soc1.makefile() #创建一个与该套接字相关的文件
测试小例子TCP:
from socket import * s=socket(AF_INET,SOCK_DGRAM) s.bind(('127.0.0.1',8081)) s.listen(2) #开始监听,并指定能同时处理多少个客户端的连接请求 conn,addr=s.accept() while True: ''' # 设置缓冲区大小为10个字节,一个 ascii 字符的大小为1个字节,例如一个字母的大小为1个, 所以能一次接收10个ascii字符,如果客户端发送过来的数据过大,那么它会分多次进行接受 ''' data=conn.recv(10) print('==================server========================') print(data) conn.sendall(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================') ''' #设置缓冲区大小为 1024B,也就是能一次接受服务端发送过来的数据大小为 1kb, 如果服务端发送过来的数据过大,那么会分多次进行接受 ''' data=phone.recv(1024) print(data)
测试小例子UDP:
#UDP是无链接的,先启动哪一端都不会报异常,另外,还可以同时跟多个服务端通信
from socket import * s=socket(AF_INET,SOCK_DGRAM) s.bind(('127.0.0.1',8088)) while True: data,addr=s.recvfrom(10) # 返回值为 客户端发送的数据 和 套字连接地址 print('==================server========================') print(data) s.sendto(data,addr) conn.close() phone.close()
from socket import * s=socket(AF_INET,SOCK_DGRAM) s.connect(('127.0.0.1',8088)) while True: msg=input('输入发送的数据').strip() s.sendto(msg.encode('utf-8'),('127.0.0.1',8088)) print('==================client================') data=s.recvfrom(1024) print(data) client
基于UDP实现时间服务器:
from socket import * from time import strftime server = socket(AF_INET,SOCK_DGRAM) server.bind(('127.0.0.1', 89)) while True: data, addr = server.recvfrom(1024) if not data: time_fmt = '%Y-%m-%d' else: time_fmt = data.decode('utf-8') back_msg = strftime(time_fmt) server.sendto(back_msg.encode('utf-8'), addr) server.close()
from socket import * client=socket(AF_INET,SOCK_DGRAM) # client.bind(('127.0.0.1','89')) while True: msg=input('请输入时间格式(例%Y%m%d)>>:').strip() client.sendto(msg.encode('utf-8'), ('127.0.0.1',89)) data=client.recv(1024) print(data.decode('utf-8')) tcp_client.close()
二:粘包
#粘包的产生:
粘包有两种:
#第一种粘包
发送端需要等缓冲区满才发送出去,但由于发送数据时间间隔很短,数据又很小,会造成两块数据粘在一起,这就是粘包!!!!!!!!
#例如生活中的场景:有A、B两个地点,A点有两堆0.5吨的泥沙,需要将A地点的泥沙用车运送到B地点, 车的载重是0.5吨。在将第一堆泥沙
铲到车后,由于运送速度和A点工人的干活速度非常快,第一堆泥沙运到B点后,B点还没来的急处理,立马又运了第二堆沙子过来,将两堆
沙子合在了一起,变成了1吨
from socket import * #服务端 server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',888)) server.listen(5) conn,addr=server.accept() data1=conn.recv(10) data2=conn.recv(10) print('----->',data1.decode('utf-8')) #打印 hellotom print('----->',data2.decode('utf-8')) conn.close() #客户端 import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(('127.0.0.1',888) ) s.send('hello'.encode('utf-8')) s.send('tom'.encode('utf-8'))
#第二种粘包
#接受方来不及接受缓冲区的包,客户端发送了一个数据段,服务端只收了一小部分,服务端下次再收的时候依旧是从缓冲区拿上次
遗留的数据,进而产生粘包
#第一个篮子能装2个苹果,第二个篮子能装10个苹果,第一次送来了5个苹果,第一个篮子装了两个,第二个篮子装了3个,第二次送来
5个苹果,装在了第二个篮子里。由此造成数据不独立粘在了一起
from socket import * #服务端 server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',888)) server.listen(5) conn,addr=server.accept() data1=conn.recv(2) data2=conn.recv(10) print('----->',data1.decode('utf-8')) #打印 hellotom print('----->',data2.decode('utf-8')) conn.close() #客户端 import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(('127.0.0.1',888) ) s.send('hello'.encode('utf-8')) s.send('tom'.encode('utf-8'))
第三:粘包解决方案
解决粘包问题,需要知道 struct 模块
https://www.cnblogs.com/gala/archive/2011/09/22/2184801.html
import socket,struct import subprocess s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) s.bind(('127.0.0.1',8080)) s.listen(5) while True: conn,addr=s.accept() while True: cmd=conn.recv(1024) if not cmd:break res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err=res.stderr.read() if err: back_msg=err else: back_msg=res.stdout.read() conn.send(struct.pack('i',len(back_msg))) #发送数据的长度 conn.sendall(back_msg) #发真实的内容 conn.close()
import socket,time,struct s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(('127.0.0.1',8080)) while True: msg=input('>>: ').strip() if len(msg) == 0:continue if msg == 'quit':break s.send(msg.encode('utf-8')) l=s.recv(4) # 解或压 缓冲区空间必须为4B 以上的大小 x=struct.unpack('i',l)[0] #将二进制数据变成原有的十进制数 print(x) resu=0 data = None #以下将数据进行分次取出,然后进行数据的拼接 while resu < x: teme_data = s.recv(100) data += teme_data resu += len(teme_data) print(data.decode('gbk'))
粘包问题小结:只有TCP才有粘包问题,TCP的特质是数据不会丢失,但UDP会丢失数据,安全性比UDP高
第四:FTP案例
from socket import * import struct,json auth_dict = {} msg = {} def auth(func): def __b(self): user = auth_dict['user'] pasd= auth_dict['pass'] if user == 'tom' and pasd == '123': func(self) else: msg['msg'] = '帐号或者密码错误' # 如果密码错误写入字典 return __b class Conn: def __init__(self, ip, port): self.ip = ip self.port = port def run(self): global auth_dict self.server = socket(AF_INET, SOCK_STREAM) self.server.bind((self.ip, self.port)) self.server.listen(5) while True: self.conn, self.addr = self.server.accept() # 阻塞,等待访问 while True: class FtpServer: def __init__(self, dict_data): self.dict_data = dict_data @auth def run(self): with open('33.mp4', 'wb+') as f: f.write(self.dict_data['data']) return def put(self, filename): pass head = self.conn.recv(4) # 接受数据长度 if not head: continue print(head) hean_len = struct.unpack('i', head)[0] hean_json = self.conn.recv(hean_len) # 接收字典 head_dict = json.loads(hean_json) # 转换字典 resu = 0 data = b'' while resu < head_dict['size']: temp_data = self.conn.recv(10000000) data += temp_data resu += len(temp_data) auth_dict = head_dict f = FtpServer(data) f.run() if msg: self.conn.send(msg['msg'].encode('gbk')) #将错误信息返回客户端 c = Conn('127.0.0.1',889) c.run()
from socket import * import struct,json,os class FtpClient: def __init__(self,ip, port): self.ip = ip self.port = port def run(self): self.client = socket(AF_INET, SOCK_STREAM) self.client.connect((self.ip, self.port)) while True: order = input('输入你的操作:').strip() # put xxx.txt if not order:continue l_str = order.split() #切割 print(l_str) if hasattr(self, l_str[0]): fun = getattr(self, l_str[0]) fun(l_str) def put(self, args): self.filename = args[1] self.cmd = args[0] if not os.path.isfile(self.filename): return else: self.filesize = os.path.getsize(self.filename) head_dict = {'cmd': self.cmd, 'user': 'tom', 'pass': '1234', 'size': self.filesize} head_bytes = bytes(json.dumps(head_dict), encoding='gbk') # 序列化 转化成 二进制 with open(self.filename, 'rb') as f: data = f.read() print(data) self.client.send(struct.pack('i', len(head_bytes))) self.client.sendall(head_bytes) self.client.sendall(data) msg = self.client.recv(1000).decode('gbk') print(msg) #将错误信息打印 while True:pass def loging(self): pass def send(self): pass f = FtpClient('127.0.0.1',889) f.run()

浙公网安备 33010602011771号