Python学习之路--网络编程
Posted rssblogs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习之路--网络编程相关的知识,希望对你有一定的参考价值。
由于不同机器上的程序要通信,才产生了网络
C/S Client/Server 客户端/服务端
服务端 一直运行 等待服务别人
客户端 寻求服务的时候 才请求服务
B/S Browser/Server 浏览器/服务器
b/s架构是c/s架构的一种
实现通信上有全球唯一的MAC地址
网卡和网线
网卡
通过ip地址就能找到对应的MAC地址 ARP协议
交换机 ---- 多台机器之间的通信问题
广播风暴
网关 局域网中的机器想要访问局域网外的机器,需要通过网关访问
IP地址 和 子网掩码 按位与 得到网段地址
端口 找到的程序
在计算机上 每一个需要网络通信的程序 都会开一个端口
在同一实际只会有一个程序占用一个端口
不可能在同一时间 在同一个计算机有两个端口占用同一个端口
端口的范围: 0--65535
127.0.0.1 本地的回环地址
ip地址 确定唯一一台机器
端口 确定唯一的一个程序
ip+端口 找到唯一的一台机器上的唯一的一个程序
基于tcp协议的socket
import socket sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #避免服务重启的时候,报address already is in use sk.bind((‘127.0.0.1‘, 8080)) sk.listen() # 监听 conn, addr = sk.accept() # 接收到客户端 连接 地址 print(addr) ret = conn.recv(1024) # 听 print(ret) conn.send(b‘hi‘) # 发送信息 必须传一个bytes类型 conn.close() # 关闭连接 sk.close() # 关闭
import socket sk = socket.socket() sk.connect((‘127.0.0.1‘, 8080)) #拨号 sk.send(b‘hello‘) # 发送 ret =sk.recv(1024) print(ret) sk.close()
tcp中的长连接
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,8080)) sk.listen() while True: conn,addr = sk.accept() while True: msg = conn.recv(1024).decode(‘utf-8‘) if msg == ‘bye‘: break print(msg) info = input(‘>>>‘) if info == ‘bye‘: conn.send(b‘bye‘) break conn.send(info.encode(‘utf-8‘)) conn.close() sk.close()
import socket sk = socket.socket() sk.connect((‘127.0.0.1‘,8080)) while True: msg = input(‘>>>>>‘) if msg == ‘bye‘: sk.send(b‘bye‘) break sk.send(msg.encode(‘utf-8‘)) ret = sk.recv(1024).decode(‘utf-8‘) if ret == ‘bye‘: break print(ret) sk.close()
import socket sk = socket.socket() sk.connect((‘127.0.0.1‘,8080)) while True: msg = input(‘client2:>>>>>‘) if msg == ‘bye‘: sk.send(b‘bye‘) break sk.send((‘client2:‘+msg).encode(‘utf-8‘)) ret = sk.recv(1024).decode(‘utf-8‘) if ret == ‘bye‘: break print(ret) sk.close()
UDP
udp的server 不需要进行监听也不需要建立连接
在启动服务之后只能被动得等待客户端发送消息过来
客户端发送消息的同时还会自带地址消息
消息回复的时候不仅需要发送消息,还需要把对方的地址填写上
单个服务端
import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind((‘127.0.0.1‘,8080)) msg,addr = sk.recvfrom(1024) print(msg.decode(‘utf-8‘)) sk.sendto(b‘bye‘,addr) sk.close()
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = (‘127.0.0.1‘,8080) sk.sendto(b‘hello‘,ip_port) ret,addr = sk.recvfrom(1024) print(ret.decode(‘utf-8‘)) sk.close()
多个客户端
import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind((‘127.0.0.1‘,8080)) while True: msg,addr = sk.recvfrom(1024) print(addr) print(msg.decode(‘utf-8‘)) info = input(‘>>‘).encode(‘utf-8‘) sk.sendto(info,addr) sk.close()
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = (‘127.0.0.1‘,8080) while True: info = input(‘二哥:‘) info = (‘\033[32m来自二哥的消息:%s\033[0m‘%info).encode(‘utf-8‘) sk.sendto(info,ip_port) msg,addr = sk.recvfrom(1024) print(msg.decode(‘utf-8‘)) sk.close()
import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = (‘127.0.0.1‘,8080) while True: info = input(‘tiger:‘) info = (‘\033[34m来自tiger的消息:%s\033[0m‘ % info).encode(‘utf-8‘) sk.sendto(info, ip_port) msg,addr = sk.recvfrom(1024) print(msg.decode(‘utf-8‘)) sk.close()
黏包
同时接受的一条消息没有接收完,下一次执行的时候又多出来;
tcp 会出现黏包现象 不丢包
一,不知道长度
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,8090)) sk.listen() conn,addr = sk.accept() ret = conn.recv(2) ret2 = conn.recv(10) print(ret) print(ret2) conn.close() sk.close() # b‘he‘ # b‘llo‘
import socket sk = socket.socket() sk.connect((‘127.0.0.1‘,8090)) sk.send(b‘hello‘) sk.close()
二、优化算法
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,8090)) sk.listen() conn,addr = sk.accept() ret = conn.recv(12) print(ret) conn.close() sk.close() # b‘helloegg‘
import socket sk = socket.socket() sk.connect((‘127.0.0.1‘,8090)) sk.send(b‘hello‘) sk.send(b‘egg‘) sk.close()
多个send小的数据黏在一起 会发生黏包现象,tcp协议内部的优化算法造成的
# 基于tcp实现远程执行命令 # 在server端下发命令 import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,8090)) sk.listen() conn, addr = sk.accept() while True: cmd = input(‘>>>‘) conn.send(cmd.encode(‘utf-8‘)) ret = conn.recv(1024).decode(‘utf-8‘) print(ret) conn.close() sk.close()
# 在client端接收消息并执行 import socket import subprocess sk = socket.socket() sk.connect((‘127.0.0.1‘,8090)) while True: cmd = sk.recv(1024).decode(‘gbk‘) ret = subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out = ‘stdout:‘+(ret.stdout.read()).decode(‘gbk‘) std_err = ‘stderr‘+(ret.stderr.read()).decode(‘gbk‘) print(std_out) print(std_err) sk.send(std_out.encode(‘utf-8‘)) sk.send(std_err.encode(‘utf-8‘)) sk.close()
udp 不会出现黏包现象 丢包
import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind((‘127.0.0.1‘,8090)) msg,addr = sk.recvfrom(1024) while True: cmd = input(‘>>>‘) if cmd == ‘q‘: break sk.sendto(cmd.encode(‘utf-8‘),addr) msg,addr = sk.recvfrom(1024) print(msg.decode(‘utf-8‘)) sk.close()
import socket import subprocess sk = socket.socket(type=socket.SOCK_DGRAM) addr = (‘127.0.0.1‘,8090) sk.sendto(‘吃了吗‘.encode(‘utf-8‘),addr) while True: cmd, addr = sk.recvfrom(1024) ret = subprocess.Popen(cmd.decode(‘gbk‘),shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out = ‘stdout:‘+(ret.stdout.read()).decode(‘gbk‘) std_err = ‘stderr‘+(ret.stderr.read()).decode(‘gbk‘) print(std_out) print(std_err) sk.sendto(std_out.encode(‘utf-8‘),addr) sk.sendto(std_err.encode(‘utf-8‘),addr) sk.close()
黏包问题解决方法一
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,8080)) sk.listen() conn,addr = sk.accept() while True: cmd = input(‘>>>>>‘) if cmd == ‘q‘: conn.send(‘q‘) break conn.send(cmd.encode(‘gbk‘)) num = conn.recv(1024).decode(‘utf-8‘) conn.send(b‘ok‘) res = conn.recv(int(num)).decode(‘gbk‘) print(res) conn.close() sk.close()
import socket import subprocess sk = socket.socket() sk.connect((‘127.0.0.1‘,8080)) while True: cmd = sk.recv(1024).decode(‘gbk‘) if cmd == ‘q‘: break res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) std_out = res.stdout.read() std_err = res.stderr.read() sk.send(str(len(std_out)+len(std_err)).encode(‘utf-8‘)) sk.recv(1024) sk.send(std_out) sk.send(std_err) sk.close()
send 和 sendto 在超过一定范围的时候会报错
当要发送大数据的时候,要明确告诉接收方要发送多大的数据,一遍接收方能够准确的接收到所有数据
多用于文件传输过程中
黏包问题解决方法二
struck模块
该模块能够把一个了下转换成固定长度的bytes
import struct ret = struct.pack(‘i‘,4096) #‘i‘代表int,就是即将要把一个数字转换成固定长度的bytes类型 print(ret) num = struct.unpack(‘i‘,ret) print(num[0]) # b‘\x00\x10\x00\x00‘ # 4096
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,8080)) sk.listen() conn,addr = sk.accept() while True: cmd = input(‘>>>>>‘) if cmd == ‘q‘: conn.send(‘q‘) break conn.send(cmd.encode(‘gbk‘)) num = conn.recv(4) num = struct.unpack(‘i‘,num)[0] res = conn.recv(int(num)).decode(‘gbk‘) print(res) conn.close() sk.close()
import struct import socket import subprocess sk = socket.socket() sk.connect((‘127.0.0.1‘,8080)) while True: cmd = sk.recv(1024).decode(‘gbk‘) if cmd == ‘q‘: break res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) std_out = res.stdout.read() std_err = res.stderr.read() len_num = len(std_out)+len(std_err) nub_byt = struct.pack(‘i‘,len_num) sk.send(nub_byt) sk.send(std_out) sk.send(std_err) sk.close()
struck 模块定制报头理论
网络上传输的所有数据 都叫数据包
数据包的里的所有数据 都叫报文
报文里不止有数据 ip地址 端口号 mac地址
所有的报文都有报头
协议 报头 接收多少个字节
字节定制报文 在复杂的应用上会用到
传输文件的时候
文件的上传下载
# 实现文件的上传下载 import socket import struct import json sk = socket.socket() sk.bind((‘127.0.0.1‘,8090)) sk.listen() buffer = 4096 conn,addr = sk.accept() head_len = conn.recv(4) head_len = struct.unpack(‘i‘,head_len)[0] json_head = conn.recv(head_len).decode(‘utf-8‘) head = json.loads(json_head) filesize = head[‘filesize‘] with open(head[‘filename‘],‘wb‘) as f: while filesize: print(filesize) if filesize >= buffer: content = conn.recv(buffer) f.write(content) filesize -= buffer else: content =conn.recv(filesize) f.write(content) break conn.close() sk.close()
import socket import os import json import struct sk = socket.socket() sk.connect((‘127.0.0.1‘,8090)) buffer = 4096 head = ‘filepath‘:r‘C:\Users\kehu\Desktop‘, ‘filename‘:r‘162210409211 冉思思财务报表分析.docx‘, ‘filesize‘:None file_path = os.path.join(head[‘filepath‘],head[‘filename‘]) filesize = os.path.getsize(file_path) head[‘filesize‘] = filesize json_head = json.dumps(head) # 字典转成了字符串 bytes_head = json_head.encode(‘utf-8‘) # 字符串转bytes print(json_head) print(bytes_head) head_len = len(bytes_head) pack_len = struct.pack(‘i‘,head_len) sk.send(pack_len) # 先发送报头长度 sk.send(bytes_head) # 再发bytes类型的报头 with open(file_path,‘rb‘) as f: while filesize: print(filesize) if filesize >= buffer: content = f.read(buffer) sk.send(content) filesize -= buffer else: content = f.read(filesize) sk.send(content) break sk.close()
以上是关于Python学习之路--网络编程的主要内容,如果未能解决你的问题,请参考以下文章