网络编程
01基于tcp协议的socket通信
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话 # socket.SOCK_STREAM 流式协议 就是TCP协议 phone.bind(('127.0.0.1', 8080)) # 买电话卡 phone.listen(5) # 开机。 # 5 不是链接数,链接可以产生N个,同一时刻只能监听5个请求。 # print(111) conn, addr = phone.accept() # 等待接电话 # 阻塞状态 # print(222) print(conn, addr) # conn 代表的是socket通信的对象,一个管道 client_data = conn.recv(1024) # 交流过程 print(client_data) conn.send(client_data.upper()) conn.close() phone.close()
import socket phone = socket.socket() # 买电话 phone.connect(('127.0.0.1', 8080)) # 拨号 msg = input('>>>').strip() phone.send(msg.encode('utf-8')) server_data = phone.recv(1024) # 限制的是最大接收字节数。 print(server_data) phone.close()
但是我们发现这个只能执行一次,就断开了,于是有了下面的通信循环。
02通信循环
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) #防止报OSError phone.bind(('127.0.0.1', 8080)) phone.listen(5) conn, addr = phone.accept() while 1: try: client_data = conn.recv(1024) print(client_data) conn.send(client_data + b'SB') #SB要转换成bytes类型 except Exception: #排除其他所有的错误 break conn.close() phone.close()
import socket phone = socket.socket() # 买电话 phone.connect(('127.0.0.1', 8080)) # 拨号 while 1: msg = input('>>>').strip() if msg.upper() == 'Q': break #选择正常退出 phone.send(msg.encode('utf-8')) server_data = phone.recv(1024) # 限制的是最大接收字节数。 print(server_data.decode('utf-8')) phone.close()
但是又发现当客户端输入q退出时,服务端就自动端口了,于是有了下面的链接循环。
03链接循环
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) phone.bind(('127.0.0.1', 8080)) phone.listen(5) while 1: conn, addr = phone.accept() print(addr) while 1: try: client_data = conn.recv(1024) print(client_data) print(222) conn.send(client_data + b'SB') print(333) except Exception: break conn.close() phone.close()
import socket phone = socket.socket() # 买电话 phone.connect(('127.0.0.1', 8080)) # 拨号 while 1: msg = input('>>>').strip() if msg.upper() == 'Q': break phone.send(msg.encode('utf-8')) server_data = phone.recv(1024) # 限制的是最大接收字节数。 print(server_data.decode('utf-8')) phone.close()
import socket phone = socket.socket() # 买电话 phone.connect(('127.0.0.1', 8080)) # 拨号 while 1: msg = input('>>>').strip() if msg.upper() == 'Q': break phone.send(msg.encode('utf-8')) server_data = phone.recv(1024) # 限制的是最大接收字节数。 print(server_data.decode('utf-8')) phone.close()
...以此类推,客户端3.4.5.6...
当客户端1输入q退出时,客户端2开始聊,客户端2输入q退出时,3.4.5....依此聊。
04远程执行命令
需要引入模块:subprocess
import subprocess obj = subprocess.Popen('dir', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) ret = obj.stdout.read().decode('gbk') ret1 = obj.stderr.read().decode('gbk') print(ret) print(ret1)
import socket import subprocess # # obj = subprocess.Popen('dir', # shell=True, # stdout=subprocess.PIPE, # stderr=subprocess.PIPE, # ) # # ret = obj.stdout.read().decode('gbk') # ret1 = obj.stderr.read().decode('gbk') # print(ret) # print(ret1) phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) phone.bind(('127.0.0.1', 8080)) phone.listen(5) conn, addr = phone.accept() while 1: try: cmd = conn.recv(1024) obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) ret = obj.stdout.read() ret1 = obj.stderr.read() conn.send(ret + ret1) except Exception: break conn.close() phone.close()
import socket phone = socket.socket() phone.connect(('127.0.0.1', 8080)) while 1: msg = input('>>>').strip() if msg.upper() == 'Q': break elif not msg: continue phone.send(msg.encode('utf-8')) server_data = phone.recv(1024) print(server_data.decode('gbk')) phone.close()
但是我们发现输入ipconfig之后,最终展示的结果是不全的,这就是粘包现象,下面看来看下粘包现象
05粘包现象
粘包现象分为两种:
# 1,流式协议。数据全部都像水一样,连在一起。 一次性发送 1519个字节(全部在客户端操作系统的缓存区)但是客户端一次只取1024字节,所以缓存区还剩495字节,下次再取。
# 粘包现象一。
import socket import subprocess # # obj = subprocess.Popen('dir', # shell=True, # stdout=subprocess.PIPE, # stderr=subprocess.PIPE, # ) # # ret = obj.stdout.read().decode('gbk') # ret1 = obj.stderr.read().decode('gbk') # print(ret) # print(ret1) phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) phone.bind(('127.0.0.1', 8082)) phone.listen(5) conn, addr = phone.accept() while 1: try: cmd = conn.recv(1024) obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) ret = obj.stdout.read() ret1 = obj.stderr.read() total_size = len(ret+ret1) #len测试的是bytes类型的数据 示例见tes.py print(total_size) conn.send(ret + ret1) except Exception: break conn.close() phone.close()
import socket phone = socket.socket() phone.connect(('127.0.0.1', 8082)) while 1: msg = input('>>>').strip() if msg.upper() == 'Q': break elif not msg: continue # 解决的是输入为空程序会夯住的问题。 phone.send(msg.encode('utf-8')) server_data = phone.recv(1024) print(server_data.decode('gbk')) phone.close()
服务端增加了一个字节:
# 2,针对于(客户端/服务端)发送的连续的小的数据,对方会一次性接收。
客户端给服务端发送两条数据,一条hello,一条world
#2 针对(客户端/服务端)发送的数据,他会一次性接收。 import socket so1 = socket.socket() so1.bind(('127.0.0.1',8888)) so1.listen(5) conn,addr = so1.accept() client_data1 = conn.recv(1024) print('data1',client_data1.decode('utf-8')) client_data2 = conn.recv(1024) print('data2',client_data2.decode('utf-8')) conn.send(client_data1) conn.close() so1.close()
#例子2: import socket phone = socket.socket() phone.connect(('127.0.0.1', 8888)) phone.send(b'hello') phone.send(b'world') print(phone.recv(1024).decode('utf-8')) phone.close()
发现,数据小,又连续,但是因为粘包现象都在data1上了。
解决方式一:让连续的数据变成不连续,不能这么做。
但是不能这么用。
解决方式二:限定服务端获取的字节数等于发送的字节数,不行,写死了。
我们发现这两种都不行。我们要采用数据大小的具体数值+循环接收的方式
循环接受怎么办,来看一下:
# 循环接收 import socket so1 = socket.socket() so1.bind(('127.0.0.1', 8888)) so1.listen(5) conn, addr = so1.accept() client_data1 = conn.recv(5) client_data2 = conn.recv(5) client_data3 = conn.recv(5) client_data4 = conn.recv(5) print(client_data1,client_data2,client_data3,client_data4) conn.send(client_data1) conn.close() so1.close()
import socket phone = socket.socket() phone.connect(('127.0.0.1', 8888)) phone.send(b'hellohellohellohellohello') # phone.send(b'world') print(phone.recv(1024).decode('utf-8')) phone.close()
还有一个问题, len测试的数据大小返回的是int,你一个int怎么发送?int怎么转成bytes类型。 int 你会说先转成str,str再转成bytes。你的脸得标准,你有一个固定长度。
如何将一个固定的数字转成bytes类型呢,下面的来介绍。
06 解决粘包
# total_size: len(ret+ret1) 不固定的数字类型。 1519 518 10000
#咱们要发送一个: 固定的头部(数据的总大小) + 数据
引入一个模块:struct
struct 将一个数据,int,转化成固定的bytes
来看下用法
import struct # 将一个数字转化成等长度的bytes类型。 ret = struct.pack('i', 183346) print(ret, type(ret), len(ret)) # 通过unpack反解回来 ret1 = struct.unpack('i',ret)[0] print(ret1, type(ret1), len(ret1)) # 但是通过struct 处理不能处理太大 ret = struct.pack('l', 4323241232132324) print(ret, type(ret), len(ret)) # 报错
struct相关
import struct # # 将不固定长度的数字 ---> 固定的bytes ret = struct.pack('i',151988) # ret = struct.pack('q',1519324324424324324324324) # 数据量大了就不可以 print(ret, type(ret), len(ret), sep='\n') # 反解 ret1 = struct.unpack('i', ret)[0] print(ret1) # header: 可以包含多种对象:文件名,文件的MD5 文件的大小,等等 # 所以将文件头部构造成字典比较合适。 # import json # head_dict = { # 'filename': '二狗.avi', # 'md5': '3243243254erew5rwef', # 'total_size': 1343243243243243243244324324322324, # } # # dict_json = json.dumps(head_dict) #字典类型的头部转成了bytes类型 # # print(dict_json) # # # header_total_size = len(dict_json) #总长度 # print(header_total_size) # # ret = struct.pack('i', header_total_size) # print(ret,len(ret)) # print(dict_dict) # 将字典转化成json格式,---> bytes-->发送。
low版:
import socket import subprocess import struct phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) phone.bind(('127.0.0.1', 8085)) phone.listen(5) conn, addr = phone.accept() while 1: try: cmd = conn.recv(1024) obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # 1,获取执行命令返回的结果 ret = obj.stdout.read() ret1 = obj.stderr.read() # 2,计算head(总数据)大小 total_size = len(ret+ret1) print(total_size) # 3,将head转换成固定长度的bytes head = struct.pack('i', total_size) # 4, 发送head conn.send(head) # 5,发送数据部分。 conn.send(ret) conn.send(ret1) except Exception: break conn.close() phone.close() # total_size: len(ret+ret1) 不固定的数字类型。 1519 518 10000 #咱们要发送一个: 固定的头部(数据的总大小) + 数据 # struct 将一个数据,int,转化成固定的bytes
import socket import struct phone = socket.socket() phone.connect(('127.0.0.1', 8085)) while 1: msg = input('>>>').strip() if msg.upper() == 'Q': break elif not msg: continue phone.send(msg.encode('utf-8')) # 1,接收报头。 header_bytes = phone.recv(4) # 2,反解报头。 total_size = struct.unpack('i', header_bytes)[0] # int # 3,接收数据部分。 data_size = 0 res = b'' while data_size < total_size: data = phone.recv(1024) #每次接收1024个数据 res = res + data #b'5000个内容' 每次加1024给res,最后加到5000 # print(res) #"b'\r\nWindows IP \xc5\xe4\xd6\xc3\r\n\r\n\r\n\xce\xde\xcf\xdf\xbe\xd6\xd3\xf2\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xb1\xbe\xb5\xd8\xc1\xac\xbd\xd3* 1:\r\n\r\n " # print(len(res)) data_size = data_size + len(data) #数据大小+最后的数据大小 print(res.decode('gbk')) phone.close()
upper版:
import socket import subprocess import struct import json phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) phone.bind(('127.0.0.1', 8080)) phone.listen(5) conn, addr = phone.accept() while 1: try: cmd = conn.recv(1024) obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # 1,获取执行命令返回的结果,计算结果的总大小 ret = obj.stdout.read() ret1 = obj.stderr.read() total_size = len(ret + ret1) # 2,构建header字典 head_dict = { 'filename': '二狗.avi', 'md5': '3243243254erew5rwef', 'total_size': total_size, } # 3,将字典转换json格式 dict_json = json.dumps(head_dict) # 4, 将字典转化成bytes dict_bytes = dict_json.encode('utf-8') # 5, 计算字典bytes类型的大小 dict_len = len(dict_bytes) # 6将bytes类型的 字典的长度转化成固定长度的bytes head_dict_len = struct.pack('i', dict_len) # 7,发送字典的总大小 conn.send(head_dict_len) # 8,发送 bytes类型的字典 conn.send(dict_bytes) # 9,发送数据部分。 conn.send(ret) conn.send(ret1) except Exception: break conn.close() phone.close() # total_size: len(ret+ret1) 不固定的数字类型。 1519 518 10000 #咱们要发送一个: 固定的头部(数据的总大小) + 数据 # struct 将一个数据,int,转化成固定的bytes
import socket import struct import json phone = socket.socket() phone.connect(('127.0.0.1', 8080)) while 1: msg = input('>>>').strip() if msg.upper() == 'Q': break elif not msg: continue phone.send(msg.encode('utf-8')) # 1,接收字典的大小。 dict_size = struct.unpack('i', phone.recv(4))[0] # 2,获取报头字典json格式。 header_dict_json = phone.recv(dict_size).decode('utf-8') # 3, 通过json 反解成 字典 header_dict = json.loads(header_dict_json) # 4,接收数据部分。 data_size = 0 res = b'' while data_size < header_dict['total_size']: data = phone.recv(1024) res = res + data data_size = data_size + len(data) print(res.decode('gbk')) phone.close()
作者:wangkaiok —— 小菜鸟111
出处:http://www.cnblogs.com/wangkaiok/
本文版权归作者和博客园共有,但未经作者同意禁止转载,转载必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。