python-网络编程

第十章 网络编程

1. 网络是什么?

  • 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统网络管理软件网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

  • 功能:信息的传输与共享和完成一些简单的程序。

  • 类别:网络操作系统。

  • 发展:早期 联机 ----> 以太网 局域网与交换机

2. 广播

  • 主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),由于其不用路径选择,所以其网络成本可以很低廉。在数据网络中也允许广播的存在,但其被限制在二层交换机的局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。

3. ip地址与ip协议

  • 规定网络地址的协议叫ip协议,它定义的地址称之为ip地址,广泛采用的v4版本即ipv4,它规定网络地址由32位2进制表示

  • 范围0.0.0.0-255.255.255.255

  • 变化的 : ip地址 能够更好的更方便的找到你的机器

  • 一个ip地址通常写成四段点分十进制数,例:172.16.10.1

  • 公网地址 :需要我们自己申请购买的地址

  • 内网地址 :保留字段

192.168.0.0 - 192.168.255.255  学校
172.16.0.0 - 172.31.255.255    学校
10.0.0.0 - 10.255.255.255      公司
  • 特殊的ip地址

    • 127.0.0.1 本地回环地址 测试的时候用的

  • 查看自己的ip地址 ipconfig【在windows系统里cmd】/ifconfig【在linux上】

  • 子网掩码也是一个ip地址 用来判断两台机器在不在一个局域网内

比如,已知IP地址172.16.10.1和172.16.10.2的子网掩码都是255.255.255.0,请问它们是否在同一个子网络?两者与子网掩码分别进行AND运算,

172.16.10.1:10101100.00010000.00001010.000000001
255255.255.255.0:11111111.11111111.11111111.00000000
AND运算得网络地址结果:10101100.00010000.00001010.00000000->172.16.10.0



172.16.10.2:10101100.00010000.00001010.00000010
255255.255.255.0:11111111.11111111.11111111.00000000
AND运算得网络地址结果:10101100.00010000.00001010.00000000->172.16.10.0
结果都是172.16.10.0,因此它们在同一个子网络。
  • IPV6协议:现在较少用,能表示的ip地址更多
    范围:0:0:0:0:0:0 - FFFFFF:FFFFFF:FFFFFF:FFFFFF:FFFFFF:FFFFFF  

4. mac地址

  • ethernet规定接入internet的设备都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址。

  • 每块网卡出厂时都被烧制上一个世界唯一的mac地址,长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)

  • 不变的 : mac地址 能够唯一标识你这台机器的【vnc/飞秋 都不涉及mac】

  • arp协议 ——查询IP地址和MAC地址的对应关系

    • 通过ip找mac

    • 地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址。

5. 局域网

  • 局域网的概念

    • 局域网(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。

    • 交换机:在同一个局域网内的机器由交换机负责通信。

      • 只能解析机器的mac地址,不能解析ip地址

      • 可以完成广播,组播,单播(单播--> mac地址(在网卡上))

  • 局域网之间通信

    • 路由器 :是连接因特网中各局域网、广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,按前后顺序发送信号。路由器具有判断网络地址和选择IP路径的功能,它能在多网络互联环境中,建立灵活的连接,可用完全不同的数据分组和介质访问方法连接各种子网,路由器只接受源站或其他路由器的信息,属网络层的一种互联设备。

      • 可以解析ip地址

      • 提供网关ip,同一个局域网的所有机器共享一个网关

      • 我们不能访问除了本局域网之外的其他内网的IP地址

    • 端口(port):用来确认一台机器上的具体应用程序(0~65535)

6. 网络开发架构

  • C/S架构:需要安装才能使用

    • client 客户端 我们用的 需要安装的

    • server 服务端

# server 服务端

import socket


sk = socket.socket()    # 创建一个server端的对象
sk.bind(('127.0.0.1', 801))   # 给server端绑定一个地址 以元组的形式,('ip地址',端口)
sk.listen()  # 开始监听(可以接收)客户端给我的连接了

conn,addr = sk.accept()   # 建立连接 conn是连接
while 1:
   mes = input('>>>')
   conn.send(mes.encode('utf-8'))
   msg = conn.recv(1024).decode('utf-8')
   print(msg)

conn.close()   # 关闭连接

sk.close()
# client 客户端

import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 801))

while 1:
   mse = input('>>>')
   sk.send(mse.encode('utf-8'))
   msg = sk.recv(1024).decode('utf-8')
   print(msg)


sk.close()
  • B/S架构 : 百度 博客园 谷歌 码云

    • browser 浏览器

    • server 服务端

 

  • b/s和c/s什么关系?

    • B/S架构也是C/S架构中的一种

 

C/S架构的好处
   可以离线使用/功能更完善/安全性更高
B/S架构
   不用安装就可以使用
   统一PC端用户的入口

7. osi5层协议

应用层     python   【包括:表示层  会话层】       # 5 层
传输层     port  udp  tcp   四层路由器 四层交换机  # 4
网络层     ip协议(ipv4 ipv6)     路由器 三层交换机        # 3
数据链路层 mac arp协议   网卡  二层交换机          # 2
物理层                                         # 1  

8 tcp和udp

  • tcp(语音聊天/视频聊天) - 线下缓存高清电影\qq远程控制\发邮件

    • 需要先建立连接 然后才能通信的

    • 占用连接\可靠(消息不会丢失)\实时性高\速度慢

# 建立连接 - 三次握手
客户端向服务器端发送syn请求,
服务端向客户端回复ack并发送syn请求,
客户端接收到请求之后再回复ack表示建立连接
由客户端的connect + 服务端的accept

# 断开连接 - 四次挥手
客户端向服务端发送fin请求,
服务端回复ack确认
服务端向客户端发送fin请求,
客户端回复ack确认
有客户端的close和服务端的close

 

  • 最简单的tcp网络通信

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9001))   # 申请操作系统的资源
sk.listen()
print('sk : ',sk)
conn,addr = sk.accept() # conn里存储的是一个客户端和server端的连接信息
print('conn : ',conn)
conn.send(b'hello')
msg = conn.recv(1024)
print(msg)
conn.close()    # 挥手 断开连接

sk.close()      # 归还申请的操作系统的资源
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9001))



msg = sk.recv(1024)
print(msg)
sk.send(b'byebye')

sk.close()

tcp协议

  1. 在连接内多和客户说几句-代码

  2. 加whlie True 为了和多个客户端进行握手

  3. sk代表申请的系统的资源

  4. conn里存储的是一个客户端和server端的连接信息

tcp协议的多人多次通信
   # 和一个人通信说多句话
   # 和一个人聊完再和其他人聊
   # socket() tcp协议的server
   # bind 绑定一个ip和端口
   # listen 监听,代表socket服务的开启
   # accept 等,到有客户端来访问和客户端建立连接
   # send 直接通过连接发送消息,不需要写地址
   # recv 只接收消息
   # connect 客户端/tcp协议的方法,和server端建立连接
   # close 关闭服务/连接
**server**(服务端)

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9001))   # 申请操作系统的资源
sk.listen()  # 监听 代表socket的开启

while True:  # 为了和多个客户端进行握手
   conn,addr = sk.accept() # 能够和多个客户端进行握手了
   print('conn : ',conn)
   while True:   # 能够接受多个客户的请求
       send_msg = input('>>>')
       conn.send(send_msg.encode('utf-8'))
       if send_msg.upper() == 'Q':   # 服务端(server)断开和客户端(client)的连接
           break
       msg = conn.recv(1024).decode('utf-8')
       if msg.upper() == 'Q': break  # 客户端断开和服务端的连接
       print(msg)
   conn.close()    # 挥手 断开连接

sk.close()      # 归还申请的操作系统的资源

# str -encode('utf-8')-> bytes
# str -encode('gbk')-> bytes
# '你' 字符
# utf-8 b'181921'     b'181921' -decode('utf-8')-> 你
# gbk   b'17210'       b'17210' -decode('gbk') -> 你

# 基于tcp协议的文件传输
**client**(客户端)

import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9001))


while True:
   msg = sk.recv(1024)
   msg2 = msg.decode('utf-8')
   if msg2.upper() == 'Q':break
   print(msg,msg2)
   send_msg = input('>>>')
   sk.send(send_msg.encode('utf-8'))
   if send_msg.upper() == 'Q':
           break
sk.close()

tcp协议的粘包现象

什么是粘包?

两条或更多条分开发送的信息连在一起就是粘包现象

粘包现象 只出现在tcp协议中,因为tcp协议 多条消息之间没有边界,并且还有一大堆优化算法 发送端 : 两条消息都很短,发送的间隔时间也非常短

发生在发送端 : 发送间隔短,数据小,由于优化机制就合并在一起发送了

 

接收端 : 多条消息由于没有及时接收,而在接收方的缓存短堆在一起导致的粘包

发生在接收端 : 接收不及时,所以数据就在接收方的缓存端黏在一起了

 

粘包发生的本质 : tcp协议的传输是流式传输 数据与数据之间没有边界

解决粘包问题的本质 :设置边界,可以借助struct模块

# 先发送四字节的数据长度       # 先接受4字节 知道数据的长度
# 再按照长度发送数据           # 再按照长度接收数据
tcp协议的自定义协议解决粘包问题 主要掌握逻辑和代码
   # 1 recv(1024)不代表一定收到1024个字节,而是最多只能收这么多
   # 2 两条连续发送的数据一定要避免粘包问题
   # 3 先发送数据的长度 再发送数据
   #   发送的数据相关的内容组成json:先发json的长度,再发json,json中存了接下来要发送的数据长度,再发数据
# 基于tcp上传(大)文件
# server端

import json
import struct
import socket
# 接收
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

conn,_ =sk.accept()
msg_len = conn.recv(4)
dic_len = struct.unpack('i',msg_len)[0]
msg = conn.recv(dic_len).decode('utf-8')
msg = json.loads(msg)

with open(msg['filename'],'wb') as f:
   while msg['filesize'] > 0:
       content = conn.recv(1024)
       msg['filesize'] -= len(content)
       f.write(content)
conn.close()
sk.close()
# client端

import os
import json
import struct
import socket
# 发送
sk = socket.socket()
# sk.connect(('192.168.14.109',9012))
sk.connect(('127.0.0.1',9001))

# 文件名\文件大小
abs_path = r'D:\python22期\day28 课上视频\3.网络基础概念.mp4'
filename = os.path.basename(abs_path)
filesize = os.path.getsize(abs_path)
dic = {'filename':filename,'filesize':filesize}
str_dic = json.dumps(dic)
b_dic = str_dic.encode('utf-8')
mlen = struct.pack('i',len(b_dic))
sk.send(mlen)   # 4个字节 表示字典转成字节之后的长度
sk.send(b_dic)  # 具体的字典数据

with open(abs_path,mode = 'rb') as f:
   while filesize>0:
       content = f.read(1024)
       filesize -= len(content)
       sk.send(content)
sk.close()

 

struct模块:该模块可以把一个类型,如数字,转成固定长度的bytes【socket 底层模块】

>>> struct.pack('i',1111111111111)

struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围


   
   
import struct

num1 = 129469649
num2 = 123
num3 = 8

ret1 = struct.pack('i',num1)
print(len(ret1))
ret2 = struct.pack('i',num2)
print(len(ret2))
ret3 = struct.pack('i',num3)
print(len(ret3))

print(struct.unpack('i',ret1)[0])
print(struct.unpack('i', ret2))
print(struct.unpack('i', ret3))
借助sturct可以完成自定义协议
import struct
import socket

sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

conn,addr = sk.accept()
msg1 = input('>>>').encode()
msg2 = input('>>>').encode()
# num = str(len(msg1)) # '10001'
# ret = num.zfill(4)   # '0006' # 不借助struct模块也可以实现自定义协议
# conn.send(ret.encode('utf-8'))
blen = struct.pack('i',len(msg1))
conn.send(blen)
conn.send(msg1)
conn.send(msg2)
conn.close()
sk.close()



import time
import struct
import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9001))
# length = int(sk.recv(4).decode('utf-8'))
length = sk.recv(4)
length = struct.unpack('i',length)[0]
msg1 = sk.recv(length)
msg2 = sk.recv(1024)
print(msg1.decode('utf-8'))
print(msg2.decode('utf-8'))

sk.close()

 

 

# 每一句话什么意思?执行到哪儿程序会阻塞?为什么阻塞?什么时候结束阻塞?
   # input() # 等待,直到用户输入enter键
   # accept 阻塞,有客户端来和我建立完连接之后
   # recv   阻塞,直到收到对方发过来的消息之后
   # recvfrom 阻塞,直到收到对方发过来的消息之后
   # connect 阻塞,直到server端结束了对一个client的服务,开始和当前client建立连接的时候

验证客户合法性【主要防止恶意攻击,提高安全性】

# 什么场景?
# 是在公司内部 无用户的情况下(没人能看得见我们的client端代码的时候),有人的情况下 就直接做登录即可。

# 密文登录
   # server userinfo密文
   # client input明文密码
   # 至少要在server端进行一次摘要
import os
import socket
import hashlib

secret_key = b'alex_sb'
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

conn,addr = sk.accept()
# 创建一个随机的字符串
rand = os.urandom(32)
# 发送随机字符串
conn.send(rand)

# 根据发送的字符串 + secrete key 进行摘要
sha = hashlib.sha1(secret_key)
sha.update(rand)
res = sha.hexdigest()

# 等待接收客户端的摘要结果
res_client = conn.recv(1024).decode('utf-8')
# 做比对
if res_client == res:
   print('是合法的客户端')
   # 如果一致,就显示是合法的客户端
   # 并可以继续操作
   conn.send(b'hello')
else:
   conn.close()
   # 如果不一致,应立即关闭连接
import socket
import hashlib

secret_key = b'alex_sb979'
sk = socket.socket()
sk.connect(('127.0.0.1',9001))

# 接收客户端发送的随机字符串
rand = sk.recv(32)
# 根据发送的字符串 + secret key 进行摘要
sha = hashlib.sha1(secret_key)
sha.update(rand)
res = sha.hexdigest()
# 摘要结果发送回server端
sk.send(res.encode('utf-8'))
# 继续和server端进行通信
msg = sk.recv(1024)
print(msg)

socketserver模块【socketserver 基于socket完成的,可以模仿并发可以多用户同时访问】

import time
import socketserver

class Myserver(socketserver.BaseRequestHandler):
   def handle(self):
       conn = self.request
       while True:
           try:
               content = conn.recv(1024).decode('utf-8')
               conn.send(content.upper().encode('utf-8'))
               time.sleep(0.5)
           except ConnectionResetError:
               break
server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()

# import socket
#
# sk = socket.socket()
# sk.bind(('127.0.0.1',9001))
# sk.listen()
# while True:
#     conn,_ = sk.accept()
#     while True:
#         try:
#             content = conn.recv(1024).decode('utf-8')
#             conn.send(content.upper().encode('utf-8'))
#             time.sleep(0.5)
#         except ConnectionResetError:
#             break


# class BaseRequestHandler:
#     def __init__(self):
#         self.handle()
#     def handle(self):
#         pass
#
# class Myserver(BaseRequestHandler):
#     def handle(self):
#         pass
# my = Myserver()

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9001))

while True:
   sk.send(b'hello')
   content = sk.recv(1024).decode('utf-8')
   print(content)

 

dp协议

  • udp(发消息) - 在线播放视频\qq发消息\微信消息

    • 不需要建立连接 就可以通信的

    • 不占用连接\不可靠(消息因为网络不稳定丢失)\速度快

    • udp不会发生粘包

     

udp协议的多人通信
   # socket(type=socket.SOCK_DGRAM)
   # sendto 需要写一个对方的地址
   # recvfrom 接收消息和地址
   # close 关闭服务/连接
import socket

sk = socket.socket(type = socket.SOCK_DGRAM) # 套接字
sk.bind(('127.0.0.1',9001))
while True:
   msg,addr= sk.recvfrom(1024)
   print(msg.decode('utf-8'))
   msg = input('>>>')
   sk.sendto(msg.encode('utf-8'),addr)
   


import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
server = ('127.0.0.1',9001)
while True:
   msg = input('>>>')
   if msg.upper() == 'Q':break
   sk.sendto(msg.encode('utf-8'),server)
   msg = sk.recv(1024).decode('utf-8')
   if msg.upper() == 'Q':break
   print(msg)

posted @   Jack_Gao  阅读(545)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示