TCP与UDP协议

  1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
  2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
  3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略

粘包现象

socket收发消息的原理

应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。

而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。怎样定义消息呢?

可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。

#1:不管是recv还是send都不是直接接收对方的数据,而是操作自己的操作系统内存--->不是一个send对应一个recv
#2:recv:
wait data 耗时非常长
copy data send:
copy data

例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

只有TCP有粘包现象,UDP永远不会粘包

粘包不一定会发生:

如果发生了:1.可能是在客户端已经粘了

      2.客户端没有粘,可能是在服务端粘了

 客户端粘包:

发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据量很小,TCP优化算法会当做一个包发出去,产生粘包)

client端:
import socket
import time client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',9904)) client.send('hello'.encode('utf-8'))
client.send('world'.encode('utf-8')) server端:
import socket
import time server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',9904)) #0-65535:0-1024给操作系统使用
server.listen(5) conn, addr=server.accept()
print('connect by ',addr)
res1 = conn.recv(100)
print('第一次',res1)
res2=conn.recv(10)
print('第二次', res2)

 服务端输出结果

connect by  ('127.0.0.1', 9787)
第一次 b'helloworld'
第二次 b''

 

服务端粘包

接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

server端:
import socket
import time
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',9904)) #0-65535:0-1024给操作系统使用
server.listen(5) conn, addr=server.accept()
print('connect by ',addr)
res1 = conn.recv(2)#第一没有接收完整
print('第一次',res1)
time.sleep(6)
res2=conn.recv(10)# 第二次会接收旧数据,再收取新的
print('第二次', res2) client端
import socket
import time client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) client.connect(('127.0.0.1',9904)) client.send('hello'.encode('utf-8'))
time.sleep(5)
client.send('world'.encode('utf-8'))

 服务端输出

connect by  ('127.0.0.1', 10184)
第一次 b'he'
第二次 b'lloworld'

解决粘包问题  

问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是发送端在发送数据前,发一个头文件包,告诉发送的字节流总大小,然后接收端来一个死循环接收完所有数据

使用struct模块可以用于将Python的值根据格式符,转换为字符串(byte类型)

struct模块中最重要的三个函数是pack(), unpack(), calcsize()

pack(fmt, v1, v2, ...)     按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)

unpack(fmt, string)       按照给定的格式(fmt)解析字节流string,返回解析出来的tuple

calcsize(fmt)                 计算给定的格式(fmt)占用多少字节的内存

struct中支持的格式如下表:

Format C Type Python 字节数
x pad byte no value 1
c char string of length 1 1
b signed char integer 1
B unsigned char integer 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I unsigned int integer or long 4
l long integer 4
L unsigned long long 4
q long long long 8
Q unsigned long long long 8
f float float 4
d double float 8
s char[] string 1

使用案例

import struct
res = struct.pack('i',123)
print(res,type(res), len(res)) # b'{\x00\x00\x00' <class 'bytes'> 4 封装一个4个字节的包 res1=struct.pack('q',11122232323)
print(res1,type(res1), len(res1)) # b'\x03\xcc\xef\x96\x02\x00\x00\x00' <class 'bytes'> 8 封装一个8个字节的包 print(struct.unpack('i',res)[0]) # 拆包
print(struct.unpack('q',res1)[0]) # #输出
# b'{\x00\x00\x00' <class 'bytes'> 4
# b'\x03\xcc\xef\x96\x02\x00\x00\x00' <class 'bytes'> 8
# (123,)
# (11122232323,)

 

解决粘包问题简单版(适用于传输字节较小)

server

import socket
import subprocess
import struct def cmd_exec(cmd):
"""
执行shell命令
:param cmd:
:return:
"""
p = subprocess.Popen(cmd, shell=True,
stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout, stderr = p.communicate()
if p.returncode != 0:
return stderr
return stdout sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 重用地址端口
sock_server.bind(('127.0.0.1', 8088)) sock_server.listen(1) # 开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
print('starting...')
while True:
conn, client_addr = sock_server.accept() # 阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象 print(client_addr) while True:
try:
data = conn.recv(1024) # 接收1024个字节
if not data: break # 适用于linux操作系统,防止客户端断开连接后死循环
print('客户端的命令', data.decode('gbk'))
res = cmd_exec(data.decode('gbk')) # 执行cmd命令
# 第一步:制作固定长度的报头4bytes
total_size = len(res)
header = struct.pack('i',total_size) # 第二步:把报头发送给客户端
conn.send(header) # 第三步:再发送真实的数据
conn.sendall(res) except ConnectionResetError: # 适用于windows操作系统,防止客户端断开连接后死循环
break
conn.close() server.close()

client

import socket
import struct client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(client) client.connect(('127.0.0.1', 8088)) while True:
data = input('input >>>')
if not data: # 如果数据为空,继续输入
continue client.send(data.encode('GBK')) # 发送数据 # 第一步:先收报头
header = client.recv(4)
# 第二步:从报头中解析出对真实数据的描述信息(数据的长度)
total_size = struct.unpack('i',header)[0]
print('收到数据长度=',total_size) # 第三步:接收真实的数据
recv_size = 0
recv_data = b''
while recv_size < total_size:
data = client.recv(1024) # 接收数据
recv_data += data
recv_size += len(data) # 不能加1024,如果加进度条,会计算有误 print('接收数据 =', recv_data.decode('gbk', 'ignore')) # 如果设置为ignore,则会忽略非法字符; client.close() # 关闭

输出结果:

server端
starting...
('127.0.0.1', 13338)
客户端的命令 dir
客户端的命令 ipconfig/all client端:
"C:\Program Files\Python36\python.exe" "路飞/第三模块/第二章网络编程/01 简单的套接字通信/客户端.py"
<socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
input >>>dir
收到数据长度= 477
接收数据 = 驱动器 C 中的卷是 BOOTCAMP
卷的序列号是 D471-4F4D C:路飞\第三模块\第二章网络编程\01 简单的套接字通信 的目录 2018/07/07 14:02 <DIR> .
2018/07/07 14:02 <DIR> ..
2018/07/05 22:43 594 cmd_util.py
2018/07/07 14:02 971 客户端.py
2018/07/07 14:01 1,673 服务端.py
3 个文件 3,238 字节
2 个目录 28,749,410,304 可用字节 input >>>ipconfig/all
收到数据长度= 7702
接收数据 =
Windows IP 配置 主机名 . . . . . . . . . . . . . : PC
主 DNS 后缀 . . . . . . . . . . . :
节点类型 . . . . . . . . . . . . : 混合
IP 路由已启用 . . . . . . . . . . : 否
WINS 代理已启用 . . . . . . . . . : 否 以太网适配器 本地连接 3: 媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Bluetooth PAN Network Adapter
物理地址. . . . . . . . . . . . . : 60-F8-1D-zz-89-EF
DHCP 已启用 . . . . . . . . . . . : 是
自动配置已启用. . . . . . . . . . : 是 无线局域网适配器 无线网络连接: 连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Broadcom 802.11ac Network Adapter
物理地址. . . . . . . . . . . . . : 60-F8-1D-AD-zz-EE
DHCP 已启用 . . . . . . . . . . . : 是
自动配置已启用. . . . . . . . . . : 是
本地链接 IPv6 地址. . . . . . . . : fe80::55d1:e185:f929:8ce3%13(首选)
IPv4 地址 . . . . . . . . . . . . : 192.168.31.125(首选)
子网掩码 . . . . . . . . . . . . : 255.255.255.0
获得租约的时间 . . . . . . . . . : 2018年7月7日 9:27:54
租约过期的时间 . . . . . . . . . : 2018年7月8日 1:25:52
默认网关. . . . . . . . . . . . . : 192.168.31.1
DHCP 服务器 . . . . . . . . . . . : 192.168.31.1
DHCPv6 IAID . . . . . . . . . . . : 291567645
DHCPv6 客户端 DUID . . . . . . . : 00-01-00-zz-7C-0D-6E-60-F8-1D-AD-89-EE
DNS 服务器 . . . . . . . . . . . : 114.114.114.114
TCPIP 上的 NetBIOS . . . . . . . : 已启用 以太网适配器 VirtualBox Host-Only Network: 连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : VirtualBox Host-Only Ethernet Adapter
物理地址. . . . . . . . . . . . . : 0A-00-27-00-zz-13
DHCP 已启用 . . . . . . . . . . . : 否
自动配置已启用. . . . . . . . . . : 是
本地链接 IPv6 地址. . . . . . . . : fe80::7d26:2c96:84f1:6c4d%19(首选)
自动配置 IPv4 地址 . . . . . . . : 169.254.108.77(首选)
子网掩码 . . . . . . . . . . . . : 255.255.0.0
默认网关. . . . . . . . . . . . . : 192.168.56.255
DHCPv6 IAID . . . . . . . . . . . : 336199719
DHCPv6 客户端 DUID . . . . . . . : 00-01-00-01-21-7C-0zz60-F8-1D-AD-89-EE
DNS 服务器 . . . . . . . . . . . : fec0:0:0:ffff::1%1
fec0:0:0:ffff::2%1
fec0:0:0:ffff::3%1
TCPIP 上的 NetBIOS . . . . . . . : 已启用 以太网适配器 VirtualBox Host-Only Network #2: 连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : VirtualBox Host-Only Ethernet Adapter #2
物理地址. . . . . . . . . . . . . : 0A-00-27-00-00-14
DHCP 已启用 . . . . . . . . . . . : 否
自动配置已启用. . . . . . . . . . : 是
本地链接 IPv6 地址. . . . . . . . : fe80::641c:b67e:fa43:a28d%20(首选)
IPv4 地址 . . . . . . . . . . . . : 192.168.96.1(首选)
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
DHCPv6 IAID . . . . . . . . . . . : 537526311
DHCPv6 客户端 DUID . . . . . . . : 00-01-00-01-21-7C-0D-6E-60-F8-1D-AD-89-EE
DNS 服务器 . . . . . . . . . . . : fec0:0:0:ffff::1%1
fec0:0:0:ffff::2%1
fec0:0:0:ffff::3%1
TCPIP 上的 NetBIOS . . . . . . . : 已启用 以太网适配器 VMware Network Adapter VMnet1: 连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet1
物理地址. . . . . . . . . . . . . : 00-50-56-C0-00-01
DHCP 已启用 . . . . . . . . . . . : 是
自动配置已启用. . . . . . . . . . : 是
本地链接 IPv6 地址. . . . . . . . : fe80::20c1:b2f0:8bff:626c%25(首选)
IPv4 地址 . . . . . . . . . . . . : 192.168.109.1(首选)
子网掩码 . . . . . . . . . . . . : 255.255.255.0
获得租约的时间 . . . . . . . . . : 2018年7月7日 9:27:50
租约过期的时间 . . . . . . . . . : 2018年7月7日 14:27:49
默认网关. . . . . . . . . . . . . :
DHCP 服务器 . . . . . . . . . . . : 192.168.109.254
DHCPv6 IAID . . . . . . . . . . . : 385896534
DHCPv6 客户端 DUID . . . . . . . : 00-01-00-01-21-7C-0D-6E-60-F8-1D-AD-89-EE
DNS 服务器 . . . . . . . . . . . : fec0:0:0:ffff::1%1
fec0:0:0:ffff::2%1
fec0:0:0:ffff::3%1
TCPIP 上的 NetBIOS . . . . . . . : 已启用 以太网适配器 VMware Network Adapter VMnet8: 连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet8
物理地址. . . . . . . . . . . . . : 00-50-56zz-00-08
DHCP 已启用 . . . . . . . . . . . : 是
自动配置已启用. . . . . . . . . . : 是
本地链接 IPv6 地址. . . . . . . . : fe80::61fd:5f66:1f70:cb3d%26(首选)
IPv4 地址 . . . . . . . . . . . . : 192.168.5.1(首选)
子网掩码 . . . . . . . . . . . . : 255.255.255.0
获得租约的时间 . . . . . . . . . : 2018年7月7日 9:27:49
租约过期的时间 . . . . . . . . . : 2018年7月7日 14:27:48
默认网关. . . . . . . . . . . . . :
DHCP 服务器 . . . . . . . . . . . : 192.168.5.254
DHCPv6 IAID . . . . . . . . . . . : 402673750
DHCPv6 客户端 DUID . . . . . . . : 00-01-00-01-21-7C-0D-6E-60-F8-1D-AD-89-EE
DNS 服务器 . . . . . . . . . . . : fec0:0:0:ffff::1%1
fec0:0:0:ffff::2%1
fec0:0:0:ffff::3%1
主 WINS 服务器 . . . . . . . . . : 192.168.5.2
TCPIP 上的 NetBIOS . . . . . . . : 已启用 隧道适配器 本地连接* 14: 媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #2
物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
DHCP 已启用 . . . . . . . . . . . : 否
自动配置已启用. . . . . . . . . . : 是 隧道适配器 Teredo Tunneling Pseudo-Interface: 媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Teredo Tunneling Pseudo-Interface
物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
DHCP 已启用 . . . . . . . . . . . : 否
自动配置已启用. . . . . . . . . . : 是 隧道适配器 isatap.{0DA4A980-7247-4922-AAFB-55760B865C15}: 媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #3
物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
DHCP 已启用 . . . . . . . . . . . : 否
自动配置已启用. . . . . . . . . . : 是 隧道适配器 isatap.localdomain: 媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #5
物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
DHCP 已启用 . . . . . . . . . . . : 否
自动配置已启用. . . . . . . . . . : 是 隧道适配器 本地连接* 15: 媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #6
物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
DHCP 已启用 . . . . . . . . . . . : 否
自动配置已启用. . . . . . . . . . : 是 隧道适配器 isatap.{94C5F926-3E20-4589-A88E-54A36934D42C}: 媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #8
物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
DHCP 已启用 . . . . . . . . . . . : 否
自动配置已启用. . . . . . . . . . : 是 input >>>

  

解决粘包问题优化版(适用于传输字节很大)

server端

import socket
import subprocess
import struct
import json def cmd_exec(cmd):
"""
执行shell命令
:param cmd:
:return:
"""
p = subprocess.Popen(cmd, shell=True,
stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout, stderr = p.communicate()
if p.returncode != 0:
return stderr
return stdout sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 重用地址端口
sock_server.bind(('127.0.0.1', 8088)) sock_server.listen(1) # 开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
print('starting...')
while True:
conn, client_addr = sock_server.accept() # 阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象 print(client_addr) while True:
try:
data = conn.recv(1024) # 接收1024个字节
if not data: break # 适用于linux操作系统,防止客户端断开连接后死循环
print('客户端的命令', data.decode('gbk'))
res = cmd_exec(data.decode('gbk')) # 执行cmd命令
# 第一步:制作固定长度的报头dict
header_dict ={
'filename':'文件名',
'md5':'md5值',
'total_size':len(res)
} header_json = json.dumps(header_dict, ensure_ascii='False',indent=2) # 序列化json
print(header_json) header_bytes = header_json.encode('utf-8') header = struct.pack('i', len(header_bytes)) # 第二步:把报头长度发送给客户端
conn.send(header)
# 第三步:把报头内容发送给客户端
conn.send(header_bytes) # 第四步:再发送真实的数据
conn.sendall(res) except ConnectionResetError: # 适用于windows操作系统,防止客户端断开连接后死循环
break
conn.close() server.close()

 client端

import socket
import struct
import json client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(client) client.connect(('127.0.0.1', 8088)) while True:
data = input('input >>>')
if not data: # 如果数据为空,继续输入
continue client.send(data.encode('GBK')) # 发送数据 # 第一步:先收报头
header = client.recv(4)
# 第二步:从报头中解析(header数据的长度)
header_size = struct.unpack('i',header)[0]
print('收到报头长度=', header_size) # 第三步:收到报头解析出对真实数据的描述信息
header_json = client.recv(header_size)
header_dict = json.loads(header_json)
print('收到报头内容=',header_dict)
total_size = header_dict['total_size'] # 第三步:接收真实的数据
recv_size = 0
recv_data = b''
while recv_size < total_size:
data = client.recv(1024) # 接收数据
recv_data += data
recv_size += len(data) # 不能加1024,如果加进度条,会计算有误 print('接收数据 =', recv_data.decode('gbk', 'ignore')) # 如果设置为ignore,则会忽略非法字符; client.close() # 关闭

 

结果

server端
starting...
('127.0.0.1', 15685)
客户端的命令 ls
{
"filename": "\u6587\u4ef6\u540d",
"md5": "md5\u503c",
"total_size": 61
}
客户端的命令 dir
{
"filename": "\u6587\u4ef6\u540d",
"md5": "md5\u503c",
"total_size": 477
} client端
<socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
input >>>ls
收到报头长度= 80
收到报头内容= {'filename': '文件名', 'md5': 'md5值', 'total_size': 61}
接收数据 = 'ls' 不是内部或外部命令,也不是可运行的程序
或批处理文件。 input >>>dir
收到报头长度= 81
收到报头内容= {'filename': '文件名', 'md5': 'md5值', 'total_size': 477}
接收数据 = 驱动器 C 中的卷是 BOOTCAMP
卷的序列号是 D471-4F4D 简单的套接字通信 的目录 2018/07/07 14:51 <DIR> .
2018/07/07 14:51 <DIR> ..
2018/07/05 22:43 594 cmd_util.py
2018/07/07 14:51 1,204 客户端.py
2018/07/07 14:51 2,098 服务端.py
3 个文件 3,896 字节
2 个目录 28,694,999,040 可用字节 input >>>ipconfig/all
收到报头长度= 82
收到报头内容= {'filename': '文件名', 'md5': 'md5值', 'total_size': 7702}
接收数据 =
Windows IP 配置
……

  

 

 

  

  

 

python粘包分析与解决的更多相关文章

  1. python 粘包问题及解决方法

    一粘包 TCP协议是面向对象的,面向流的,提高可靠性服务.使用了优化算法,Nagle算法.将多次间隔较少且数据量小的数据,合并成一个大的数据块,然后进行封包.这样接收端就很难分辨出来.TCP协议数据是 ...

  2. 网络编程——TCP协议、UDP协议、socket套接字、粘包问题以及解决方法

    网络编程--TCP协议.UDP协议.socket套接字.粘包问题以及解决方法 TCP协议(流式协议) ​ 当应用程序想通过TCP协议实现远程通信时,彼此之间必须先建立双向通信通道,基于该双向通道实现数 ...

  3. 网络编程----粘包以及粘包问题的解决、FTP上传

    一.粘包现象 让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig) 注意注意: res=subprocess.Popen(cmd.decode('u ...

  4. 什么是TCP粘包?怎么解决这个问题

    在socket网络编程中,都是端到端通信,由客户端端口+服务端端口+客户端IP+服务端IP+传输协议组成的五元组可以明确的标识一条连接.在TCP的socket编程中,发送端和接收端都有成对的socke ...

  5. python之路--subprocess,粘包现象与解决办法,缓冲区

    一. subprocess 的简单用法 import subprocess sub_obj = subprocess.Popen( 'dir', #系统指令 shell=True, #固定方法 std ...

  6. Python socket粘包问题(初级解决办法)

    server端配置: import socket,subprocess,struct from socket import * server=socket(AF_INET,SOCK_STREAM) s ...

  7. python 之网络编程(基于TCP协议Socket通信的粘包问题及解决)

    8.4 粘包问题 粘包问题发生的原因: 1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包),这样接收端,就难于分辨出来了,必须提供科学的拆包机制. ...

  8. Netty(三) 什么是 TCP 拆、粘包?如何解决?

    前言 记得前段时间我们生产上的一个网关出现了故障. 这个网关逻辑非常简单,就是接收客户端的请求然后解析报文最后发送短信. 但这个请求并不是常见的 HTTP ,而是利用 Netty 自定义的协议. 有个 ...

  9. 什么是 TCP 拆、粘包?如何解决(Netty)

    前言 记得前段时间我们生产上的一个网关出现了故障. 这个网关逻辑非常简单,就是接收客户端的请求然后解析报文最后发送短信. 但这个请求并不是常见的 HTTP ,而是利用 Netty 自定义的协议. 有个 ...

随机推荐

  1. 2018.11.06 NOIP训练 最大获利(profit)(01分数规划+最大权闭合子图)

    传送门 好题啊. ∑i<jpi,jK∗(200−K)>X\frac{\sum_{i<j}p_{i,j}}{K*(200-K)}>XK∗(200−K)∑i<j​pi,j​​ ...

  2. idea在哪执行maven clean?

  3. spring+springMVC+mybatis+maven+mysql环境搭建(二)

    上一篇整合了spring+mybatis,基本上还不是web工程,接下来接入springMVC,Let's go! 一.工程转换成Web工程 首先右击项目-->properties-->p ...

  4. SQL中的split方法的使用

    参数说明: 1.@String :需要split的字符串 2.@Delimiter :格式化时分隔符 3.@index :返回split后数组的值 ), ),)) ) AS BEGIN )) ) DE ...

  5. mysql order by 中文 排序

    mysql order by 中文 排序 1. 在MySQL中,我们经常会对一个字段进行排序查询,但进行中文排序和查找的时候,对汉字的排序和查找结果往往都是错误的. 这种情况在MySQL的很多版本中都 ...

  6. python3.4对已经存在的excel写入数据

    #!/usr/bin/env python # -*- coding:utf-8 -*- # __author__ = "blzhu" """ pyt ...

  7. 学以致用七---Centos7.2+python3.6.2+django2.1.1 --搭建一个网站(补充)

    补充:上一节出现的报错提示 可在settings.py 里,改成 ‘*’  ,这样所有的主机都可以访问了. 打开网页 注意红色框出来的 hello 是和 urls.py里的hello对应 urls.p ...

  8. 线段树区间覆盖 蛤玮打扫教室(zzuli 1877)

    http://acm.zzuli.edu.cn/zzuliacm/problem.php?id=1877 Description   现在知道一共有n个机房,算上蛤玮一共有m个队员,教练做了m个签,每 ...

  9. poj 3013 最短路变形

    http://poj.org/problem?id=3013 给出n个点,m个边.给出每个点的权值,每个边的权值.在m条边中选n-1条边使这n个点成为一棵树,root=1,求这棵树的最小费用,费用=树 ...

  10. Scala_模式匹配

    模式匹配 简单匹配 Scala的模式匹配最常用于match语句中.下面是一个简单的整型值的匹配实例 object TestMatch {  def main(args: Array[String]): ...