一、软件开发架构
c/s架构(client/server)
c:客户端
s:服务端
b/s架构(browser/server)
b:浏览器
s:服务器
ps:bs架构本质也是cs架构
bs架构未来有很大潜力
二、网络编程发展史 :任何先进的技术最早都来源于军事
想实现远程通信第一个需要具备的条件是:
第一,物理连接介质
第二,需要有一套公共的标准、协议
1、OSI协议
五层:(一般也称七层)
应用层(应用层,表示层,会话层)
传输层
网络层
数据链路层
物理连接层
1.物理连接层:基于电信号传输010101010二进制数据
2.数据链路层(以下两点合成为以太网协议)
1.规定的电信号的分组方式
2.规定了任何一台接入互联网的计算机都必须有一块网卡(每块网卡都刻有世界上独一无二的编号)
编号为12位16进制数:前6位是厂商编号,后6位是流水线编号(管这12数叫MAC地址)
基于以太网协议通信
通信基本靠吼
广播、单播、广播风暴
交换机:让连接交换机的计算机实现彼此之间的互联
局域网:构成互联网的基本单位
ps:以太网协议不能跨局域网传输
路由器:连接不同局域网
网关:网间连接器、协议转换器
3.网络层
IP协议
规定了只要是接入互联网的计算机都必须有一个IP地址
ip地址特点:点分十进制 且动态分配
ip地址最小:0.0.0.0 最大:255.255.255.255
版本:IPV4和IPV6(由于IPV4已经不够表示目前存在的计算机了,所以推出了IPV6)
4.传输层
TCP/UDP都是基于端口工作的协议:
端口(port):用来唯一标识一台计算机上的某个应用程序
计算机与计算机之间其实是计算机上的应用程序与应用程序之间的通信
端口号的范围:0~65535(端口号也是动态分配,关闭再次启动厚,端口号可能变)
注意:0~1024这些都是操作系统默认使用的端口
建议:使用8000之后的端口
Mysql默认端口:3306;Redis默认端口:6379;django默认端口:8000;flask默认端口:5000
so:ip+port是唯一标识接入互联网一台计算机上的某个应用程序
5.应用层
HTTP协议
FTP协议
ps:TCP协议(类似打电话):流式协议,可靠协议,三次握手,四次挥手
UDP协议(类似发短信):数据报协议,无需建立双向通道,数据传输是不安全,将内存中的数据直接发送出去,不会做保留
三、Socket(套接字)
TCP:
发送 send ;接收recv
1.解决地址绑定套接字问题:
from socket import SOL_SOCKET,SO_REUSEADDR
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
import socket server = socket.socket() # 买手机 不传参数默认用的就是TCP协议 server.bind(('127.0.0.1',8080)) # bind((host,port)) 插电话卡 绑定ip和端口 server.listen(5) # 开机 半连接池 conn, addr = server.accept() # 接听电话 等着别人给你打电话 阻塞 data = conn.recv(1024) # 听别人说话 接收1024个字节数据 阻塞 print(data) conn.send(b'hello baby~') # 给别人回话 conn.close() # 挂电话 server.close() # 关机
ps :127.0.0.1 本机回还地址
只能自己识别自己 其他人无法访问
import socket client = socket.socket() # 拿电话 client.connect(('127.0.0.1',8080)) # 拨号 写的是对方的ip和port client.send(b'hello world!') # 对别人说话 data = client.recv(1024) # 听别人说话 print(data) client.close() # 挂电话
注意点:
send和recv对应,不要出现两边都相同的情况
recv是跟内存要数据,至于数据的来源无需考虑
TCP特点:会将数据量比较小的并且时间间隔比较短的数据一次性打包发送给对方
UCP:(type=socket.SOCK_DGRAM)
发送sendto;接收recvfrom;无listen()
import socket server = socket.socket(type=socket.SOCK_DGRAM) # UDP协议 server.bind(('127.0.0.1',8080)) # UDP不需要设置半连接池 它也没有半连接池的概念 # 因为没有双向通道 不需要accept 直接就是通信循环 while True: data, addr = server.recvfrom(1024) print('数据:',data) # 客户端发来的消息 print('地址:',addr) # 客户端的地址 server.sendto(data.upper(),addr)
import socket client = socket.socket(type=socket.SOCK_DGRAM) # 不需要建立连接 直接进入通信循环 server_address = ('127.0.0.1',8080) while True: client.sendto(b'hello',server_address) data, addr = client.recvfrom(1024) print('服务端发来的数据',data) print('服务端的地址',addr)
特点:数据报协议(自带报头)
没有双向通道,通信类似于发短信
1.udp协议客户端允许发空
2.udp协议不会粘包
3.upd协议服务端不存在的情况下,客户端照样不会报错
4.udp协议支持并发
import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1',8080)) while True: data, addr = server.recvfrom(1024) print(data.decode('utf-8')) msg = input('>>>:') server.sendto(msg.encode('utf-8'),addr)
import socket client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) while True: msg = input('>>>:') msg = '来自客户端1的消息:%s'%msg client.sendto(msg.encode('utf-8'),server_address) data, server_addr = client.recvfrom(1024) print(data.decode('utf-8'))
import socket client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) while True: msg = input('>>>:') msg = '来自客户端2的消息:%s'%msg client.sendto(msg.encode('utf-8'),server_address) data, server_addr = client.recvfrom(1024) print(data.decode('utf-8'))
四、解决黏包问题:(tcp才会有)
先了解下struct模块
import struct # res = 'akdjsladlkjafkldjfgsdafhjksdfhfdgfdsgdfgssgdglkjdfsfdsadkfjlksdjf;klsdkl;fk;lsfdgklj;sfkldgj;kldfg;lfkd;lgk;lsdkfg;lkfd;glskljdklsajkldsa' # print('最原始的',len(res)) # 当原始数据特别大的时候 i模式打包不了 需要更换模式? # 如果遇到数据量特别大的情况 该如何解决? d = { 'name':'jason', 'file_size':3455435435345354524534532456546546565466564366463654543453454353455, 'info':'为大家的骄傲' } import json json_d = json.dumps(d) print(len(json_d)) res1 = struct.pack('i',len(json_d)) print(len(res1)) res2 = struct.unpack('i',res1)[0] print('解包之后的',res2)
服务端
1.先制作一个发送给客户端的字典
2.制作字典的报头
3.发送字典的报头
4.发送字典
5.再发真实数据
import socket import subprocess import struct import json server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn, addr = server.accept() while True: try: cmd = conn.recv(1024) if len(cmd) == 0:break cmd = cmd.decode('utf-8') obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) res = obj.stdout.read() + obj.stderr.read() d = {'name':'jason','file_size':len(res),'info':'asdhjkshasdad'} json_d = json.dumps(d) # 1.先制作一个字典的报头 header = struct.pack('i',len(json_d)) # 2.发送字典报头 conn.send(header) # 3.发送字典 conn.send(json_d.encode('utf-8')) # 4.再发真实数据 conn.send(res) # conn.send(obj.stdout.read()) # conn.send(obj.stderr.read()) except ConnectionResetError: break conn.close()
客户端
1.先接受字典的报头
2.解析拿到字典的数据长度
3.接受字典
4.从字典中获取真实数据的长度
5.接受真实数据
import socket import struct import json client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0:continue client.send(msg) # 1.先接受字典报头 header_dict = client.recv(4) # 2.解析报头 获取字典的长度 dict_size = struct.unpack('i',header_dict)[0] # 解包的时候一定要加上索引0 # 3.接收字典数据 dict_bytes = client.recv(dict_size) dict_json = json.loads(dict_bytes.decode('utf-8')) # 4.从字典中获取信息 print(dict_json) recv_size = 0 real_data = b'' while recv_size < dict_json.get('file_size'): # real_size = 102400 data = client.recv(1024) real_data += data recv_size += len(data) print(real_data.decode('gbk'))
五、socketserver模块(可实现并发)
tcp:
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): # print('来啦 老弟') while True: data = self.request.recv(1024) print(self.client_address) # 客户端地址 print(data.decode('utf-8')) self.request.send(data.upper()) if __name__ == '__main__': """只要有客户端连接 会自动交给自定义类中的handle方法去处理""" server = socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) # 创建一个基于TCP的对象 server.serve_forever() # 启动该服务对象
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: client.send(b'hello') data = client.recv(1024) print(data.decode('utf-8'))
udp:
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): # print('来啦 老弟') while True: data,sock = self.request print(self.client_address) # 客户端地址 print(data.decode('utf-8')) sock.sendto(data.upper(),self.client_address) if __name__ == '__main__': """只要有客户端连接 会自动交给自定义类中的handle方法去处理""" server = socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyServer) # 创建一个基于TCP的对象 server.serve_forever() # 启动该服务对象
import socket import time client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) while True: client.sendto(b'hello',server_address) data,addr = client.recvfrom(1024) print(data.decode('utf-8'),addr) time.sleep(1) # 添加时间,否则udp响应太快,看不到效果