Day 24 tcp,udp 粘包问题,socketserver并发

osi 七层协议的传输层:TCP/UDP协议

 

 

 

  tcpudp的对比

tcp协议:

 

 传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,
流量控制等功能,保证数据能从一端传到另一端。
当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方“握手”之后,TCP 将在两个应用程序之间建立一个全双工 
(full-duplex) 的通信。

  这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。

    # 面向连接的 可靠的 全双工的 流式传输
    # 面向连接 :同一时刻只能和一个客户端通信
    #            三次握手、四次挥手
    # 可靠的 :数据不丢失、慢
    # 全双工 :能够双向通信
    # 流式传输 :粘包 无边界

 

 

udp协议:

# 用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用
在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。

   # 无连接的 面向数据包 不可靠的 快速的     

  #无连接的 :不需要accept/connect 也没有握手     

  #面向数据包的 :不会粘包     

  #不可靠的 :没有自动回复的机制     

  #快速的 :没有那些复杂的计算、保证数据传输的机制

 

 

 

 

 

 

tcp的粘包现象(4种可能导致粘包):

黏包现象只发生在tcp协议中:

1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

 

1.发送端粘包  合包机制 + 缓存区

 

 

2.接收到粘包 延迟接受 + 缓存区

 

 

 

3.流式传输

电流 高低电压

所以我们说 tcp协议是无边界的流式传输

 

4,拆包

 

 

 

 

 

解决粘包的方法:

借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。

发送时

接收时

先发送struct转换好的数据长度4字节

先接受4个字节使用struct转换成数字来获取要接收的数据长度

再发送数据

再按照长度接收数据

 

# 1.两个连续的send就会发生粘包
# 2.用struct自定义协议可以解决粘包问题
# 3.什么情况下我们不需要解决粘包 : 文件的传输
# 4.自定义协议的进阶版本
    # 先发送字符串的长度,再发送字符串
    # 先发送json的长度,再发送json,json的字典中包含着下一条信息的长度,然后按照长度接受
import struct

ret = struct.pack('i',560000)
print(ret,len(ret))    # b'\x80\x8b\x08\x00' 4

res = struct.unpack('i',ret)
print(res[0])   # 560000

 

 

服务器端:

 

file_path = r'F:\爬虫\2017年全新Python3.6网络爬虫实战案例5章(基础+实战+框架+分布式)教程34课 压缩(www.axxzy.com)(2)\[www.17zixueba.com]章节5: 分布式篇\[www.17zixueba.com]课时32:Scrapy分布式原理及Scrapy-Redis源码解析.mp4'
file_name = os.path.basename(file_path)
file_size = os.path.getsize(file_path)
file_info = {'file_size':file_size, 'file_name':file_name, 'operate':'upload'}
file_info_json = json.dumps(file_info)
file_info_bytes = file_info_json.encode()
# print(file_info_bytes) #b'{"file_size": 66300817, "file_name": "[www.17zixueba.com]\\u8bfe\\u65f632\\uff1aScrapy\\u5206\\u5e03\\u5f0f\\u539f\\u7406\\u53caScrapy-Redis\\u6e90\\u7801\\u89e3\\u6790.mp4", "operate": "upload"}'
bytes_len = len(file_info_bytes)


# 先发送文件信息的长度,再发送文件信息
sk.send(struct.pack('i', bytes_len))   #  计算出字典bytes的长度,压缩成 b'\xb6\x00\x00\x00' ,对方只要接受4个字节就可以获取到字典的具体长度了
sk.send(file_info_bytes)   # 此时,对方已经知道要接收多长的字节

# 文件上传
with open(file_path, 'rb') as f1:
    while file_size > 0:
        content = f1.read(2048)
        file_size -= len(content)
        sk.send(content)

 

 

 

客户端: 

bytes_len = conn.recv(4)  # 固定接收4个字节内容,便可以获取到 字典的长度
info_len = struct.unpack('i', bytes_len)[0] # 解压获得字典长度
js_info = conn.recv(info_len).decode() # 知道长度便可以收取到字典内容
info_dic = json.loads(js_info) # 转成字典
print(info_dic)

with open(info_dic['file_name'], 'wb') as f:
    while info_dic['file_size'] > 0:
        content = conn.recv(2048)
        f.write(content)
        info_dic['file_size'] -= len(content)

 

 

基于tcp协议的socket

服务器端:

import socket

sk = socket.socket()      # 创建套接字对象
sk.bind(('127.0.0.1', 9999))   # 绑定套接字
sk.listen(5)        # 监听

conn, addr = sk.accept()  # 接收客户端链接
ret = conn.recv(1024)  #  接收客户端信息
print(ret)          # 打印客户端信息
conn.send(b'hello')  # 给客户端发送信息

conn.close()    # 关闭客户端套接字
sk.close()      # 关闭服务器套接字

客户端:

import socket

sk = socket.socket()    # 创建套接字对象
sk.connect(('127.0.0.1', 9999)) # 尝试链接服务器
sk.send(b'i am fine')   # 发送内容
ret = sk.recv(1024)  # 对话(发送,接收)
print(ret)
sk.close()       # 关闭客户端套接字

 

基于udp协议的socket                  

服务器端:

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
udp_sk.close()                         # 关闭服务器套接字

客户端:

import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)

 

socketserver:

 

import socketserver

class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        while True:
            conn.send(b'hello')


server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),Myserver)
server.serve_forever()

 

posted @ 2019-01-25 10:04  addit  Views(132)  Comments(0)    收藏  举报