Python--第二阶段--03.网络编程基础


计算机网络功能主要包括实现资源共享,实现数据信息的快速传递。

OSI七层模型

制定组织:ISO(国际标准化组织)

作用:使网络通信工作流程标准化

我们主要实践的:应用层、表示层、会话层
通信的研究:网络层、链路层、物理层

应用层:提供用户服务,具体功能有应用程序实现
表示层:数据的压缩优化加密
会话层:建立用户级的链接,选择适当的传输服务
传输层:提供传输服务
网络层:路由选择,网络互联----ip地址层
链路层:进行数据交互,控制具体数据的发送----信息转换成二进制信息
物理层:提供数据传输的硬件保证,网卡接口,传输介质----MAC地址层

优点:

  1. 建立了统一的工作流程;
  2. 分布清晰,各司其职,每个步骤分工明确;

四层模型(TCP/IP模型)

七层和四层的对比

数据传输过程

  1. 发送端由应用程序发送消息,逐层添加首部信息,最终在物理层发送消息包;
  2. 发送的消息经过多个节点(交换机、路由器)传输,最终达到目标主机;
  3. 目标主机由物理层逐层解析首部消息包,最终到应用程序呈现消息。
    网络传输过程

网络协议

在网络数据传输中,都遵循的规定,包括建立什么样的数据结构,什么样的特殊标志等。

网络基础概念

  • IP地址(确定哪一台主机)
    功能:确定一台主机的网络路由位置
    查看本机网络地址命令:ifconfig
    结构:
    • IPv4点分十进制表示 172.40.91.185 每部分取值范围是0-255
    • IPv6 128位 扩大了地址范围
  • 域名
    定义:给网络服务器地址起名字
    作用:方便记忆,表达一定含义
    ping [IP]:测试是否可以正常连接
  • 端口号(port)(确定哪一个应用)
    作用:端口是网络地址的一部分,用于区分主机上不同的网络应用程序。
    特点:一个系统中的应用监听端口不能重复
    取值范围:1-65535
    • 1-1023系统应用或者大众程序监听端口
    • 1024-65535自用端口

传输层服务

面向连接的传输服务(基于TCP协议的数据传输)

  1. 传输特征:提供了可靠的数据传输,靠靠性指数据传输过程中无丢失,无失序,无差错,无重复。
  2. 实现手段:在通信前需要建立数据连接,通信结束要正常断开连接。
三次握手(建立连接)
  • 客户端向服务器发送消息报文请求连接
  • 服务端收到请求后,回复报文确定可以连接
  • 客户端收到回复,发送最终报文连接建立
    TCP三次握手
  1. 适用情况:对数据传输准确性有明确要求,传输文件较大,需要确保可靠性的情况。比如:网页获取,文件下载,邮件收发。
四次挥手(断开连接)

四次挥手

面向无连接的传输服务(基于UDP协议的数据传输)

  1. 传输特点:不保证传输的可靠性,传输过程没有连接和断开,数据收发自由随意。
  2. 适用情况:网络交叉,对传输可靠性要求不高。比如:网络视频,群聊,广播

面试要求

  • OSI七层模型介绍一下,TCP/IP模型是什么?
  • tcp服务和udp服务有什么区别?
  • 三次握手和四次挥手指什么,过程是怎样的?

socket套接字编程

套接字介绍

  1. 套接字:实现网络编程进行数据传输的一种技术分段
  2. Python实现套接字编程:import socket
  3. 套接字分类:
    • 流式套接字(SOCK_STREAM):以字节流方式传输数据,实现tcp网络传输方案。(面向连接–tcp协议–可靠的–流式套接字)
    • 数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现UDP网络传输方案。(无连接–UDP协议–不可靠–数据报套接字)

tcp套接字编程

服务端流程

socket(创建电话)–>bind(绑定地址)–>listen(监听,可以被连接)–>accept(开机,等待连接)–>send/recv–>close(销毁电话)

  1. 创建套接字(socket.socket())
  • 函数:
    • sockfd = socket.socket()
  • 功能:创建套接字对象
  • 参数:
    • socket_family 网络地址类型 AF_INET表示ipv4(默认)
    • socket_type 套接字类型SOCK_STREAM(流式套接字TCP) SOCK_DGRAM(数据报UDP)
    • proto 通常为0 选择子协议,默认值为0
  • 返回值:套接字对象
  1. 绑定地址(套接字对象.bind(addr))
  • 可填写地址:
    • 本地地址:‘localhost’,‘127.0.0.1’别的计算机客户端无法访问,只能在本机的客户端才可以访问
    • 网络地址:‘172.40.91.185’ ifconfig出来的网络地址,都可以访问,都必须访问该ip来访问
    • 自动获取地址:‘0.0.0.0’,或者‘’自动获取IP

在这里插入图片描述

  • 函数方法:
    • sockfd.bind(addr)
  • 功能:绑定本机网络地址
  • 参数:addr是一个二元元祖(ip,port)(‘0.0.0.0’,8888)
  1. 设置监听(套接字对象.listen)
  • 函数方法
    • sockfd.listen(n)
  • 功能:将套接字设置为监听套接字,确定监听队列大小
  • 参数:n监听队列大小
  1. 等待处理客户端连接请求
  • 函数方法:
    • connfd.addr = sockfd.accept()–>阻塞操作
  • 功能:阻塞等待处理客户端请求
  • 返回值:
    • connfd客户端连接套接字
    • addr 连接的客户端地址
  1. 消息收发
  • 收消息:
    • 函数方法:
      • data = connfd.recv(buffersize)–>阻塞操作
    • 功能:接收客户端消息
    • 参数:每次最多接收消息的大小
    • 返回值:接收到的内容
  • 发消息:
    • 函数方法:n = connfd.send(data)
    • 参数要求发送的内容均为bytes格式
    • 返回值:发送的字节数
  1. 关闭套接字
    sockfd.close()
    关闭套接字

TCP的socket服务端代码实现:
功能性代码,注重流程和函数的使用

'''
    功能性代码,注重流程和函数的使用

'''

import socket

# 创建TCP套接字
sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 绑定地址
sockfd.bind(('0.0.0.0',8888))
# 设置监听
sockfd.listen()

# 阻塞等待连接处理
print('waiting for connect...')
connfd,addr = sockfd.accept()
print('connect from ',addr) # 打印连接的客户端地址

# 收发消息
data = connfd.recv(1024).decode()
print('收到:',data)
n = connfd.send(b'Thanks') # 发送字节串
print(n)

# 关闭套接字
connfd.close()
sockfd.close()

作业:
1.将文件操作总结
2.总结面试要求题目的回答
3.将服务端流程函数熟练

客户端流程

socket–>bind[可选] -->connect–>send/recv–>close

  1. socket
  • 函数:
    • sockfd = socket.socket()
  1. connect(和服务端accept相对应)
    套接字对象.connect(server_addr)
    功能:连接服务器
    参数:元祖 服务器地址

  2. send/recv

  3. close

  • 注意:
    • 连接端阻塞在recv,对方断开的话,收到空
    • tcp连接中如果一段已经不存在,仍然视图通过send发送则会产生broken pipe错误
    • 一个服务端可以连接多个客户端。

TCP客户端实现代码:

import socket
# 创建tcp套接字
sockfd = socket.socket()  # 使用默认草书-->tcp套接字
# 和服务端连接
sockfd.connect(('192.168.1.102',8888))
# 发送消息
sockfd.send('你好'.encode())    # 转换为字节串再发送
# 接收消息
data = sockfd.recv(1024)
print(data)
# 断开连接
sockfd.close()

TCP服务端可循环代码:

import socket

#创建TCP套接字
sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sockfd.bind(('0.0.0.0',8888))
# 设置监听
sockfd.listen()
while True:
    # 阻塞等待连接处理
    print('waiting for connect...')
    connfd,addr = sockfd.accept()
    print('connect from ',addr) # 打印连接的客户端地址
    while True:
        # 收发消息
        data = connfd.recv(1024).decode()
        print('收到:',data)
        if not data:
            break
        n = connfd.send(b'Thanks') # 发送字节串
        print(n)
    # 关闭套接字
    connfd.close()
sockfd.close()

TCP客户端可循环代码:

import socket
# 创建套接字
sockfd = socket.socket()
# 和服务端连接
sockfd.connect(('192.168.1.102',8888))
# 发送消息
while True:
    data = input('msg>>')
    if data == 'quit':
        break
    sockfd.send(data.encode()) # 转换为字节串再发送
    # 接收消息
    data = sockfd.recv(1024)
    print(data)
# 断开连接
sockfd.close()

TCP粘包现象

  • 缓冲区:
    • 作用:协调收发速度
    • 传输速度过快,多次的接收的内容一次性的被缓冲区接收,但是所有发过来的数据都会接收,不会丢失。
    • send–>发送端缓冲区–>接收端缓冲区–>recv
  • 粘包现象
    • 原因:tcp以字节流方式传输,没有消息便捷。多次发送的消息被一次接收,此时就会形成粘包。
    • 影响:如果每次发送内容是一个独立的含义,需要接收端独立解析,此时粘包会有影响。
    • 处理方法
      1. 人为的添加消息边界;
      2. 控制发送速度(发送端sleep())。

练习:
文件的传输:将一个文件从客户端发送到服务端保存
要求:文件可能是文本类型,也可能是二进制类型文件。

文件传输服务端代码:

from socket import *

sk = socket()
sk.bind(('0.0.0.0',8888))
sk.listen()
conn,addr = sk.accept()
print('连接',addr)
'''
    思路:
    1.以二进制写的模式打开文件wb
    2.将接收到的内容写入文件
'''
# 打开文件
f = open('test1.txt','wb')
# 循环接收写入文件:
while True:
    data = conn.recv(1024)
    if not data:
        break
    f.write(data)
f.close()
conn.close()
sk.close()

文件传输客户端代码:

from socket import *
s = socket()
s.connect(('192.168.1.102',8888))

# 读取目标文件,循环发送
f = open('test.txt','rb')
while True:
    data = f.read(1024)
    if not data:
        break
    s.send(data)
f.close()
s.close()

UDP套接字编程

服务端流程

socket–>bind–>recvfrom–>sendto–>close

  1. 创建UDP套接字连接
    sockfd = socket(AF_INET,SOCK_DGRAM)
  2. 绑定地址
    sockfd.bind(addr)
  3. 消息收发
    • 消息接收:
      • 函数:data,addr = sockfd.recvfrom(buffersize)
      • 功能:接收UDP消息
      • 参数:每次最多接收多少字节
      • 返回值:
        • data 接收到的内容
        • addr消息发送方地址
    • 消息发送
      • 函数:n = sockfd.sendto(data,addr)
      • 功能:发送UDP消息
      • 参数:
        • data发送的内容,bytes格式
        • addr目标地址
      • 返回值:发送的字节数
  4. 关闭套接字
"""
    UDP_server.py
    重点代码
"""

from socket import *
# 创建套接字
sk = socket(AF_INET,SOCK_DGRAM)

# 绑定地址
server_addr = ('127.0.0.1',8888)
sk.bind(server_addr)

# 循环收发消息
while True:
    data,addr = sk.recvfrom(1024)
    print(data)
    sk.sendto(b'Thanks',addr)

# 关闭套接字
sk.close()

客户端流程

socket–>sendto–>recvfrom–>close
代码实现:

"""
    UDP_client.py
    重点代码
"""

from socket import *
# 创建套接字
sk = socket(AF_INET,SOCK_DGRAM)
# 服务端地址
server_addr = ('127.0.0.1', 8888)
# 循环收发消息
while True:
    msg = input('msg')
    if not msg:
        break
    sk.sendto(msg.encode(), server_addr)
    data,addr = sk.recvfrom(1024)
    print(data)

# 关闭套接字
sk.close()

练习:使用udp客户端查询单词,得到单词的解释,如果没有该单词则得到“没有单词”。客户端可以循环输入单词,知道输入空退出
作业代码(dict文件参考上一篇文章最后的任务文件):
server端:

from socket import *

# 创建套接字
sk = socket(AF_INET, SOCK_DGRAM)

# 绑定地址
server_addr = ('127.0.0.1', 8888)
sk.bind(server_addr)


def find_word(data):
    f = open('dict.txt', 'r')
    for line in f:
        w = line.split(' ')[0]
        if w > data:
            f.close()
            return '没有找到该单词'
        elif w == data:
            f.close()
            return line

# 循环收发消息
while True:
    data, addr = sk.recvfrom(1024)
    data = data.decode()
    # 查单词
    return_date = find_word(data)
    sk.sendto(return_date.encode(), addr)

# 关闭套接字
sk.close()

client端:

from socket import *
# 创建套接字
sk = socket(AF_INET,SOCK_DGRAM)
# 服务端地址
server_addr = ('127.0.0.1', 8888)
# 循环收发消息
while True:
    msg = input('word>>')
    if not msg:
        break
    sk.sendto(msg.encode(), server_addr) # 发送单词
    data,addr = sk.recvfrom(1024)   # 返回单词的解释
    print(data.decode())

# 关闭套接字
sk.close()

总结:

tcp套接字和udp套接字编程区别

  1. 流式套接字是以字节流方式传输数据,数据报套接字以数据报形式传输;
  2. tcp套接字会有粘包,udp套接字有消息边界,不会粘包;
  3. tcp套接字保证消息的完整性,udp套接字则不能;
  4. tcp套接字依赖listen、accept建立连接才能收发消息,udp套接字则不需要;
  5. tcp套接字使用send、recv收发消息,udp套接字使用sendto、recvfrom。

套接字属性

from socket import *

s = socket()
s.bind(('127.0.0.1', 8889))

print('地址类型:', s.family)
print('套接字类型:', s.type)
print('获取套接字绑定地址:', s.getsockname())
print('文件描述符:', s.fileno())
print('连接端地址:', s.getpeername())

# setsockopt设置套接字选项,需要在绑定端口之前使用
# 参数:level选项类别(SOL_SOCKET)  option 具体选项内容 value选项值
# setsockopt(SOL_SOCKET,SO_REUSRADDR,1)
# getsockopt(SOL_SOCKET,SO_REUSRADDR)

UDP套接字的应用

广播

广播定义:一端发送多点接收
广播地址:每个网络的最大地址为发送广播的地址,向该地址发送,则网段内所有主机都能接收。
recv.py

"""
广播接收
    1.创建UDP套接字
    2.设置套接字可以发送接收广播(setsockopt)
    3.选择接收的端口
    4.等待接收
"""

from socket import *
sk = socket(AF_INET,SOCK_DGRAM)

# 设置套接字接收广播
sk.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
sk.bind(('0.0.0.0',9999))

while True:
    msg,addr = sk.recvfrom(1024)
    print(msg.decode())

server.py

"""

    广播发送端
    1.创建UDP套接字;
    2.设置可以发送广播;
    3.循环向广播地址发送。
"""
import time
from socket import *
# 广播地址192.168.1.255
dest = ('192.168.1.255',9999)
s = socket(AF_INET,SOCK_DGRAM)
s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
data = '''
没意思,拜拜了
你想玩啥去玩吧
'''
while True:
    time.sleep(2)
    s.sendto(data.encode(),dest)

TCP套接字之HTTP传输

HTTP协议(超文本传输协议)

  1. 用途:网络获取,数据的传输
  2. 特点:
    • 应用层协议,传输层使用tcp传输
    • 简单,灵活,很多语言都有HTTP专门接口
    • 无状态,协议不记录传输内容
    • HTTP1.1支持持久连接,丰富了请求类型
  3. 网页请求过程
    • 客户端(浏览器)通过tcp传输,发送http请求给服务端
    • 服务端接收到http请求后进行解析
    • 服务端处理请求内容,组织响应内容
    • 服务端将响应内容以http响应格式发送给浏览器
    • 浏览器接收到响应内容,解析展示在这里插入图片描述

HTTP请求(request)

  1. 请求行:具体的请求类别和请求内容
GET/HTTP/1.1
请求类别请求内容协议版本
  • 请求类别:每个请求类别表示要做不同的事情

    • GET:获取网络资源
    • POST:提交一定的信息,得到反馈
    • HEAD:只获取网络资源的响应头
    • PUT:更新服务器资源
    • DELETE:删除服务器资源
    • CONNECT
    • TRACE:测试
    • OPTIONS:获取服务器性能信息
  • 请求内容:/后面的网址,就是请求内容

  • 协议版本:

  1. 请求头:对请求的进一步解释和描述.
    键值对表述
  2. 空行
  3. 请求体

作业:
1.熟悉http协议请求的格式结构
2.熟练四个程序(tcp、udp)

HTTP响应(response)

响应格式:
响应行:
HTTP/1.1 200 OK
版本信息 响应码 附加信息

响应码:
1xx 提示信息,表示请求被接收
2xx 响应成功
3xx 响应需要进一步操作,重定向
4xx 客户端错误
5xx 服务器错误
响应头:对响应内容的描述
空行:
响应体:响应的主题内容信息

HTTP协议服务器端代码:

import socket

#创建TCP套接字
sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sockfd.bind(('0.0.0.0',8888))
# 设置监听
sockfd.listen(3)
while True:
    # 阻塞等待连接处理
    print('waiting for connect...')
    connfd,addr = sockfd.accept()
    print('connect from ',addr) # 打印连接的客户端地址
    while True:
        # 收发消息
        data = connfd.recv(1024).decode()
        print('收到:',data)
        if not data:
            break
        response = '''HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n\r\n<h1>Hello world</h1>
        '''
        n = connfd.send(response.encode()) # 发送字节串
        print(n)
        print(response)
    # 关闭套接字
    connfd.close()
sockfd.close()

struct模块的使用

可以按照自己设定的格式,将数字、字符、字节串等转化为可以作为传输的bytes格式。
发送的内容转化为网络字节序,在接收端在用网络字节序转化为可读。
原理:将一组简单数据进行打包,转化为bytes格式发送,或将一组bytes格式数据进行解析。(将Python的数据格式转换成类C的数据)
接口使用:
Struct(fmt)
功能:生成结构化对象
参数:fmt 定制的数据结构 查表(i表示整形,s表示字节串,f表示浮点型)

st.pack(v1,v2,v3…)
功能:将一组数据按照指定格式打包转换为bytes
参数:要打包的数据
返回值:bytes字节串

st.unpack(bytes_data)
功能:将bytes数据转换为元组数据
参数:bytes字节串
返回值:一个元组

在这里插入图片描述
也可以直接用模块名来操作,
struct.pack(fmt,v1,v2,v3)
struct.unpack(fmt,bytes_data)
在这里插入图片描述

格式符C语言类型Python类型Standard size
xpadbyte(填充字节)no value
ccharstring of length 11
bsigned charinteger1
Bunsigned charinteger1
?_Boolbool1
hshortinteger2
Hunsigned shortinteger2
iintinteger4
I(大写的i)unsigned intinteger4
l(小写的L)longinteger4
Lunsigned longlong4
qlong longlong8
Qunsigned long longlong8
ffloatfloat4
ddoublefloat8
schar[]string
pchar[]string
Pvoid *long

练习:
1.使用UDP,在客户端不断录入学生信息,将其打包发送到服务端,在服务端将学生信息写入到一个文件中,每个学生信息占一行;
2.信息格式:id name age score
注意:id (int) name(str) age(int)score(float)
server端:

from socket import *
import struct
# 创建套接字
sk = socket(AF_INET,SOCK_DGRAM)

# 绑定地址
server_addr = ('127.0.0.1',8888)
sk.bind(server_addr)
st = struct.Struct('i32sif')
f = open('student.txt','a')
# 循环收发消息
while True:
    data,addr = sk.recvfrom(1024)
    print(data)
    data = st.unpack(data)
    info = '%d %-10s %d %.1f\n'%data
    f.write(info)
    f.flush()
# 关闭套接字
sk.close()

client端

from socket import *
import struct
st = struct.Struct('i32sif')

# 创建套接字
sk = socket(AF_INET,SOCK_DGRAM)
# 服务端地址
server_addr = ('127.0.0.1', 8888)
# 循环收发消息
while True:
    id = int(input('ID:'))
    name = input('Name:').encode()
    age = int(input('age:'))
    score = float(input('Score:'))
    data = st.pack(id,name,age,score)
    sk.sendto(data, server_addr)
# 关闭套接字
sk.close()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值