定制 网站,顺德建设工程交易中心网站,php网页设计培训,wordpress网站下载文件参考#xff1a;http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注#xff1a;只有在TCP协议通信的情况下#xff0c;才会产生黏包问题 基于TCP协议实现的黏包 #!/usr/bin/env python
# -*- coding: utf-8 -*-
…参考http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注只有在TCP协议通信的情况下才会产生黏包问题 基于TCP协议实现的黏包 #!/usr/bin/env python
# -*- coding: utf-8 -*-
# tcp_server_cmd.pyimport socket
import subprocessip_port (127.0.0.1, 8080) #服务端地址及端口
BUFFERSIZE 1024 #设置缓冲区大小tcp_server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) #设置为通过TCP协议通信(默认)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)#用于socket关闭后重用socket
tcp_server_socket.bind(ip_port) #绑定ip和端口
tcp_server_socket.listen() #开始监听客户端连接while True:conn, addr tcp_server_socket.accept() #与客户端建立连接print(客户端地址, addr)while True:cmd conn.recv(BUFFERSIZE).decode(utf-8) #接收客户端输入print(cmd:, cmd)if len(cmd)1 or cmd quit: breakres subprocess.Popen(cmd, shellTrue, stdoutsubprocess.PIPE,stderrsubprocess.PIPE) #执行客户端输入命令#以下标准输出信息都只能读取一次std_out res.stdout.read() #获取输出到标准输出设备的成功信息std_err res.stderr.read() #获取输出到标准输出设备的错误信息print(stdout:,std_out.decode(gbk))print(stderr:,std_err.decode(gbk))conn.send(std_out)conn.send(std_err)conn.close() #关闭连接tcp_server_socket.close() #关闭socket tcp-server-package #!/usr/bin/env python
# -*- coding: utf-8 -*-
#tcp_client_cmd.pyimport socketip_port (127.0.0.1, 8080) #服务端地址及端口
BUFFERSIZE 1024 #设置缓冲区大小
tcp_client_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获取socket对象
tcp_client_socket.connect(ip_port) #与服务端建立连接while True:cmd input(Please input cmd ).strip() #输入命令if len(cmd) 1: continue #跳过本次循环开始下一次循环elif cmd quit: tcp_client_socket.send(cmd.encode(utf-8)) #发送中断请求给服务端break #中断循环
tcp_client_socket.send(cmd.encode(utf-8))ret tcp_client_socket.recv(BUFFERSIZE)print(ret.decode(gbk))tcp_client_socket.close() tcp-client-package 基于UDP协议实现(无黏包现象) #!/usr/bin/env python
# -*- coding: utf-8 -*-
# udp_server_cmd.pyimport socket
import subprocessip_port (127.0.0.1, 8080)
BUFFERSIZE 2048udp_server_socket socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #设置为通过UDP协议通信
udp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
udp_server_socket.bind(ip_port)while True:cmd, addr udp_server_socket.recvfrom(BUFFERSIZE)print(client ip:,addr)cmd cmd.decode(utf-8)print(cmd:,cmd)if len(cmd)1 or cmd quit:breakres subprocess.Popen(cmd, shellTrue, stdoutsubprocess.PIPE,stderrsubprocess.PIPE)std_out res.stdout.read()std_err res.stderr.read()print(stdout:, std_out.decode(gbk))print(stderr:, std_err.decode(gbk))udp_server_socket.sendto(std_out, addr)udp_server_socket.sendto(std_err, addr)udp_server_socket.close() udp-server-package #!/usr/bin/env python
# -*- coding: utf-8 -*-
# udp_client_cmd.pyimport socketip_port (127.0.0.1, 8080)
BUFFERSIZE 2048udp_client_socket socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_client_socket.connect(ip_port)while True:cmd input(Please input cmd ).strip()if len(cmd)1: continueelif cmd quit: udp_client_socket.sendto(cmd.encode(utf-8), ip_port)breakudp_client_socket.sendto(cmd.encode(utf-8), ip_port)ret, addr udp_client_socket.recvfrom(BUFFERSIZE)print(ret.decode(gbk))udp_client_socket.close() udp-client-cmd 2.黏包的成因基于TCP协议传输 tcp协议的拆包机制tcp面向流的通信是无消息保护边界的tcp的Nagle优化算法若连续几次需要send的数据都很少通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去这样接收方就收到了粘包数据接收方和发送方的缓存机制3.导致黏包的根本因素 接收方不知道消息之间的界限不知道一次性提取多少字节的数据4.黏包的解决方法 由于导致黏包的根本原因是接收端不知道发送端将要传送的字节流的长度故有如下两种解决方案 方案一在发送消息前将要发送的字节流总大小让接收端知晓然后接收端来一个死循环接收完所有数据 #!/usr/bin/env python
# -*- coding: utf-8 -*-
# tcp_server_cmd.py
实现客户端远程操作服务端命令import socket
import subprocessip_port (127.0.0.1, 8080) #服务端地址及端口
BUFFERSIZE 1024 #设置缓冲区大小tcp_server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) #设置为通过TCP协议通信(默认)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)#用于socket关闭后重用socket
tcp_server_socket.bind(ip_port) #绑定ip和端口
tcp_server_socket.listen() #开始监听客户端连接flag Truewhile flag:conn, addr tcp_server_socket.accept() #与客户端建立连接print(client ip addr:, addr)while True:cmd conn.recv(BUFFERSIZE).decode(utf-8) #接收客户端输入if len(cmd)1 or cmd quit: flag False #防止死循环在多个客户端连接时可以去掉breakres subprocess.Popen(cmd, shellTrue, stdoutsubprocess.PIPE,stderrsubprocess.PIPE) #执行客户端输入命令#以下标准输出信息都只能读取一次std_err res.stderr.read() #获取输出到标准输出设备的错误信息if std_err: #判断返回信息的类型ret std_errelse:ret res.stdout.read() #获取输出到标准输出设备的成功信息以下是方案一的核心部分conn.send(str(len(ret)).encode(utf-8)) #发送要发送信息的长度print(ret:,ret.decode(gbk))data conn.recv(BUFFERSIZE).decode(utf-8) #接收客户端准备确认信息if data recv_ready: conn.sendall(ret) #发送所有信息
conn.close() #关闭连接tcp_server_socket.close() #关闭socket tcp_server_package #!/usr/bin/env python
# -*- coding: utf-8 -*-
#client_tcp_cmd.pyimport socketip_port (127.0.0.1, 8080) #服务端地址及端口
BUFFERSIZE 1024 #设置缓冲区大小
tcp_client_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获取socket对象
tcp_client_socket.connect(ip_port) #与服务端建立连接while True:cmd input(Please input cmd ).strip() #输入命令if len(cmd) 1: continue #跳过本次循环开始下一次循环elif cmd quit: tcp_client_socket.send(cmd.encode(utf-8)) #发送中断请求给服务端break #中断循环
tcp_client_socket.send(cmd.encode(utf-8)) #发送要执行的命令以下是方案一的核心部分info_len tcp_client_socket.recv(BUFFERSIZE).decode(utf-8) #接收要接收的信息长度
tcp_client_socket.send(brecv_ready) #给服务端发送已经准备好接收信息
data bret_size 0while ret_size int(info_len): #判断信息是否已接收完data tcp_client_socket.recv(BUFFERSIZE) #接收指定大小的信息ret_size len(data) #将已经接收的信息长度累加print(data.decode(gbk))tcp_client_socket.close() #关闭socket tcp_client_package 存在的问题
程序的运行速度远快于网络传输速度所以在发送一段字节前先用send去发送该字节流长度这种方式会放大网络延迟带来的性能损耗 方案二针对方案一的问题引入struct模块struct模块可以将发送的数据长度转换成固定长度的字节 #!/usr/bin/env python
# -*- coding: utf-8 -*-
# tcp_server_cmd.py
实现客户端远程操作服务端命令import socket
import subprocess
import struct
import jsonip_port (127.0.0.1, 8080) #服务端地址及端口
BUFFERSIZE 1024 #设置缓冲区大小tcp_server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) #设置为通过TCP协议通信(默认)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)#用于socket关闭后重用socket
tcp_server_socket.bind(ip_port) #绑定ip和端口
tcp_server_socket.listen() #开始监听客户端连接flag Truewhile flag:conn, addr tcp_server_socket.accept() #与客户端建立连接print(client ip addr:, addr)while True:cmd conn.recv(BUFFERSIZE).decode(utf-8) #接收客户端输入if len(cmd)1 or cmd quit: flag False #防止死循环在多个客户端连接时可以去掉breakres subprocess.Popen(cmd, shellTrue, stdoutsubprocess.PIPE,stderrsubprocess.PIPE) #执行客户端输入命令#以下标准输出信息都只能读取一次std_err res.stderr.read() #获取输出到标准输出设备的错误信息if std_err: #判断返回信息的类型back_info std_errelse:back_info res.stdout.read() #获取输出到标准输出设备的成功信息以下是方案二的核心部分(定制化报头)head {data_size:len(back_info)}head_json json.dumps(head) #将python对象转化为json字符串head_bytes bytes(head_json, encodingutf-8) #将json字符串转化为bytes字节码对象head_struct_len struct.pack(i, len(head_bytes)) #使用struct将定制化的报头打包为4个字节的长度conn.send(head_struct_len) #发送定制报头的长度,4个字节conn.send(head_bytes) #发送定制报头信息print(back_info:,back_info.decode(gbk))conn.sendall(back_info) #发送所有的真实信息
conn.close() #关闭连接tcp_server_socket.close() #关闭socket tcp_server_package #!/usr/bin/env python
# -*- coding: utf-8 -*-
#client_tcp_cmd.pyimport socket
import struct
import jsonip_port (127.0.0.1, 8080) #服务端地址及端口
BUFFERSIZE 1024 #设置缓冲区大小
tcp_client_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获取socket对象
tcp_client_socket.connect(ip_port) #与服务端建立连接while True:cmd input(Please input cmd ).strip() #输入命令if len(cmd) 1: continue #跳过本次循环开始下一次循环elif cmd quit: tcp_client_socket.send(cmd.encode(utf-8)) #发送中断请求给服务端break #中断循环
tcp_client_socket.send(cmd.encode(utf-8)) #发送要执行的命令以下是方案二的核心部分(定制化报头)head_struct tcp_client_socket.recv(4) #接收4字节的定制报头head_json_len struct.unpack(i, head_struct)[0] #struct解包定制报头后是一个tuple,如(1024,)head_json tcp_client_socket.recv(head_json_len).decode(utf-8) #将接收的bytes字节码报头解码为json字符串head json.loads(head_json) #将json字符串转化为python对象print(head:,head)data bret_size 0while ret_size head[data_size]: #判断信息是否已接收完data tcp_client_socket.recv(BUFFERSIZE) #接收指定缓冲大小的信息ret_size len(data) #将已经接收的信息长度累加print(data.decode(gbk)) #windows默认编码是gbktcp_client_socket.close() #关闭socket tcp_client_package 5.TCP和UDP协议的简介 待补充。。。 6.补充 1.[WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试 原因端口被占用导致 解决 Windows下
C:\ netstat -ano|findstr 8080 #查找8080端口占用进程号
TCP 127.0.0.1:8080 0.0.0.0:0 LISTENING 17496
C:\ tasklist |findstr 17496 #查找17496进程号对应的程序
python.exe 17496 Console 1 10,664 K
C:\ taskkill /pid 17496 /F #杀掉17496进程
成功: 已终止 PID 为 17496 的进程。Linux下
[rootlocalhost]# netstat -nltup | grep 80 #查找80端口上的程序
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1479/nginx
[rootlocalhost]# ps -ef | grep nginx #查找nginx对应进程号
root 1479 1 0 Jul23 ? 00:00:00 nginx: master process ./nginx
[rootlocalhost]# kill -9 1479 #杀掉1479进程 2.struct模块可打包和解包的数据类型 3.socket模块方法说明 服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据
s.sendall() 发送TCP数据
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件 socket模块方法 转载于:https://www.cnblogs.com/yueyun00/p/10002730.html