网络编程汇总

汇总

软件开发架构

  • C/S架构

    就是有客户端和服务端

  • B/S架构

    就是客户端是浏览器

网络编程

  • 什么是网络编程

    基于网络编写代码 能够实现数据远程交互

  • 学习网络编程的目的

    能够开发cs架构的软件

OSI七层协议简介

OSI七层协议:规定了所有计算机在远程交互的时候必须经过相同的处理流程、在制造过程中国必须拥有相同的功能硬件

详细

应用层

传输层

网络层

数据链路层

物理层

  • 物理层会把所有的数据打成二进制 单位 位

  • 数据链路层会把电信号分组 单位 帧 以太网协议

    ARP协议

  • 网络层 ip协议 单位段

    子网掩码

  • 传输层 端口协议 TCP协议 UDP协议 单位 包

  • 应用层 http https ftp 协议

TCP协议

TCP协议有称之为流式协议 可靠协议

  • 三次握手建立连接

    客户端会发送一个SYN给服务端请求与服务端建立键连接,服务端收到会回一个ACK,然后服务端在发送一个SYN给客户端请求与客户端建立连接,客户端会回给服务端一个ACK

    中间的服务端返回ACK和SYN的两步可以合成一不

  • 四次挥手断开连接

    客户端发送给服务端发送一个FIN给服务端请求断开连接,服务端会回一个ACK,然后服务端会给客户端发送一个FIN请求与客户端断开连接,然后客户端回一个ACK。

因为TCP每次发包都会先复制一份,然后发过去,如果对方返回一个数据证明它收到了,发送方才会把复制的包删掉。

  • UDP协议

    也称之为数据报协议

    UDP发送数据它只管发 发过去不管对方收不到都不管所以UDP也称之为不可靠协议

socket模块

如果我们需要编写基于网络进行数据交互的程序意味着我们需要自己通过代码控制我们之前所写的OSI七层协议

服务端:

import socket
# 1. 产生一个socket对象 并指定采用的通信版本和协议(TCP)
# 括号内不写参数 默认就是TCP协议
# family = AF_INET基于网络的套接字 type = SOCK_STREAM流式协议即TCP
server = socket.socket()

# 2.绑定一个固定的地址(服务端必备条件)
server.bind(("127.0.0.1", 8081))  # 127.0.0.1 本地回环地址 只有自己的电脑能可以访问
# 3. 设立半连接池
server.listen(5)

# 4.等待客户端接入
sock, addr = server.accept()  # 返回sock addr         三次握手建连接

print(sock)  # sock 就是双向通道
print(addr)  # 客户端的IP 和端口
# 5.开始服务
data = sock.recv(1024)  # 接收客户端发送过来的消息 最大为1024个字节

print(data.decode("utf8"))  # 因为传输的是二进制 所以要解码

sock.send("芜湖".encode("utf8"))  # 给客户端发消息 因为只能发二进制 所以要编码

# 6.关闭双向通道
sock.close()  # 四次挥手断开连接

# 7.关闭服务
server.close()  # 店倒闭了

客户端:

import socket
# 1.生成socket对象指定类型和协议
client = socket.socket()  # 默认TCP协议
# 2.通过服务端的地址 连接服务器
client.connect(("127.0.0.1", 8081))
# 3.直接给服务器发送消息
client.send("哈哈哈芜湖".encode("utf8"))
# 4.接收服务端发送过来的消息
data = client.recv(1024)
print(data.decode("utf8"))
# 5.断开连接
client.close()

半连接池

当前多个客户端连接进来,我们可以等待数量(不考虑并发问题)

server.listen(5)

粘包问题

当发送端在短时间内发送多次较短的时候接收端会一次性把这个数据全部接收

  • 解决思路

    让接收方知道要收的数据有多大

struct模块

pack 压缩 i 模式可以把固定的整型压缩成4个字节

unpack 解压 i 模式 可以解压成一个元组 元组的第一个数据是 发送过来的数据的真实的长度

随笔- 0 文章- 39 评论- 2 阅读- 318

粘包问题

目录

内容概要

  • 粘包现象
  • 解决粘包逻辑思路
  • 代码实操
  • UDP基本代码使用
  • 并发编程理论之操作系统发展史
  • 多道技术
  • 进程理论即调度算法

粘包现象

当发送端在短时间内发送多次较短的数据的时候

接收端会一次性把这些数据全部接收

  • 粘包问题产生的原因

    1. 不知道每次的数据到底多大
    2. TCP也称之为流式协议:数据像水流一样不断没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)
  • 避免粘包现象的核心思路\关键点

    如何明确即将接收的数据具体有多大

ps:如何将长度变化的数据全部制作成固定长度的数据

struct模块

import json
import struct

l1 = [1, 2, 3, 4, 5, 6, 7]
d1 = {
    "name": "jason",
    "age": "123"
}
# 1.因为字典没有办法转成字节类型 只有字符串可以转成字节类型
# 2.因为我们是为了发做准备 所以先把他转换成json格式 让接收方 但是解开的还是 原来的类型
# 比如 我传的字典 传过去打开还是字典
# res = struct.pack("i", json.dumps(l1).__len__())  # 第一参数传模式  第二个参数传数字 int类型
# res1 = struct.pack("i", json.dumps(d1).__len__())
#
# print(len(res))  # 4
# print(len(res1))  # 4

# res = struct.pack("i", len(l1))  
# res1 = struct.pack("i", len(d1))  
# print(len(res))  # 4
# print(len(res1))  # 4

不管多少 压缩后的长度都是 4

但是他也是有长度的限制的

img

解决粘包问题

服务端:

import struct
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8080))
server.listen(5)

sock, addr = server.accept()  # 三次握手 建连接 返回管道  和 客户端的地址

# 发送先发送头  也就是 先告诉对方我要发送的数据的大小
data = "hello"
# 因为发送的字节形式  所以要发送的字节的长度
data_head = struct.pack("i", len(data.encode("utf8")))
sock.send(data_head)
sock.send(data.encode("utf8"))
data1 = "world"
data1_head = struct.pack("i", len(data1.encode("utf8")))
sock.send(data1_head)
sock.send(data1.encode("utf8"))

客户端:

import struct
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8080))

data_head = client.recv(4)
data_len = struct.unpack("i", data_head)[0]
print(data_len)
data = client.recv(data_len)
print(data.decode("utf8"))

data1_head = client.recv(4)
data1_len = struct.unpack("i", data1_head)[0]
print(data1_len)
data1 = client.recv(data1_len)
print(data1.decode("utf8"))

并发编程理论

计算机真正干活的人是CPU

  • 操作系统的发展史

    • 穿孔卡片
    • 批处理系统
    • 多道程序设计
    • 现在的操作系统
  • 多道技术

    • 单道技术

      就是一个一个来

    • 多道技术

      利用空闲的时间准备其他数据,最大化提升CPU利用率

  • 多道技术详细

    1. 切换

      1. 计算机的CPU会在两种情况下切换

        输入输出操作

      2.程序长时间占用CPU

    2. 保存状态

      CPU每次切走之前都需要保存当前的状态(就是执行到哪了)之后回来知道执行到哪了然后在继续执行

  • 进程的并行与并发

    • 并行

      就是多个程序 一起运行 并行多少个程序就需要多少个CPU

    • 并发

      看上去像并行 单个CPU可以实现

  • 进程的三种状态

    • 就绪态

      只有有CPU就可以立即运行

    • 运行态

      正在运行

    • 阻塞态

      在进行IO操作,就算给了CPU也无法运行

  • 同步异步

    • 同步
      就是当你在调用函数的时候,会等到函数执行完才会执行下面代码(就是函数名加括号下的代码)

    • 异步

      就是当你在调用函数的时候他调一下函数然后就接着执行下面的代码(就是不会等函数执行完在运行,就调一下就往下运行了)

  • 阻塞与非阻塞

    这是描述进程的状态

    • 阻塞

      阻塞态

    • 非阻塞

      就是运行态 或者 就绪态

  • 代码创建进程的两种方式

    """
    方式一
    """
    # from multiprocessing import Process
    #
    #
    # def task(name):
    #     print("芜湖 %s" % name)
    #
    #
    # if __name__ == '__main__':
    #     p = Process(target=task, args=("jason",))
    #     p.start()
    
    
    """
    
    方式二
    """
    
    from multiprocessing import Process
    
    
    class MyProcess(Process):
        def __init__(self, name):
            super().__init__()
            self.name = name
    
        def task(self):
            print("芜湖 %s" % self.name)
    
        def run(self):
            self.task()
    
    
    if __name__ == '__main__':
        p = MyProcess("jason")
        p.start()
    
  • 进程的对象的方法

    from multiprocessing import Process
    
    
    def task(name):
        print("芜湖 %s" % name)
    
    
    if __name__ == '__main__':
        p = Process(target=task, args=("jason",), name="我是子进程哟")
        p.start()  # 创建进程并运行
        # p.join()  # 会让主进程等到子进程运行完在运行
        # print(p.pid)  # 打印子进程号
        # p.terminate() # 直接杀死当前进程
        print(p.name)  # 答应子进程的名字
        p.daemon = True  # 让子进程变成父进程的守护进程
        p.is_alive()  # 返回一个布尔值 如果子进程 活着则返回True  没有则返回False
    
  • 获取pid的两种方法

    from multiprocessing import Process,current_process
    import os
    def task():
        print(current_process())
        print("芜湖")
        print(current_process().pid)
        print("我是os", os.getpid())
        print("我是父进程的pid", os.getppid())
    
    
    if __name__ == '__main__':
        p = Process(target=task)
        p.start()
        print(os.getpid())
    
    """
    12208
    <Process name='Process-1' parent=12208 started>
    芜湖
    14652
    我是os 14652
    我是父进程的pid 12208
    
    """
    
  • 消息队列

    可以让进程与进程之间通信

    消息队列:存储数据的地方 所有人可以存 也都可以取

    from multiprocessing import Process, Queue
    
    
    def put_data(q, data):
        q.put(data)
    
    
    def get_data(q):
        print(q.get())
    
    
    if __name__ == '__main__':
        q = Queue()
        p1 = Process(target=put_data, args=(q, "jason"))
        p2 = Process(target=get_data, args=(q, ))
        p1.start()
        p2.start()
    
    """
    jason
    """
    
  • 生产者消费者模型

    生产者 生产数据的人 消费者 取数据的人

    但是生产者消费者模型都必须有消息队列

  • 守护进程

    守护进程会随着守护的进程结束掉而结束

  • 僵尸进程和孤儿进程

    • 僵尸进程就是进程死掉了但是跟这个进程的一些资源还没有被回收

    • 孤儿进程

      子进程运行着父进程意外死亡,当出现这种情况的时候操作系统会派pid为1的进程去接管这些孤儿进程

  • 多进程数据错乱问题

    当多个数据去修改同一个数据的时候可能会没有把数据修改成功。

posted @ 2022-11-20 21:41  可否  阅读(34)  评论(0)    收藏  举报