首页 > 编程语言 >python必坑知识点(网络编程)

python必坑知识点(网络编程)

时间:2023-08-23 15:15:05浏览次数:39  
标签:知识点 socket python data server file 必坑 conn size

1 基础

1.1 作业题1

# 1. 简述 二层交换机 & 路由器 & 三层交换机 的作用。
"""
二层交换机:构建局域网并实现局域网内数据的转发。
路由器:实现跨局域网进行通信。
三层交换机:具备二层交换机和路由的功能。
"""

# 2. 简述常见词:IP、子网掩码、DHCP、公网IP、端口、域名的作用。
"""
IP,本质上是一个32位的二进制,通过 . 等分为了4组8位二进制。
子网掩码,用于指定IP的网络地址和主机地址。
DHCP,网络设备中的一个服务,用于给接入当前网络的电脑自动设置 IP、子网掩码、网关。(动态分配IP、子网掩码、网关)
公网IP,一般企业拉专线时会给固定的公网IP,只有具备公网IP才可以被互联网上的其他电脑访问。
端口,IP用于表示某台电脑,端口则表示此电脑上的具体程序。0-65535
域名,与IP构造对应关系,方便用户记忆。
"""

2 TCP和UDP

2.1 基础

# IP可复用
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 非阻塞
server.setblocking(False)
# TCP和UDP
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

2.1.1 TCP实例

  1. server

    import os
    import json
    import socket
    import struct
    
    
    def recv_data(conn, chunk_size=1024):
        # 获取头部信息:数据长度
        has_data_len = 0
        bytes_data = b''
        while has_data_len < 4:
            chunk = conn.recv(4 - has_data_len)
            has_data_len += len(chunk)
            bytes_data += chunk
        data_length = struct.unpack('i', bytes_data)[0]
    
        # 获取数据
        data_list = []
        has_read_data_size = 0
        while has_read_data_size < data_length:
            size = chunk_size if (data_length - has_read_data_size) > chunk_size else data_length - has_read_data_size
            chunk = conn.recv(size)
            data_list.append(chunk)
            has_read_data_size += len(chunk)
    
        data = b"".join(data_list)
    
        return data
    
    
    def recv_file(conn, save_file_name, chunk_size=1024):
        save_file_path = os.path.join('files', save_file_name)
        # 获取头部信息:数据长度
        has_read_size = 0
        bytes_list = []
        while has_read_size < 4:
            chunk = conn.recv(4 - has_read_size)
            bytes_list.append(chunk)
            has_read_size += len(chunk)
        header = b"".join(bytes_list)
        data_length = struct.unpack('i', header)[0]
    
        # 获取数据
        file_object = open(save_file_path, mode='wb')
        has_read_data_size = 0
        while has_read_data_size < data_length:
            size = chunk_size if (data_length - has_read_data_size) > chunk_size else data_length - has_read_data_size
            chunk = conn.recv(size)
            file_object.write(chunk)
            file_object.flush()
            has_read_data_size += len(chunk)
        file_object.close()
    
    
    def run():
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # IP可复用
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    
        sock.bind(('127.0.0.1', 8001))
        sock.listen(5)
        while True:
            conn, addr = sock.accept()
    
            while True:
                # 获取消息类型
                message_type = recv_data(conn).decode('utf-8')
                if message_type == 'close':  # 四次挥手,空内容。
                    print("关闭连接")
                    break
                # 文件:{'msg_type':'file', 'file_name':"xxxx.xx" }
                # 消息:{'msg_type':'msg'}
                message_type_info = json.loads(message_type)
                if message_type_info['msg_type'] == 'msg':
                    data = recv_data(conn)
                    print("接收到消息:", data.decode('utf-8'))
                else:
                    file_name = message_type_info['file_name']
                    print("接收到文件,要保存到:", file_name)
                    recv_file(conn, file_name)
    
            conn.close()
        sock.close()
    
    
    if __name__ == '__main__':
        run()
    
    
  2. client

    import os
    import json
    import socket
    import struct
    
    
    def send_data(conn, content):
        data = content.encode('utf-8')
        header = struct.pack('i', len(data))
        conn.sendall(header)
        conn.sendall(data)
    
    
    def send_file(conn, file_path):
        file_size = os.stat(file_path).st_size
        header = struct.pack('i', file_size)
        conn.sendall(header)
    
        has_send_size = 0
        file_object = open(file_path, mode='rb')
        while has_send_size < file_size:
            chunk = file_object.read(2048)
            conn.sendall(chunk)
            has_send_size += len(chunk)
        file_object.close()
    
    
    def run():
        client = socket.socket()
        client.connect(('127.0.0.1', 8001))
    
        while True:
            """
            请发送消息,格式为:
                - 消息:msg|你好呀
                - 文件:file|xxxx.png
            """
            content = input(">>>")  # msg or file
            if content.upper() == 'Q':
                send_data(client, "close")
                break
            input_text_list = content.split('|')
            if len(input_text_list) != 2:
                print("格式错误,请重新输入")
                continue
    
            message_type, info = input_text_list
    
            # 发消息
            if message_type == 'msg':
    
                # 发消息类型
                send_data(client, json.dumps({"msg_type": "msg"}))
    
                # 发内容
                send_data(client, info)
    
            # 发文件
            else:
                file_name = info.rsplit(os.sep, maxsplit=1)[-1]
    
                # 发消息类型
                send_data(client, json.dumps({"msg_type": "file", 'file_name': file_name}))
    
                # 发内容
                send_file(client, info)
    
        client.close()
    
    
    if __name__ == '__main__':
        run()
    
    

2.1.2 UDP实例

  1. server

    import socket
    
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server.bind(('127.0.0.1', 8002))
    
    while True:
        data, (host, port) = server.recvfrom(1024) # 阻塞
        print(data, host, port)
        server.sendto("好的".encode('utf-8'), (host, port))
    
  2. client

    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    while True:
        text = input("请输入要发送的内容:")
        if text.upper() == 'Q':
            break
        client.sendto(text.encode('utf-8'), ('127.0.0.1', 8002))
        data, (host, port) = client.recvfrom(1024)
        print(data.decode('utf-8'))
    
    client.close()
    

2.2 三次握手,四次挥手

三次握手情景:
1、客户端主机说:“我可以给你发送数据吗?”
2、服务器说:“可以的,不过我可能也会给你发送数据。”
3、客户端:“好,那我开始互相发送数据吧。”

四次挥手的情景大致是这样的:
1、客户端主机说:“我没有数据了,断开连接吧。 ”
2、服务器说:“好,但是我还有数据(不断给客户端发送数据,此时客户端已经不能给服务端发送数据了,但是必须要就收服务端发来的数据)。”
3、(当服务端给客户端发完数据后)服务端说:“我发完了,断开连接吧。”
4、客户端说:“好,断开连接吧。”

2.3 OSI七层模型

img

2.4 对比

UDP TCP
是否连接 无连接 面向连接
是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输,使用流量控制和拥塞控制
连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信
传输方式 面向报文 面向字节流
首部开销 首部开销小,仅8字节 首部最小20字节,最大60字节
适用场景 适用于实时应用(IP电话、视频会议、直播等) 适用于要求可靠传输的应用,例如文件传输

3 粘包

当客户端向服务端放松数据时,服务端由于网络等原因,无法迅速的接受到数据,这时如果客户端连续的发送多条数据,服务端接收时无法区分客户端发来几条数据,而是直接接收全部(数据量如果小的话),这时客户端发送的几条消息就会被服务端当成一条数据接收。
处理:
	客户端每次发送数据时,先把数据的长度按固定的字节数发送到服务端,服务端在接收时,先按固定的字节数把客户端要发送的数据的长度获取一下,之后再根据数据长度获取客户端发来的数据。

4 阻塞和非阻塞

server.setblocking(False)  # 加上就变为了非阻塞

如果代码变成了非阻塞,程序运行时一旦遇到 `accept`、`recv`、`connect` 就会抛出 BlockingIOError 的异常。

5 IO多路复用

IO多路复用 + 非阻塞,可以实现让TCP的服务端同时处理多个客户端的请求
"""
优点:
	1. 干点那其他的事。
	2. 让服务端支持多个客户端同时来连接。
"""

# ################### socket服务端 ###################
import select
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)  # 加上就变为了非阻塞
server.bind(('127.0.0.1', 8001))
server.listen(5)

inputs = [server, ]  # socket对象列表 -> [server, 第一个客户端连接conn ]

while True:
    # 当 参数1 序列中的socket对象发生可读时(accetp和read),则获取发生变化的对象并添加到 r列表中。
    # r = []
    # r = [server,]
    # r = [第一个客户端连接conn,]
    # r = [server,]
    # r = [第一个客户端连接conn,第二个客户端连接conn]
    # r = [第二个客户端连接conn,]
    r, w, e = select.select(inputs, [], [], 0.05)
    for sock in r:
        # server
        if sock == server:
            conn, addr = sock.accept()  # 接收新连接。
            print("有新连接")
            # conn.sendall()
            # conn.recv("xx")
            inputs.append(conn)
        else:
            data = sock.recv(1024)
            if data:
                print("收到消息:", data)
            else:
                print("关闭连接")
                inputs.remove(sock)
    # 干点其他事 20s

# ################### socket客户端 ###################
import socket

client = socket.socket()
# 阻塞
client.connect(('127.0.0.1', 8001))

while True:
    content = input(">>>")
    if content.upper() == 'Q':
        break
    client.sendall(content.encode('utf-8'))

client.close()

标签:知识点,socket,python,data,server,file,必坑,conn,size
From: https://www.cnblogs.com/z-h-q/p/17651659.html

相关文章

  • python必坑知识点(面向对象)
    面向对象的三大特性:封装,继承,多态1三大特性1.1封装将数据或方法放到类里,以供外部调用或自己隐藏#封装原则1、高内聚:高内聚是指一个模块中各个部分之间关联应该是紧密的。2、低耦合:低耦合是指多个模块之间的关联应该是松散的。1.2继承将类中的公共的方法提取到基类中......
  • python面试题01
    基础题写一个带参数的闭包fromfunctoolsimportwrapsdefouter(n):@wraps(n)defwrapper(func):definner(*args,**kwargs):print("inner")print(n)returnfunc(*args,**kwargs)returninner......
  • Python列表推导式
    int_list=range(1,55,2)print(list(int_list))结果:[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53]int_list=range(1,55,2)print([str(x)forxinint_list])结果:['1','3',&......
  • 【腾讯云 TDSQL-C Serverless 产品体验】 使用 Python 向 TDSQL-C 添加读取数据 实现
    前言TDSQL-CMySQL版(TDSQL-CforMySQL)是腾讯云自研的新一代云原生关系型数据库。融合了传统数据库、云计算与新硬件技术的优势,为用户提供具备高弹性、高性能、海量存储、安全可靠的数据库服务。TDSQL-CMySQL版100%兼容MySQL5.7、8.0。实现超百万级QPS的高吞吐,最高PB级智......
  • 真香!基于 Prometheus 的持久化存储,全是知识点
    Prometheus将基于告警规则生成的告警存储为时间序列,不会将Alertmanager的告警信息持久化存储,那么针对历史告警的检索、统计等需求就无法实现。因此需要一种持久化机制用于存储历史告警信息,本文主要探究基于alertmanager告警的开源持久化方案。1.告警触发机制基于主机层面内存......
  • python必坑知识点05
    1、js基本数据类型undefined,null,number,string,symbol,boolean,object,array,date2、js中的拷贝(深浅拷贝)https://www.cnblogs.com/echolun/p/7889848.htmlhttps://www.bilibili.com/video/BV1iu411e7DS/?spm_id_from=333.337.search-card.all.click&vd_source=69181b959bc0......
  • Python基础入门学习笔记 077 GUI的终极选择:Tkinter14
    Tkinter提供了三种标准对话框模块,分别是:messagebox、filedialog、colorchoosermessagebox(消息对话框)实例1:askokcancel函数1fromtkinterimport*23print(messagebox.askokcancel("FishCDemo","发射核弹?"))45mainloop() 实例2:askquestion函数 实例3:asire......
  • Python基础入门学习笔记 074 GUI的终极选择:Tkinter11
    事件绑定对于每个组件来说,可以通过bind()方法将函数或方法绑定到具体的事件上。当被触发的事件满足该组件绑定的事件时,Tkinter就会带着事件描述去调用handler()方法实例1:捕获单击鼠标位置1fromtkinterimport*23root=Tk()45defcallback(event):6prin......
  • Python基础入门学习笔记 075 GUI的终极选择:Tkinter12
    Message组件Message(消息)组件是Label组件的变体,用于显示多行文本信息。Message组件能够自动换行,并调整文本的尺寸使其适应给定得尺寸。实例1:1fromtkinterimport*23root=Tk()4w1=Message(root,text="这是一则消息",width=100)5w1.pack()6w2=Message(root,......
  • Python基础入门学习笔记 071 GUI的终极选择:Tkinter8
    Canvas(画布)组件一个可以让你随心所欲绘制界面的组件。通常用于显示和编辑图形,可以用它来绘制直线、图形、多边形,甚至是绘制其他组件。实例1:1fromtkinterimport*2root=Tk()3#创建canvas对象框,设置其宽度、高度与背景色4w=Canvas(root,width=200,height=100,b......