day32

半连接数

1.什么是半连接数:客户端和服务器之间建立连接时,三次握手没有成功就叫半连接数。

2.产生的原因:

   原因1 恶意客户端没有返回第三次握手信息

   原因2 服务器没空及时处理你的请求

3.socket中 listen(半连接最大数量)

  例:server = socket.socket()
         server.bind(("127.0.0.1",1688))
         server.listen(5)

   注:说明半连接数最大为5,当第6个尝试接入会直接被拒绝掉

 

粘包问题

1.什么粘包:粘包指的是数据与数据之间没有明确的分界线,导致不能正确读取数据。

2.注意:TCP协议也称之为流式协议(UDP称为数据报协议)

3.粘包 仅发生在TCP协议中,发生的场景

  1. 发送端 发送的数据量小 并且间隔短 会粘

  2. 接收端 一次性读取了两次数据的内容 会粘

  3. 接收端 没有接收完整 剩余的内容 和下次发送的粘在一起

无论是那种情况,其根本原因在于 接收端不知道数据到底有多少

解决方案就是 提前告知接收方 数据的长度

解决方案

先发长度给对方 再发真实数据

#发送端

1.使用struct 将真实数据的长度转为固定的字节数据

2.发送长度数据

3.发送真实数据

接收端

1.先收长度数据 字节数固定

2.再收真实数据 真实可能很长 需要循环接收

发送端和接收端必须都处理粘包 才算真正的解决了

案例: 远程CMD程序

#    ==================================================客户端
import socket
from 二_CMD程序 import smallTool
import struct

client = socket.socket()
try:
   client.connect(("127.0.0.1",1688))
   print("链接成功!")
   while True:
       msg = input("请输入要执行指令:").strip()
       if msg == "q": break
       if not msg: continue
       # 发送指令
       # 先发长度
       len_bytes = struct.pack("q",len(msg.encode("utf-8")))
       client.send(len_bytes)
       # 在发指令
       client.send(msg.encode("utf-8"))

       data = smallTool.recv_data(client)
       print(data.decode("GBK"))

   client.close()
except ConnectionRefusedError as e:
   print("链接服务器失败了!",e)
except ConnectionResetError as e:
   print("服务器挂了!", e)
   client.close()

服务器

import socket
import subprocess
import struct
from 二_CMD程序 import  smallTool

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",1688))
server.listen()
# back

while True:
   # socket,addr一个元组 客户端的ip和port
   client,addr = server.accept()
   print("客户端链接成功!")
   # 循环收发数据
   while True:
       try:
           cmd = smallTool.recv_data(client)
           if not cmd:
               break
           print(cmd)

           p = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
           # 不要先读err错误信息 它会卡主 原因不详 linux不会有问题 tasklist netstat - ano啥的
           data = p.stdout.read()
           err_data = p.stderr.read()

           len_size = len(data) + len(err_data)
           print("服务器返回了: %s " %  len_size)

           len_bytes = struct.pack("q",len_size)

           # 在发送真实数据前先发送 长度
           client.send(len_bytes)

           # 返回的结果刚好就是二进制
           # 发送真实数据
           client.send(data + err_data)


       except ConnectionResetError as e:
           print("客户端了挂了!",e)
           break
   client.close()

#server.close()

自定义报头

当需要在传输数据时 传呼一些额外参数时就需要自定义报头

报头本质是一个json 数据

具体过程如下:

发送端

1 发送报头长度

2 发送报头数据 其中包含了文件长度 和其他任意的额外信息

3 发送文件内容

 

接收端

1.接收报头长度

2.接收报头信息

3.接收文件内容

案例:

服务器

import socket
import os
import struct
import json
"""
客户端接链成功我就给你发个文件过去  
固定的文件下载

"""

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",1688))
server.listen()
# back

while True:
   # socket,addr一个元组 客户端的ip和port
   client,addr = server.accept()
   print("客户端链接成功!")
   f = None
   try:

       path = r"F:\2.半链接数.mp4"
       file_size = os.path.getsize(path)


       # 我想把文件名发过去
       file_info = {"file_name":"半链接数.mp4","file_size":file_size,"md5":"xxxxxxxxx"}

       json_str = json.dumps(file_info).encode("utf-8")

       # 发送报头长度
       client.send(struct.pack("q",len(json_str)))

       # 发报头
       client.send(json_str)

       # 发文件了
       # 发送文件数据
       f = open(path,"rb")
       # 循环发送文件内容   每次发2048
       while True:
           temp = f.read(2048)
           if not temp:
               break
           client.send(temp)
       print("文件发送完毕!")

   except Exception as e:
       print("出问题了",e)
   finally:
       if f:f.close()
   client.close()

   # 无论是否抛出异常 文件都要关闭

#server.close()

# 用户可以指定要下载什么文件 FTP

客户端

"""
客户端输入指令
服务器接收指令并执行 最后返回执行结果
"""

import socket
import struct
import json

client = socket.socket()
try:
   client.connect(("127.0.0.1",1688))
   print("链接成功!")

   # 1.先收报头长度
   head_size = struct.unpack("q",client.recv(8))[0]

   # 2.收报头数据
   head_str = client.recv(head_size).decode("utf-8")
   file_info = json.loads(head_str)
   print("报头数据:",file_info)
   file_size = file_info.get("file_size")
   file_name = file_info.get("file_name")


   # 3.再收文件内容
   # 已接收大小
   recv_size = 0
   buffer_size = 2048
   f = open(file_name,"wb")
   while True:
       if file_size - recv_size >= buffer_size:
           temp = client.recv(buffer_size)

       else:
           temp = client.recv(file_size - recv_size)
       f.write(temp)
       recv_size += len(temp)
       print("已下载:%s%%" % (recv_size / file_size * 100))
       if recv_size == file_size:
           break
   f.close()
except ConnectionRefusedError as e:
   print("链接服务器失败了!",e)
posted @ 2019-05-29 22:29  呼吸决定丶  阅读(243)  评论(0)    收藏  举报