网络编程:使计算机之间基于网络实现数据交互
交换机:只要是接入了交换机的计算机 彼此之间都是互联的
局域网:互联网其实都是由N多个局域网连接而成
基于以太网协议通信的特点
- 通信基本靠吼
- 广播
- 单播
根据IP地址获取mac地址

OSI协议:(补充)

路由器:连接局域网
域名解析:www.baidu.com >>> 14.215.177.39:80
应用层
- HTTP协议:超文本传输协议
- FTP协议
UDP协议
- 数据报协议
- 无需建立双向通道 数据传输是不安全的
- 将内存中的数据直接发送出去 不会做保留
- 例如早期的qq
TCP协议类似于打电话
UDP协议类似于发短信
今日内容
socket套接字
解决端口占用问题
from socket import SOL_SOCKET,SO_REUSEADDR sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
服务端
import socket
# 买手机
server = socket.socket() # 不传参数时,默认用的就是TCP协议
# 插电话卡 bind((host,port)) 绑定ip和port
server.bind(('127.0.0.1',8080))
# 开机 半连接池
server.listen(5)
# 接听电话 等着别人给你打电话
conn, addr = server.accept() # 阻塞,等待
# 听别人说话 接收1024个字节数据
data = conn.recv(1024) # 阻塞,等待
# 给别人回话
conn.send(b'hello baby~')
# 挂电话
conn.close()
# 关机
sercer.close()
客户端
import socket
client = socket.socket() # 拿电话
client.connect(('127.0.0.1',8080)) # 拨号,写的是对方的ip和port
client.send(b'hello world!') # 对别人说话
data = client.recv(1024) # 听别人说话,接收1024个字节数据
client.close() # 挂电话
总结:
127.0.0.1是本机回还地址,只能自己识别自己,不能访问其他
send和recv对应:
- 不要出现两边都是相同的情况
- recv是跟内存要数据,至于数据的来源,我们无需考虑
关于半连接池
半连接池的概念

图解:假如上述一共7个对象,首位已在被执行,有5个等待位对应5个对象,还有1个对象在等待进入等待位,server.listen(5)中的5就是5个等待位
通信循环下的服务端
import socket
server = socket.socket() # 生成一个对象
server.bind(('127.0.0.1',8080)) # 绑定ip和port
server.listen(5) # 半连接池,最大等待数目
while True: # 使服务端24小时不停的提供服务
conn, addr = server.accept() # 等到别人来 conn就是类似于双向通道
print(addr) # ('127.0.0.1',51323) 客户端的地址
while True:
try:
data = conn.recv(1024)
print(data) # b""是二进制数据,针对mac与linux客户端异常退出之后,服务端不会报错,只会一直接收b""
if len(data) == 0: break
conn.send(data.upper())
except ConnectionResetError as e:
printi(e)
break
conn.close()
通信循环下的客户端
import socket
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)
data = client.recv(1024)
print(data)
TCP粘包问题
TCP特点
- 会将数据量比较小的一次性发给对方
如何将数据打包成固定的数据包(运用struct模块)?
import struct
res = 'ssssssssssssssssssssssss'
# 当原始数据特别大时,i模式打包不了,需要更换其他模式
res1 = struct.pack('i',len(res))
print(len(res1)) # 打包后的数据长度为4
res2 = struct.unpack('i',res1)[0]
print(res2) # 解包之后的真实数据长度
如果遇到数据量特别大时,又该如何解决?
import struct
# 当原始数据特别大的时候 i模式打包不了 需要更换模式?
# 如果遇到数据量特别大的情况 该如何解决?
d = {
'name':'jason',
'file_size':34554354353453545245345324565465465654,
'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)
关于subprocess的应用
import subprocess
cmd = input('cmd>>>:')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
print(obj.stdout.read().decode('gbk')) # 正确命令返回的结果
print(obj.stderr.read().decode('gbk')) # 错误的命令返回的结果
# subprocess获取到的数据 拿完就没有了 不能重复的拿
print(obj.stdout.read().decode('gbk')) # 正确命令返回的结果
print(obj.stderr.read().decode('gbk')) # 错误的命令返回的结果
解决粘包问题的服务端
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('uft-8')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
res = obj.stdout.read() + obj.stderr.read()
json_d = json.dumps(d)
# 先制作一个字典的报头
header = struct.pack('i',len(json_d))
# 发送字典报头
conn.send(header)
# 发送字典
conn.send(json_d.encode('utf-8'))
# 再发真实数据
conn.send(res)
except ConnectionResetError:
break
conn.close()
解决粘包问题的客户端
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)
# 先接收字典报头
header_dict = client.recv(4)
# 解析报头 获取字典的长度
dict_size = struct.unpack('i',header_dict)[0] # 解包时一定要加上索引0
# 接收字典数据
dict_bytes = client.recv(dict_size)
dict_json = json.loads(dict_bytes.decode('utf-8'))
# 从字典中获取真实数据
recv_size = 0
real_data = b''
while recv_size < dict_json.get('file_size'):
data = client.recv(1024)
real_data += data
recv_size += len(data)
print(real_data.decode('gbk'))
解决粘包问题
服务端
- 1.先制作一个发送给客户端的字典
- 2.制作字典的报头
- 3.发送字典的报头
- 4.发送字典
- 5.再发真实数据
客户端
- 1.先接受字典的报头
- 2.解析拿到字典的数据长度
- 3.接受字典
- 4.从字典中获取真实数据的长度
- 5.接受真实数据
案例实现:写一个上传电影的功能
- 1.循环打印某一个文件夹下面的所有的文件
- 2.用户选取想要上传的文件
- 3.将用户选择的文件上传到服务端
- 4.服务端保存该文件