python网络编程
1、 互联网协议
功能:定义计算机如何接入Internet,以及接入Internet的计算机通信的标准。
OSI七层协议如图:
物理层
主要时基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0
数据链路层
定义了电信号的分组方式, 使用统一的标准以太网协议
以太网协议(ethernet):
ehernet规定:
1. 一组电信号构成一个数据包,叫做帧
2. 每一组数据帧分成:报头head和data两部分

源地址和目标地址:指的是网卡的地址,即mac地址,ethernet规定接入internet的设备都必须具备网卡
通信方式:广播
网络层
功能:引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址
IP协议:ipv4范围0.0.0.0~255.255.255.255
ip地址分成两部分:
网络部分:标识子网
主机部分:标识主机
注:单纯的ip地址段只是标识ip地址的种类,无法辨识ip所处的子网
子网掩码:
表示子网络特征的一个参数,它的网络部分全是1,主机部分全是0
如何判断两个IP在同一子网中:
方法是将两个IP地址与子网掩码分别进行AND运算,如果结果相同,则表示它们再统一子网络中。
IP数据包分为head和打他部分,直接放入以太网包的data部分
ARP协议
功能:广播的方式发送数据包,获取莫表主机的mac地址
工作方式:已知每台主机的IP地址
1. 通过IP地址和子网掩码分出是否处于相同的子网
2. 如果不再同一子网,发数据到网关(里面的目标地址是网关的ip),通过arp获取的是网关的mac地址,
然后发数据发给网关(里面的目标地址是目标ip)
3. 如果在同一子网中,所有主机接收后会拆开包,发现目标ip是自己的,就响应,返回自己的mac地址
(交换机内有个mac地址表,先查表,有就直接发过去,没有就群发)
传输层
功能:建立端口到端口的通信,端口的范围0-65535,0-1023为系统占用端口
tcp协议:
可靠传输;TCP数据包没有长度限制;理论上可以无限长,但是为了保证网络的效率,
通常TCP数据包的长度不会超过IP数据包的长度,确保单个TCP数据包不会再被分割
udp协议:
不可靠传输,head部分只有8个字节,总长不超过65535字节,正好放进一个ip数据包
TCP的三次握手四次挥手

为什么tcp是可靠传输?
因为每次传输数据包时,都会有一个返回一个收到信号,如果没收到信号,则重发。
为什么是四次挥手?
这里有数据传输的问题,如果单方面直接断开连接,就可能造成数据丢失
2.、socket
套接字,ip+port, 位于传输层和应用层之间的软件抽象层,就是一组接口,封装了TCP/IP协议。
# 服务端 import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print(server) server.bind(('127.0.0.1', 8080)) server.listen(5) print("start.....") conn, client_addr = server.accept() # conn代表双向链接,用来收发消息 print(conn, client_addr) data = conn.recv(1024) #1024接收的最大字节数bytes print("客户端数据",data) conn.send(data.title()) conn.close() server.close() # 客户端 import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080)) client.send("hi man.".encode("UTF-8")) data = client.recv(1024) print(data.decode("UTF-8")) client.close()
# 服务端 import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('127.0.0.1', 8080)) server.listen(5) # 限制的是请求数,超过时,客户端抛异常 print("start.....") conn, client_addr = server.accept() # 加上通讯循环 while True: data = conn.recv(1024) print("客户端数据",data) conn.send(data.title()) conn.close() server.close() # 客户端 import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080)) # 加上通讯循环 while True: msg = input(">>: ").strip() client.send(msg.encode("UTF-8")) data = client.recv(1024) print(data.decode("UTF-8")) client.close()
# 解决一些小bug # 端口占用问题 # 服务端不能收到空字符,但客户端发空,造成了双方都在相互等待 # 客户端不正常关闭,造成服务端也终止了 # 服务端 import socket server= socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)#修复端口占用 server.bind(('127.0.0.1', 8080)) server.listen(5) print("start.....") #链路循环 while True: conn, client_addr =server.accept() print(client_addr) # 通讯循环 while True: try: data = conn.recv(1024) #适用于linux,或window下有代码错误造成的关闭 if not data: break print("客户端数据",data) conn.send(data.title()) #适用于window系统非正常关闭 except ConnectionResetError as e: break conn.close() server.close() # 客户端 import socket client= socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080)) while True: msg = input(">>: ").strip() if not msg: continue phone.send(msg.encode("UTF-8")) data = phone.recv(1024) print(data.decode("UTF-8")) client.close()
# 服务端 import socket import subprocess server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #修复端口占用 server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('127.0.0.1', 8080)) server.listen(5) print("start.....") while True:#链路循环 conn, client_addr = server.accept() print(client_addr) while True: try: cmd = conn.recv(1024) if not cmd: break#适用于linux obj = subprocess.Popen(cmd.decode("UTF-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() conn.send(stdout+stderr)#“+”可改进 # 改进 # conn.send(stdout) # conn.send(stderr) except ConnectionResetError as e:#适用于window系统 break conn.close() server.close() # 客户端 import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080)) while True: cmd = input(">>: ").strip() if not cmd: continue client.send(cmd.encode("UTF-8")) data = client.recv(1024) print(data.decode("GBK")) client.close()
3、粘包问题
什么时粘包问题
主要还是因为接收方不知道消息的界限,不知道一次可以提取多少字节数据造成的
什么原因造成的粘包现象
TCP传输时,使用了Nagle优化算法,会把多次间隔较小且数据量小的数据,合成一个大的数据包发送出去。
什么情况下发生粘包
1. 发送端会把多个时间间隔短,数据量小的数据放到一起发送到接收端
2. 接收端不及时接收缓冲区的包,造成多个包放到一起,下次接收时,取到了上次遗留下的数据。
如何解决粘包问题
1. 第一步,使用自定义报头,把数据的长度还有其它的一些信息,放到报头里,
2. 然后就用到了struct模块,这个模块可以把数据长度变成固定长度的字符,比如"i"模式是4个字符,
3. 先接收报头长度的固定字符,得到报头长度,从报头里得到数据的长度,然后再接收数据
# 服务端 import socket import subprocess import struct import json server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)#修复端口占用 server.bind(('127.0.0.1', 8080)) server.listen(5) print("start.....") while True:#链路循环 conn, client_addr = server.accept() while True: try: cmd = conn.recv(1024) if not cmd: break#适用于linux obj = subprocess.Popen(cmd.decode("UTF-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() #制作报头 header_dic = { "filename": "a.txt", "md5": "xxxxx", "total_size": len(stdout) + len(stderr) } header_str = json.dumps(header_dic) header_bytes = header_str.encode("UTF-8") #先发送报头长度 conn.send(struct.pack("i",len(header_bytes))) #发报头 conn.send(header_bytes) #发数据 conn.send(stdout)#“+”可改进 conn.send(stderr) except ConnectionResetError as e:#适用于window系统 break conn.close() server.close()
import socket import struct import json client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080)) while True: cmd = input(">>: ").strip() if not cmd: continue client.send(cmd.encode("UTF-8")) #先收报头长度 header_size = struct.unpack("i", client.recv(4))[0] #收报头,解析数据的长度 header_bytes = client.recv(header_size) header_str = header_bytes.decode("UTF-8") header_dic = json.loads(header_str) print(header_dic) total_size = header_dic["total_size"] #接受数据 recv_size = 0 recv_data = '' while recv_size < total_size: if total_size-recv_size < 1024: res = client.recv(total_size-recv_size) else: res = client.recv(1024) # 不能直接接收1024个, # 原因:如果在接收文件时,服务端又发送了数据容易造成粘包问题 # res = client.recv(1024) recv_data += res recv_size += len(res) print(recv_data.decode("GBK")) client.close()
4、UDP传输
# 服务端 from socket import * server = socket(AF_INET, SOCK_DGRAM) server.bind(("127.0.0.1", 8080)) print("start......") while True: data, client_addr = server.recvfrom(1024) server.sendto(data.upper(), client_addr) server.close() # 客户端 from socket import * client = socket(AF_INET, SOCK_DGRAM) while True: msg = input(">>: ").strip() client.sendto(msg.encode("UTF-8"),("127.0.0.1", 8080)) data = client.recvfrom(1024)[0] print(data.decode("UTF-8")) # 特点: # 1. 可以先启动客户端,直接发数据,只不过数据发送出去就丢失了 # 2. 可以发空消息 # 3. 不会发生粘包问题
5、 socketserver模块
# 服务端 import socketserver class MyTCPServer(socketserver.BaseRequestHandler): def handle(self): print("from %s"%self.client_address) while True: #通讯循环 try: data = self.request.recv(1024) if not data: break print("client's message: %s"%data) self.request.send(data.upper()) except ConnectionResetError: break if __name__ == '__main__': server = socketserver.ThreadingTCPServer(("127.0.0.1", 8080), MyTCPServer) server.serve_forever()
# 服务端 import socketserver class MyUdphandler(socketserver.BaseRequestHandler): def handle(self): data,sock=self.request sock.sendto(data.upper(),self.client_address) if __name__ == '__main__': server=socketserver.ThreadingUDPServer(('127.0.0.1',8081),MyUdphandler) server.serve_forever() # 客户端 from socket import * client=socket(AF_INET,SOCK_DGRAM) while True: client.sendto(b'hello',('127.0.0.1',8081)) data,server_addr=client.recvfrom(1024) print(data)

浙公网安备 33010602011771号