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. 分享url带中文参数,打开html操作完毕跳转jsp页面中文乱码解决

    1.在app端分享参数组合时不对传递的url进行任何编码. 2.打开html页面时使用 escape函数对有中文的参数进行编码 escape(GetQueryString("paramete ...

  2. WPF编程宝典(Pro wpf in c# 2012)(文摘)

    第一部分 基础知识 第1章 WPF概述 第2章 XAML 第3章 布局 第4章 依赖项属性 第5章 路由事件 第二部分 进一步研究WPF 第6章 控件 第7章 Application类 第8章 元素绑 ...

  3. SVN安装配置与使用

    http://www.cnblogs.com/skyway/archive/2011/08/10/2133399.html http://www.cnblogs.com/lidabo/archive/ ...

  4. Linux centos 连接网络

    1.在安装过程中不要选择网络适配器,其他正常执行 2.编辑 [root@zb ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0 一般安装完软件,打开et ...

  5. Host is not allowed to connect to this MySQL server---------------->windows10

    错误,数据库无法远程连接. 第一步,关闭本地防火墙 注意: 两台连接的机器都需要关闭 第二步,两台机器互相ping,看是否可以互相访问 丢失为零,意为可以连接. 第三步登录数据库 第四步,切换,数据库 ...

  6. 同时安装python2.7和python3.5

    同时安装python2.7和python3.5,并配置sublime ctrl+B选择运行python版本 安装python 首先是安装两个版本的python,并配置相应的环境变量 1.在下载安装好P ...

  7. silverlight导出图片文件

    新建一个Silverlight应用程序,添加下面两个控件: image控件:image1: Button控件:Click="Button1_Click"; code-Behind代 ...

  8. javascript的Mixins

    mixin在javascript里可以看作是一种从别的对象"借用"功能的方法.每一个新定义的对象都有一个 prototype属性,其他的对象就可以从这里"借用" ...

  9. (转)VmWare下安装CentOS6图文安装教程

    转自:http://www.cnblogs.com/seesea125/archive/2012/02/25/2368255.html 第一次使用VmWare和CentOS6,中间遇到不少问题,记性不 ...

  10. Java理论学时第五节。课后作业。

    随机生成10个数,填充一个数组,然后用消息框显示数组内容,接着计算数组元素的和,将结果也显示在消息框中. 设计思路:先声明一个数组,再利用for循环对每一个元素赋随机生成的值,在求所有数的和,再根据要 ...